From b7af9edb8a8802c35f1a460f8dafff8643b34639 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 14 Apr 2018 02:12:19 -0400 Subject: add std.os.createThread this adds kernel thread support to the standard library for linux. See #174 --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 3ba58a09bd..0fac1bd219 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18407,6 +18407,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAddImplicitReturnType: case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: + case IrInstructionIdAtomicRmw: return true; case IrInstructionIdPhi: @@ -18487,7 +18488,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroSize: case IrInstructionIdCoroSuspend: case IrInstructionIdCoroFree: - case IrInstructionIdAtomicRmw: case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: return false; -- cgit v1.2.3 From 4a2bfec150ac8b78185d98324782da7841eddb9b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 12:57:45 -0400 Subject: fix linux implementation of self exe path closes #894 --- src/os.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index e0491b21de..97462bd658 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) { buf_resize(out_path, buf_len(out_path) * 2); continue; } + buf_resize(out_path, amt); return 0; } #endif -- cgit v1.2.3 From b5459eb987d89c4759c31123a7baa0a0d962c024 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 13:21:52 -0400 Subject: add @sqrt built-in function See #767 --- CMakeLists.txt | 1 - doc/langref.html.in | 12 +- src/all_types.hpp | 12 +- src/analyze.cpp | 9 +- src/bigfloat.cpp | 4 + src/bigfloat.hpp | 1 + src/codegen.cpp | 24 +++- src/ir.cpp | 94 +++++++++++++++ src/ir_print.cpp | 15 +++ std/math/sqrt.zig | 295 ++++++----------------------------------------- std/math/x86_64/sqrt.zig | 15 --- std/special/builtin.zig | 209 +++++++++++++++++++++++++++++++++ test/cases/math.zig | 16 +++ 13 files changed, 419 insertions(+), 288 deletions(-) delete mode 100644 std/math/x86_64/sqrt.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 021fd43cf0..bf90a7ef46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -498,7 +498,6 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" - "math/x86_64/sqrt.zig" "mem.zig" "net.zig" "os/child_process.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index 856d62f142..d9436e55b7 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4669,6 +4669,16 @@ pub const FloatMode = enum { The result is a target-specific compile time constant.

{#header_close#} + {#header_open|@sqrt#} +
@sqrt(comptime T: type, value: T) -> T
+

+ Performs the square root of a floating point number. Uses a dedicated hardware instruction + when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO. +

+

+ This is a low-level intrinsic. Most code can use std.math.sqrt instead. +

+ {#header_close#} {#header_open|@subWithOverflow#}
@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool

@@ -5991,7 +6001,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw", + built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index d27a5c7a1c..b43214a60e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1317,6 +1317,7 @@ enum BuiltinFnId { BuiltinFnIdDivFloor, BuiltinFnIdRem, BuiltinFnIdMod, + BuiltinFnIdSqrt, BuiltinFnIdTruncate, BuiltinFnIdIntType, BuiltinFnIdSetCold, @@ -1413,6 +1414,7 @@ enum ZigLLVMFnId { ZigLLVMFnIdOverflowArithmetic, ZigLLVMFnIdFloor, ZigLLVMFnIdCeil, + ZigLLVMFnIdSqrt, }; enum AddSubMul { @@ -1433,7 +1435,7 @@ struct ZigLLVMFnKey { } clz; struct { uint32_t bit_count; - } floor_ceil; + } floating; struct { AddSubMul add_sub_mul; uint32_t bit_count; @@ -2047,6 +2049,7 @@ enum IrInstructionId { IrInstructionIdAddImplicitReturnType, IrInstructionIdMergeErrRetTraces, IrInstructionIdMarkErrRetTracePtr, + IrInstructionIdSqrt, }; struct IrInstruction { @@ -3036,6 +3039,13 @@ struct IrInstructionMarkErrRetTracePtr { IrInstruction *err_ret_trace_ptr; }; +struct IrInstructionSqrt { + IrInstruction base; + + IrInstruction *type; + IrInstruction *op; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index c73e6b39e3..9092da6e3b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5801,9 +5801,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { case ZigLLVMFnIdClz: return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; case ZigLLVMFnIdFloor: - return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168; + return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; case ZigLLVMFnIdCeil: - return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089; + return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089; + case ZigLLVMFnIdSqrt: + return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385; case ZigLLVMFnIdOverflowArithmetic: return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) + @@ -5822,7 +5824,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { return a.data.clz.bit_count == b.data.clz.bit_count; case ZigLLVMFnIdFloor: case ZigLLVMFnIdCeil: - return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count; + case ZigLLVMFnIdSqrt: + return a.data.floating.bit_count == b.data.floating.bit_count; case ZigLLVMFnIdOverflowArithmetic: return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) && diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp index 2cab9658e8..dcb6db61db 100644 --- a/src/bigfloat.cpp +++ b/src/bigfloat.cpp @@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) { f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored); return !f128M_eq(&floored, &bigfloat->value); } + +void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) { + f128M_sqrt(&op->value, &dest->value); +} diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp index 894b252c3a..e212c30c87 100644 --- a/src/bigfloat.hpp +++ b/src/bigfloat.hpp @@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2 void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); +void bigfloat_sqrt(BigFloat *dest, const BigFloat *op); void bigfloat_append_buf(Buf *buf, const BigFloat *op); Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); diff --git a/src/codegen.cpp b/src/codegen.cpp index a58832f983..b45214a5e0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -717,12 +717,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, return fn_val; } -static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) { +static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) { assert(type_entry->id == TypeTableEntryIdFloat); ZigLLVMFnKey key = {}; key.id = fn_id; - key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count; + key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count; auto existing_entry = g->llvm_fn_table.maybe_get(key); if (existing_entry) @@ -733,6 +733,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi name = "floor"; } else if (fn_id == ZigLLVMFnIdCeil) { name = "ceil"; + } else if (fn_id == ZigLLVMFnIdSqrt) { + name = "sqrt"; } else { zig_unreachable(); } @@ -1900,7 +1902,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type if (type_entry->id == TypeTableEntryIdInt) return val; - LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor); + LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor); return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); } @@ -1908,7 +1910,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_ if (type_entry->id == TypeTableEntryIdInt) return val; - LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil); + LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil); return LLVMBuildCall(g->builder, ceil_fn, &val, 1, ""); } @@ -3247,10 +3249,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui fn_name = "cttz"; key.id = ZigLLVMFnIdCtz; key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; - } else { + } else if (fn_id == BuiltinFnIdClz) { fn_name = "ctlz"; key.id = ZigLLVMFnIdClz; key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else { + zig_unreachable(); } auto existing_entry = g->llvm_fn_table.maybe_get(key); @@ -4402,6 +4406,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e return nullptr; } +static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) { + LLVMValueRef op = ir_llvm_value(g, instruction->op); + assert(instruction->base.value.type->id == TypeTableEntryIdFloat); + LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt); + return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -4623,6 +4634,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); case IrInstructionIdMarkErrRetTracePtr: return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); + case IrInstructionIdSqrt: + return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); } zig_unreachable(); } @@ -6109,6 +6122,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2); create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); + create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); diff --git a/src/ir.cpp b/src/ir.cpp index 0fac1bd219..08229b8bb3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -733,6 +733,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP return IrInstructionIdMarkErrRetTracePtr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { + return IrInstructionIdSqrt; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2731,6 +2735,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco return &instruction->base; } +static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { + IrInstructionSqrt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -3845,6 +3860,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); } + case BuiltinFnIdSqrt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + return ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); + } case BuiltinFnIdTruncate: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -18031,6 +18060,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze * return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { + TypeTableEntry *float_type = ir_resolve_type(ira, instruction->type->other); + if (type_is_invalid(float_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *op = instruction->op->other; + if (type_is_invalid(op->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat; + if (!ok_type) { + ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); + if (type_is_invalid(casted_op->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (instr_is_comptime(casted_op)) { + ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); + if (!val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + + if (float_type->id == TypeTableEntryIdNumLitFloat) { + bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); + } else if (float_type->id == TypeTableEntryIdFloat) { + switch (float_type->data.floating.bit_count) { + case 32: + out_val->data.x_f32 = sqrtf(val->data.x_f32); + break; + case 64: + out_val->data.x_f64 = sqrt(val->data.x_f64); + break; + case 128: + f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } + + return float_type; + } + + assert(float_type->id == TypeTableEntryIdFloat); + if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) { + ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_op); + ir_link_new_instruction(result, &instruction->base); + result->value.type = float_type; + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -18278,6 +18369,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); case IrInstructionIdMarkErrRetTracePtr: return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); + case IrInstructionIdSqrt: + return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); } zig_unreachable(); } @@ -18490,6 +18583,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroFree: case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: + case IrInstructionIdSqrt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 99f79ff75e..5f8dd60187 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1204,6 +1204,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe fprintf(irp->f, ")"); } +static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { + fprintf(irp->f, "@sqrt("); + if (instruction->type != nullptr) { + ir_print_other_instruction(irp, instruction->type); + } else { + fprintf(irp->f, "null"); + } + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->op); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -1590,6 +1602,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdMarkErrRetTracePtr: ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); break; + case IrInstructionIdSqrt: + ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 690f8b6901..982bd28b72 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -14,26 +14,8 @@ const TypeId = builtin.TypeId; pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { - return T(sqrt64(x)); - }, - TypeId.Float => { - switch (T) { - f32 => { - switch (builtin.arch) { - builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x), - else => return sqrt32(x), - } - }, - f64 => { - switch (builtin.arch) { - builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x), - else => return sqrt64(x), - } - }, - else => @compileError("sqrt not implemented for " ++ @typeName(T)), - } - }, + TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128 + TypeId.Float => return @sqrt(T, x), TypeId.IntLiteral => comptime { if (x > @maxValue(u128)) { @compileError("sqrt not implemented for comptime_int greater than 128 bits"); @@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ } return T(sqrt_int(u128, x)); }, - TypeId.Int => { - return sqrt_int(T, x); - }, + TypeId.Int => return sqrt_int(T, x), else => @compileError("sqrt not implemented for " ++ @typeName(T)), } } -fn sqrt32(x: f32) f32 { - const tiny: f32 = 1.0e-30; - const sign: i32 = @bitCast(i32, u32(0x80000000)); - var ix: i32 = @bitCast(i32, x); - - if ((ix & 0x7F800000) == 0x7F800000) { - return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan - } - - // zero - if (ix <= 0) { - if (ix & ~sign == 0) { - return x; // sqrt (+-0) = +-0 - } - if (ix < 0) { - return math.snan(f32); - } - } - - // normalize - var m = ix >> 23; - if (m == 0) { - // subnormal - var i: i32 = 0; - while (ix & 0x00800000 == 0) : (i += 1) { - ix <<= 1; - } - m -= i - 1; - } - - m -= 127; // unbias exponent - ix = (ix & 0x007FFFFF) | 0x00800000; - - if (m & 1 != 0) { // odd m, double x to even - ix += ix; - } - - m >>= 1; // m = [m / 2] - - // sqrt(x) bit by bit - ix += ix; - var q: i32 = 0; // q = sqrt(x) - var s: i32 = 0; - var r: i32 = 0x01000000; // r = moving bit right -> left - - while (r != 0) { - const t = s + r; - if (t <= ix) { - s = t + r; - ix -= t; - q += r; - } - ix += ix; - r >>= 1; - } - - // floating add to find rounding direction - if (ix != 0) { - var z = 1.0 - tiny; // inexact - if (z >= 1.0) { - z = 1.0 + tiny; - if (z > 1.0) { - q += 2; - } else { - if (q & 1 != 0) { - q += 1; - } - } - } - } - - ix = (q >> 1) + 0x3f000000; - ix += m << 23; - return @bitCast(f32, ix); -} - -// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound -// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are -// potentially some edge cases remaining that are not handled in the same way. -fn sqrt64(x: f64) f64 { - const tiny: f64 = 1.0e-300; - const sign: u32 = 0x80000000; - const u = @bitCast(u64, x); - - var ix0 = u32(u >> 32); - var ix1 = u32(u & 0xFFFFFFFF); - - // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan - if (ix0 & 0x7FF00000 == 0x7FF00000) { - return x * x + x; - } - - // sqrt(+-0) = +-0 - if (x == 0.0) { - return x; - } - // sqrt(-ve) = snan - if (ix0 & sign != 0) { - return math.snan(f64); - } - - // normalize x - var m = i32(ix0 >> 20); - if (m == 0) { - // subnormal - while (ix0 == 0) { - m -= 21; - ix0 |= ix1 >> 11; - ix1 <<= 21; - } - - // subnormal - var i: u32 = 0; - while (ix0 & 0x00100000 == 0) : (i += 1) { - ix0 <<= 1; - } - m -= i32(i) - 1; - ix0 |= ix1 >> u5(32 - i); - ix1 <<= u5(i); - } - - // unbias exponent - m -= 1023; - ix0 = (ix0 & 0x000FFFFF) | 0x00100000; - if (m & 1 != 0) { - ix0 += ix0 + (ix1 >> 31); - ix1 = ix1 +% ix1; - } - m >>= 1; - - // sqrt(x) bit by bit - ix0 += ix0 + (ix1 >> 31); - ix1 = ix1 +% ix1; - - var q: u32 = 0; - var q1: u32 = 0; - var s0: u32 = 0; - var s1: u32 = 0; - var r: u32 = 0x00200000; - var t: u32 = undefined; - var t1: u32 = undefined; - - while (r != 0) { - t = s0 +% r; - if (t <= ix0) { - s0 = t + r; - ix0 -= t; - q += r; - } - ix0 = ix0 +% ix0 +% (ix1 >> 31); - ix1 = ix1 +% ix1; - r >>= 1; - } - - r = sign; - while (r != 0) { - t = s1 +% r; - t = s0; - if (t < ix0 or (t == ix0 and t1 <= ix1)) { - s1 = t1 +% r; - if (t1 & sign == sign and s1 & sign == 0) { - s0 += 1; - } - ix0 -= t; - if (ix1 < t1) { - ix0 -= 1; - } - ix1 = ix1 -% t1; - q1 += r; - } - ix0 = ix0 +% ix0 +% (ix1 >> 31); - ix1 = ix1 +% ix1; - r >>= 1; - } - - // rounding direction - if (ix0 | ix1 != 0) { - var z = 1.0 - tiny; // raise inexact - if (z >= 1.0) { - z = 1.0 + tiny; - if (q1 == 0xFFFFFFFF) { - q1 = 0; - q += 1; - } else if (z > 1.0) { - if (q1 == 0xFFFFFFFE) { - q += 1; - } - q1 += 2; - } else { - q1 += q1 & 1; - } - } - } - - ix0 = (q >> 1) + 0x3FE00000; - ix1 = q1 >> 1; - if (q & 1 != 0) { - ix1 |= 0x80000000; - } - - // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same - // behaviour at least. - var iix0 = i32(ix0); - iix0 = iix0 +% (m << 20); - - const uz = (u64(iix0) << 32) | ix1; - return @bitCast(f64, uz); -} - test "math.sqrt" { - assert(sqrt(f32(0.0)) == sqrt32(0.0)); - assert(sqrt(f64(0.0)) == sqrt64(0.0)); + assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0)); + assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0)); } test "math.sqrt32" { const epsilon = 0.000001; - assert(sqrt32(0.0) == 0.0); - assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon)); - assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon)); - assert(sqrt32(4.0) == 2.0); - assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon)); - assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon)); - assert(sqrt32(64.0) == 8.0); - assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon)); - assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon)); + assert(@sqrt(f32, 0.0) == 0.0); + assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon)); + assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon)); + assert(@sqrt(f32, 4.0) == 2.0); + assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon)); + assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon)); + assert(@sqrt(f32, 64.0) == 8.0); + assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon)); + assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon)); } test "math.sqrt64" { const epsilon = 0.000001; - assert(sqrt64(0.0) == 0.0); - assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon)); - assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon)); - assert(sqrt64(4.0) == 2.0); - assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon)); - assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon)); - assert(sqrt64(64.0) == 8.0); - assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon)); - assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon)); + assert(@sqrt(f64, 0.0) == 0.0); + assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon)); + assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon)); + assert(@sqrt(f64, 4.0) == 2.0); + assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon)); + assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon)); + assert(@sqrt(f64, 64.0) == 8.0); + assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon)); + assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon)); } test "math.sqrt32.special" { - assert(math.isPositiveInf(sqrt32(math.inf(f32)))); - assert(sqrt32(0.0) == 0.0); - assert(sqrt32(-0.0) == -0.0); - assert(math.isNan(sqrt32(-1.0))); - assert(math.isNan(sqrt32(math.nan(f32)))); + assert(math.isPositiveInf(@sqrt(f32, math.inf(f32)))); + assert(@sqrt(f32, 0.0) == 0.0); + assert(@sqrt(f32, -0.0) == -0.0); + assert(math.isNan(@sqrt(f32, -1.0))); + assert(math.isNan(@sqrt(f32, math.nan(f32)))); } test "math.sqrt64.special" { - assert(math.isPositiveInf(sqrt64(math.inf(f64)))); - assert(sqrt64(0.0) == 0.0); - assert(sqrt64(-0.0) == -0.0); - assert(math.isNan(sqrt64(-1.0))); - assert(math.isNan(sqrt64(math.nan(f64)))); + assert(math.isPositiveInf(@sqrt(f64, math.inf(f64)))); + assert(@sqrt(f64, 0.0) == 0.0); + assert(@sqrt(f64, -0.0) == -0.0); + assert(math.isNan(@sqrt(f64, -1.0))); + assert(math.isNan(@sqrt(f64, math.nan(f64)))); } fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) { diff --git a/std/math/x86_64/sqrt.zig b/std/math/x86_64/sqrt.zig deleted file mode 100644 index ad9ce0c96c..0000000000 --- a/std/math/x86_64/sqrt.zig +++ /dev/null @@ -1,15 +0,0 @@ -pub fn sqrt32(x: f32) f32 { - return asm ( - \\sqrtss %%xmm0, %%xmm0 - : [ret] "={xmm0}" (-> f32) - : [x] "{xmm0}" (x) - ); -} - -pub fn sqrt64(x: f64) f64 { - return asm ( - \\sqrtsd %%xmm0, %%xmm0 - : [ret] "={xmm0}" (-> f64) - : [x] "{xmm0}" (x) - ); -} diff --git a/std/special/builtin.zig b/std/special/builtin.zig index ac6eefe3d9..56aa2ebaf8 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -194,3 +194,212 @@ fn isNan(comptime T: type, bits: T) bool { unreachable; } } + +// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound +// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are +// potentially some edge cases remaining that are not handled in the same way. +export fn sqrt(x: f64) f64 { + const tiny: f64 = 1.0e-300; + const sign: u32 = 0x80000000; + const u = @bitCast(u64, x); + + var ix0 = u32(u >> 32); + var ix1 = u32(u & 0xFFFFFFFF); + + // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan + if (ix0 & 0x7FF00000 == 0x7FF00000) { + return x * x + x; + } + + // sqrt(+-0) = +-0 + if (x == 0.0) { + return x; + } + // sqrt(-ve) = snan + if (ix0 & sign != 0) { + return math.snan(f64); + } + + // normalize x + var m = i32(ix0 >> 20); + if (m == 0) { + // subnormal + while (ix0 == 0) { + m -= 21; + ix0 |= ix1 >> 11; + ix1 <<= 21; + } + + // subnormal + var i: u32 = 0; + while (ix0 & 0x00100000 == 0) : (i += 1) { + ix0 <<= 1; + } + m -= i32(i) - 1; + ix0 |= ix1 >> u5(32 - i); + ix1 <<= u5(i); + } + + // unbias exponent + m -= 1023; + ix0 = (ix0 & 0x000FFFFF) | 0x00100000; + if (m & 1 != 0) { + ix0 += ix0 + (ix1 >> 31); + ix1 = ix1 +% ix1; + } + m >>= 1; + + // sqrt(x) bit by bit + ix0 += ix0 + (ix1 >> 31); + ix1 = ix1 +% ix1; + + var q: u32 = 0; + var q1: u32 = 0; + var s0: u32 = 0; + var s1: u32 = 0; + var r: u32 = 0x00200000; + var t: u32 = undefined; + var t1: u32 = undefined; + + while (r != 0) { + t = s0 +% r; + if (t <= ix0) { + s0 = t + r; + ix0 -= t; + q += r; + } + ix0 = ix0 +% ix0 +% (ix1 >> 31); + ix1 = ix1 +% ix1; + r >>= 1; + } + + r = sign; + while (r != 0) { + t = s1 +% r; + t = s0; + if (t < ix0 or (t == ix0 and t1 <= ix1)) { + s1 = t1 +% r; + if (t1 & sign == sign and s1 & sign == 0) { + s0 += 1; + } + ix0 -= t; + if (ix1 < t1) { + ix0 -= 1; + } + ix1 = ix1 -% t1; + q1 += r; + } + ix0 = ix0 +% ix0 +% (ix1 >> 31); + ix1 = ix1 +% ix1; + r >>= 1; + } + + // rounding direction + if (ix0 | ix1 != 0) { + var z = 1.0 - tiny; // raise inexact + if (z >= 1.0) { + z = 1.0 + tiny; + if (q1 == 0xFFFFFFFF) { + q1 = 0; + q += 1; + } else if (z > 1.0) { + if (q1 == 0xFFFFFFFE) { + q += 1; + } + q1 += 2; + } else { + q1 += q1 & 1; + } + } + } + + ix0 = (q >> 1) + 0x3FE00000; + ix1 = q1 >> 1; + if (q & 1 != 0) { + ix1 |= 0x80000000; + } + + // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same + // behaviour at least. + var iix0 = i32(ix0); + iix0 = iix0 +% (m << 20); + + const uz = (u64(iix0) << 32) | ix1; + return @bitCast(f64, uz); +} + +export fn sqrtf(x: f32) f32 { + const tiny: f32 = 1.0e-30; + const sign: i32 = @bitCast(i32, u32(0x80000000)); + var ix: i32 = @bitCast(i32, x); + + if ((ix & 0x7F800000) == 0x7F800000) { + return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan + } + + // zero + if (ix <= 0) { + if (ix & ~sign == 0) { + return x; // sqrt (+-0) = +-0 + } + if (ix < 0) { + return math.snan(f32); + } + } + + // normalize + var m = ix >> 23; + if (m == 0) { + // subnormal + var i: i32 = 0; + while (ix & 0x00800000 == 0) : (i += 1) { + ix <<= 1; + } + m -= i - 1; + } + + m -= 127; // unbias exponent + ix = (ix & 0x007FFFFF) | 0x00800000; + + if (m & 1 != 0) { // odd m, double x to even + ix += ix; + } + + m >>= 1; // m = [m / 2] + + // sqrt(x) bit by bit + ix += ix; + var q: i32 = 0; // q = sqrt(x) + var s: i32 = 0; + var r: i32 = 0x01000000; // r = moving bit right -> left + + while (r != 0) { + const t = s + r; + if (t <= ix) { + s = t + r; + ix -= t; + q += r; + } + ix += ix; + r >>= 1; + } + + // floating add to find rounding direction + if (ix != 0) { + var z = 1.0 - tiny; // inexact + if (z >= 1.0) { + z = 1.0 + tiny; + if (z > 1.0) { + q += 2; + } else { + if (q & 1 != 0) { + q += 1; + } + } + } + } + + ix = (q >> 1) + 0x3f000000; + ix += m << 23; + return @bitCast(f32, ix); +} diff --git a/test/cases/math.zig b/test/cases/math.zig index 574aa39bb1..47d001a590 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -402,3 +402,19 @@ test "comptime float rem int" { assert(x == 1.0); } } + +test "@sqrt" { + testSqrt(f64, 12.0); + comptime testSqrt(f64, 12.0); + testSqrt(f32, 13.0); + comptime testSqrt(f32, 13.0); + + const x = 14.0; + const y = x * x; + const z = @sqrt(@typeOf(y), y); + comptime assert(z == x); +} + +fn testSqrt(comptime T: type, x: T) void { + assert(@sqrt(T, x * x) == x); +} -- cgit v1.2.3 From a8d794215e5031d60c5bd761a2478f382972bde6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 15:22:07 -0400 Subject: exit with error code instead of panic for file not found --- src/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index b45214a5e0..7a0117421a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6337,7 +6337,8 @@ static void define_builtin_compile_vars(CodeGen *g) { int err; Buf *abs_full_path = buf_alloc(); if ((err = os_path_real(builtin_zig_path, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + exit(1); } assert(g->root_package); -- cgit v1.2.3 From b9360640cefd1aa30dedf71a0c6b7bddc51a6ae3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Apr 2018 18:12:00 -0400 Subject: add @atomicLoad builtin See #174 --- doc/langref.html.in | 21 +++++++++- src/all_types.hpp | 11 +++++ src/codegen.cpp | 13 ++++++ src/ir.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++---- src/ir_print.cpp | 21 ++++++++++ std/os/index.zig | 2 +- test/cases/atomics.zig | 14 ++++++- 7 files changed, 183 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index d9436e55b7..5cbec218a9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3880,6 +3880,25 @@ pub fn main() void { {#header_open|@ArgType#}

TODO

{#header_close#} + {#header_open|@atomicLoad#} +
@atomicLoad(comptime T: type, ptr: &const T, comptime ordering: builtin.AtomicOrder) -> T
+

+ This builtin function atomically dereferences a pointer and returns the value. +

+

+ T must be a pointer type, a bool, + or an integer whose bit count meets these requirements: +

+
    +
  • At least 8
  • +
  • At most the same as usize
  • +
  • Power of 2
  • +
+

+ TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe + we can remove this restriction +

+ {#header_close#} {#header_open|@atomicRmw#}
@atomicRmw(comptime T: type, ptr: &T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -> T

@@ -6001,7 +6020,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index b43214a60e..708ad81791 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1347,6 +1347,7 @@ enum BuiltinFnId { BuiltinFnIdExport, BuiltinFnIdErrorReturnTrace, BuiltinFnIdAtomicRmw, + BuiltinFnIdAtomicLoad, }; struct BuiltinFnEntry { @@ -2043,6 +2044,7 @@ enum IrInstructionId { IrInstructionIdCoroPromise, IrInstructionIdCoroAllocHelper, IrInstructionIdAtomicRmw, + IrInstructionIdAtomicLoad, IrInstructionIdPromiseResultType, IrInstructionIdAwaitBookkeeping, IrInstructionIdSaveErrRetAddr, @@ -3003,6 +3005,15 @@ struct IrInstructionAtomicRmw { AtomicOrder resolved_ordering; }; +struct IrInstructionAtomicLoad { + IrInstruction base; + + IrInstruction *operand_type; + IrInstruction *ptr; + IrInstruction *ordering; + AtomicOrder resolved_ordering; +}; + struct IrInstructionPromiseResultType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 7a0117421a..9f319d9122 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4385,6 +4385,16 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, ""); } +static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable, + IrInstructionAtomicLoad *instruction) +{ + LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering); + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, ""); + LLVMSetOrdering(load_inst, ordering); + return load_inst; +} + static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable, IrInstructionMergeErrRetTraces *instruction) { @@ -4628,6 +4638,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction); case IrInstructionIdAtomicRmw: return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); + case IrInstructionIdAtomicLoad: + return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); case IrInstructionIdSaveErrRetAddr: return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction); case IrInstructionIdMergeErrRetTraces: @@ -6136,6 +6148,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdExport, "export", 3); create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); + create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index 08229b8bb3..d43efe0190 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -709,6 +709,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) { return IrInstructionIdAtomicRmw; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { + return IrInstructionIdAtomicLoad; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) { return IrInstructionIdPromiseResultType; } @@ -2673,6 +2677,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *operand_type, IrInstruction *ptr, + IrInstruction *ordering, AtomicOrder resolved_ordering) +{ + IrInstructionAtomicLoad *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand_type = operand_type; + instruction->ptr = ptr; + instruction->ordering = ordering; + instruction->resolved_ordering = resolved_ordering; + + if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_type) { @@ -4303,6 +4324,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo // these 2 values don't mean anything since we passed non-null values for other args AtomicRmwOp_xchg, AtomicOrderMonotonic); } + case BuiltinFnIdAtomicLoad: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_instruction) + return arg2_value; + + return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value, + // this value does not mean anything since we passed non-null values for other arg + AtomicOrderMonotonic); + } } zig_unreachable(); } @@ -17898,35 +17940,43 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, return result->value.type; } -static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) { - TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other); - if (type_is_invalid(operand_type)) { +static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { + TypeTableEntry *operand_type = ir_resolve_type(ira, op); + if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; - } + if (operand_type->id == TypeTableEntryIdInt) { if (operand_type->data.integral.bit_count < 8) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (!is_power_of_2(operand_type->data.integral.bit_count)) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } } else if (get_codegen_ptr_type(operand_type) == nullptr) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, op, buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name))); return ira->codegen->builtin_types.entry_invalid; } + return operand_type; +} + +static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *ptr_inst = instruction->ptr->other; if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -17974,6 +18024,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *ptr_inst = instruction->ptr->other; + if (type_is_invalid(ptr_inst->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); + IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); + if (type_is_invalid(casted_ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + AtomicOrder ordering; + if (instruction->ordering == nullptr) { + ordering = instruction->resolved_ordering; + } else { + if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) + return ira->codegen->builtin_types.entry_invalid; + } + + if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) { + assert(instruction->ordering != nullptr); + ir_add_error(ira, instruction->ordering, + buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel")); + return ira->codegen->builtin_types.entry_invalid; + } + + if (instr_is_comptime(casted_ptr)) { + IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr); + ir_link_new_instruction(result, &instruction->base); + assert(result->value.type != nullptr); + return result->value.type; + } + + IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering); + ir_link_new_instruction(result, &instruction->base); + result->value.type = operand_type; + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other); if (type_is_invalid(promise_type)) @@ -18357,6 +18450,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction); case IrInstructionIdAtomicRmw: return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); + case IrInstructionIdAtomicLoad: + return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); case IrInstructionIdPromiseResultType: return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction); case IrInstructionIdAwaitBookkeeping: @@ -18584,6 +18679,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCoroPromise: case IrInstructionIdPromiseResultType: case IrInstructionIdSqrt: + case IrInstructionIdAtomicLoad: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5f8dd60187..45b666ae73 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1172,6 +1172,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio fprintf(irp->f, ")"); } +static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) { + fprintf(irp->f, "@atomicLoad("); + if (instruction->operand_type != nullptr) { + ir_print_other_instruction(irp, instruction->operand_type); + } else { + fprintf(irp->f, "[TODO print]"); + } + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ","); + if (instruction->ordering != nullptr) { + ir_print_other_instruction(irp, instruction->ordering); + } else { + fprintf(irp->f, "[TODO print]"); + } + fprintf(irp->f, ")"); +} + static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) { fprintf(irp->f, "@awaitBookkeeping("); ir_print_other_instruction(irp, instruction->promise_result_type); @@ -1605,6 +1623,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdSqrt: ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); break; + case IrInstructionIdAtomicLoad: + ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/os/index.zig b/std/os/index.zig index 15b54f2e98..dbdb8c90cd 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2392,7 +2392,7 @@ pub const Thread = struct { pub fn wait(self: &const Thread) void { while (true) { - const pid_value = self.pid; // TODO atomic load + const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); if (pid_value == 0) break; const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); switch (linux.getErrno(rc)) { diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index e8e81b76e6..323906e4a4 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -15,13 +15,25 @@ test "fence" { x = 5678; } -test "atomicrmw" { +test "atomicrmw and atomicload" { var data: u8 = 200; testAtomicRmw(&data); assert(data == 42); + testAtomicLoad(&data); } fn testAtomicRmw(ptr: &u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); assert(prev_value == 200); + comptime { + var x: i32 = 1234; + const y: i32 = 12345; + assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); + assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); + } +} + +fn testAtomicLoad(ptr: &u8) void { + const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); + assert(x == 42); } -- cgit v1.2.3 From 253ecd5c11747f49575b8425a506c2fecfe26ee2 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 03:26:10 +0300 Subject: Added ReleaseSmall mode --- src/all_types.hpp | 1 + src/codegen.cpp | 18 ++++++++++++------ src/main.cpp | 3 +++ src/zig_llvm.cpp | 4 ++-- src/zig_llvm.h | 2 +- std/build.zig | 15 ++++++++++----- std/special/builtin.zig | 4 +++- 7 files changed, 32 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 708ad81791..7ef7c10393 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1457,6 +1457,7 @@ enum BuildMode { BuildModeDebug, BuildModeFastRelease, BuildModeSafeRelease, + BuildModeSmallRelease, }; enum EmitFileType { diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f319d9122..0279771be7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -512,7 +512,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { } if (fn_table_entry->body_node != nullptr) { - bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off; + bool want_fn_safety = g->build_mode != BuildModeFastRelease && + g->build_mode != BuildModeSmallRelease && + !fn_table_entry->def_scope->safety_off; if (want_fn_safety) { if (g->libc_link_lib != nullptr) { addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong"); @@ -817,7 +819,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) { } static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) { - if (g->build_mode == BuildModeFastRelease) + if (g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease) return false; // TODO memoize @@ -5747,10 +5749,12 @@ static void do_code_gen(CodeGen *g) { os_path_join(g->cache_dir, o_basename, output_path); ensure_cache_dir(g); + bool is_small = g->build_mode == BuildModeSmallRelease; + switch (g->emit_file_type) { case EmitFileTypeBinary: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug)) + ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small)) { zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg); } @@ -5760,7 +5764,7 @@ static void do_code_gen(CodeGen *g) { case EmitFileTypeAssembly: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug)) + ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small)) { zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg); } @@ -5769,7 +5773,7 @@ static void do_code_gen(CodeGen *g) { case EmitFileTypeLLVMIr: if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path), - ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug)) + ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small)) { zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg); } @@ -6160,6 +6164,7 @@ static const char *build_mode_to_str(BuildMode build_mode) { case BuildModeDebug: return "Mode.Debug"; case BuildModeSafeRelease: return "Mode.ReleaseSafe"; case BuildModeFastRelease: return "Mode.ReleaseFast"; + case BuildModeSmallRelease: return "Mode.ReleaseSmall"; } zig_unreachable(); } @@ -6300,6 +6305,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Debug,\n" " ReleaseSafe,\n" " ReleaseFast,\n" + " ReleaseSmall,\n" "};\n\n"); } { @@ -6471,7 +6477,7 @@ static void init(CodeGen *g) { } } - g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease; + g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease; define_builtin_fns(g); define_builtin_compile_vars(g); diff --git a/src/main.cpp b/src/main.cpp index 35c7462f4b..3398fd1dea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,7 @@ static int usage(const char *arg0) { " --pkg-end pop current pkg\n" " --release-fast build with optimizations on and safety off\n" " --release-safe build with optimizations on and safety on\n" + " --release-small build with size optimizations on and safety off\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" " --target-arch [name] specify target architecture\n" @@ -482,6 +483,8 @@ int main(int argc, char **argv) { build_mode = BuildModeFastRelease; } else if (strcmp(arg, "--release-safe") == 0) { build_mode = BuildModeSafeRelease; + } else if (strcmp(arg, "--release-small") == 0) { + build_mode = BuildModeSmallRelease; } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index b4eef13cc1..ef0e9f24a8 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -81,7 +81,7 @@ static const bool assertions_on = false; #endif bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug) + const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small) { std::error_code EC; raw_fd_ostream dest(filename, EC, sys::fs::F_None); @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM return true; } PMBuilder->OptLevel = target_machine->getOptLevel(); - PMBuilder->SizeLevel = 0; + PMBuilder->SizeLevel = is_small ? 1 : 0; PMBuilder->DisableTailCalls = is_debug; PMBuilder->DisableUnitAtATime = is_debug; diff --git a/src/zig_llvm.h b/src/zig_llvm.h index d6809000ce..0d267b1014 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType { }; ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug); + const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small); ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); diff --git a/std/build.zig b/std/build.zig index a4d745e450..a312b28a6f 100644 --- a/std/build.zig +++ b/std/build.zig @@ -426,15 +426,18 @@ pub const Builder = struct { const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false; + const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false; - const mode = if (release_safe and !release_fast) + const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe - else if (release_fast and !release_safe) + else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast - else if (!release_fast and !release_safe) + else if (release_small and !release_fast and !release_safe) + builtin.Mode.ReleaseSmall + else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: { - warn("Both -Drelease-safe and -Drelease-fast specified"); + warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); self.markInvalidUserInput(); break :x builtin.Mode.Debug; }; @@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct { builtin.Mode.Debug => {}, builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, + builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, } zig_args.append("--cache-dir") catch unreachable; @@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct { args.append("ssp-buffer-size=4") catch unreachable; } }, - builtin.Mode.ReleaseFast => { + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => { args.append("-O2") catch unreachable; args.append("-fno-stack-protector") catch unreachable; }, @@ -1706,6 +1710,7 @@ pub const TestStep = struct { builtin.Mode.Debug => {}, builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"), builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"), + builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"), } switch (self.target) { diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 56aa2ebaf8..a5126bc4f3 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -54,7 +54,9 @@ export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 { } comptime { - if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) { + if (builtin.mode != builtin.Mode.ReleaseFast and + builtin.mode != builtin.Mode.ReleaseSmall and + builtin.os != builtin.Os.windows) { @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong); } if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { -- cgit v1.2.3 From 1c85050dad6a7e1d486606205ca2eb2cd7028ef5 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 03:54:40 +0300 Subject: Set SizeLevel to 2 in ReleaseSmall mode --- src/zig_llvm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index ef0e9f24a8..a56378ab3e 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM return true; } PMBuilder->OptLevel = target_machine->getOptLevel(); - PMBuilder->SizeLevel = is_small ? 1 : 0; + PMBuilder->SizeLevel = is_small ? 2 : 0; PMBuilder->DisableTailCalls = is_debug; PMBuilder->DisableUnitAtATime = is_debug; -- cgit v1.2.3 From 96ebd8b23b39e2d4019a8019a6774d7c3d20149d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Apr 2018 22:33:34 -0400 Subject: fix windows not respecting --msvc-lib-dir, --kernel32-lib-dir I believe this was a regression caused by 51a6ff18d454f4cb0faa0f1837df9f0c55a80b43 closes #927 --- src/analyze.cpp | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 9092da6e3b..ca18208ba9 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4421,24 +4421,30 @@ void find_libc_lib_path(CodeGen *g) { if (g->zig_target.os == OsWindows) { ZigWindowsSDK *sdk = get_windows_sdk(g); - Buf* vc_lib_dir = buf_alloc(); - if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { - zig_panic("Unable to determine vcruntime path."); + if (g->msvc_lib_dir == nullptr) { + Buf* vc_lib_dir = buf_alloc(); + if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { + zig_panic("Unable to determine vcruntime path."); + } + g->msvc_lib_dir = vc_lib_dir; } - Buf* ucrt_lib_path = buf_alloc(); - if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine ucrt path."); + if (g->libc_lib_dir == nullptr) { + Buf* ucrt_lib_path = buf_alloc(); + if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { + zig_panic("Unable to determine ucrt path."); + } + g->libc_lib_dir = ucrt_lib_path; } - Buf* kern_lib_path = buf_alloc(); - if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine kernel32 path."); + if (g->kernel32_lib_dir == nullptr) { + Buf* kern_lib_path = buf_alloc(); + if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { + zig_panic("Unable to determine kernel32 path."); + } + g->kernel32_lib_dir = kern_lib_path; } - g->msvc_lib_dir = vc_lib_dir; - g->libc_lib_dir = ucrt_lib_path; - g->kernel32_lib_dir = kern_lib_path; } else if (g->zig_target.os == OsLinux) { g->libc_lib_dir = get_linux_libc_lib_path("crt1.o"); } else { -- cgit v1.2.3 From f1f998e07124f141312289ff82e0ad8d99af1cf7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 12:16:42 -0400 Subject: improve cmpxchg * remove @cmpxchg, add @cmpxchgWeak and @cmpxchgStrong - See explanations in the langref. * add operand type as first parameter * return type is ?T where T is the operand type closes #461 --- doc/langref.html.in | 54 ++++++++++++++++++++++++++++--- src/all_types.hpp | 9 +++++- src/codegen.cpp | 21 ++++++++++-- src/ir.cpp | 85 ++++++++++++++++++++++++++----------------------- src/zig_llvm.cpp | 8 +++-- src/zig_llvm.h | 2 +- test/cases/atomics.zig | 16 ++++++++-- test/compile_errors.zig | 16 +++++----- 8 files changed, 148 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 5cbec218a9..a5d31aada4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4065,16 +4065,60 @@ comptime {

{#header_close#} - {#header_open|@cmpxchg#} -
@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> bool
+ {#header_open|@cmpxchgStrong#} +
@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T

- This function performs an atomic compare exchange operation. + This function performs a strong atomic compare exchange operation. It's the equivalent of this code, + except atomic: +

+ {#code_begin|syntax#} +fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { + const old_value = *ptr; + if (old_value == expected_value) { + *ptr = new_value; + return null; + } else { + return old_value; + } +} + {#code_end#} +

+ If you are using cmpxchg in a loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented + more efficiently in machine instructions.

AtomicOrder can be found with @import("builtin").AtomicOrder.

@typeOf(ptr).alignment must be >= @sizeOf(T).

- {#see_also|Compile Variables#} + {#see_also|Compile Variables|cmpxchgWeak#} + {#header_close#} + {#header_open|@cmpxchgWeak#} +
@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
+

+ This function performs a weak atomic compare exchange operation. It's the equivalent of this code, + except atomic: +

+ {#code_begin|syntax#} +fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { + const old_value = *ptr; + if (old_value == expected_value and usuallyTrueButSometimesFalse()) { + *ptr = new_value; + return null; + } else { + return old_value; + } +} + {#code_end#} +

+ If you are using cmpxchg in a loop, the sporadic failure will be no problem, and cmpxchgWeak + is the better choice, because it can be implemented more efficiently in machine instructions. + However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}. +

+

+ AtomicOrder can be found with @import("builtin").AtomicOrder. +

+

@typeOf(ptr).alignment must be >= @sizeOf(T).

+ {#see_also|Compile Variables|cmpxchgStrong#} {#header_close#} {#header_open|@compileError#}
@compileError(comptime msg: []u8)
@@ -6020,7 +6064,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 7ef7c10393..5a3590dd4d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1310,7 +1310,8 @@ enum BuiltinFnId { BuiltinFnIdReturnAddress, BuiltinFnIdFrameAddress, BuiltinFnIdEmbedFile, - BuiltinFnIdCmpExchange, + BuiltinFnIdCmpxchgWeak, + BuiltinFnIdCmpxchgStrong, BuiltinFnIdFence, BuiltinFnIdDivExact, BuiltinFnIdDivTrunc, @@ -2528,6 +2529,7 @@ struct IrInstructionEmbedFile { struct IrInstructionCmpxchg { IrInstruction base; + IrInstruction *type_value; IrInstruction *ptr; IrInstruction *cmp_value; IrInstruction *new_value; @@ -2535,8 +2537,13 @@ struct IrInstructionCmpxchg { IrInstruction *failure_order_value; // if this instruction gets to runtime then we know these values: + TypeTableEntry *type; AtomicOrder success_order; AtomicOrder failure_order; + + bool is_weak; + + LLVMValueRef tmp_ptr; }; struct IrInstructionFence { diff --git a/src/codegen.cpp b/src/codegen.cpp index 0279771be7..a7d373e9d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3558,9 +3558,20 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, - success_order, failure_order); + success_order, failure_order, instruction->is_weak); - return LLVMBuildExtractValue(g->builder, result_val, 1, ""); + assert(instruction->tmp_ptr != nullptr); + assert(type_has_bits(instruction->type)); + + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); + return instruction->tmp_ptr; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -5588,6 +5599,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdCmpxchg) { + IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; + slot = &cmpxchg_instruction->tmp_ptr; } else { zig_unreachable(); } @@ -6115,7 +6129,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); - create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); + create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); + create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); diff --git a/src/ir.cpp b/src/ir.cpp index d43efe0190..89193a4c27 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -110,6 +110,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); +static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -1832,38 +1833,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, - IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - AtomicOrder success_order, AtomicOrder failure_order) +static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + IrInstruction *success_order_value, IrInstruction *failure_order_value, + bool is_weak, + TypeTableEntry *type, AtomicOrder success_order, AtomicOrder failure_order) { IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; + instruction->is_weak = is_weak; + instruction->type = type; instruction->success_order = success_order; instruction->failure_order = failure_order; + if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - ir_ref_instruction(success_order_value, irb->current_basic_block); - ir_ref_instruction(failure_order_value, irb->current_basic_block); + if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); + if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); return &instruction->base; } -static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, - IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - AtomicOrder success_order, AtomicOrder failure_order) -{ - IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node, - ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) { IrInstructionFence *instruction = ir_build_instruction(irb, scope, source_node); instruction->order_value = order_value; @@ -3771,7 +3768,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_embed_file(irb, scope, node, arg0_value); } - case BuiltinFnIdCmpExchange: + case BuiltinFnIdCmpxchgWeak: + case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); @@ -3798,9 +3796,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; + AstNode *arg5_node = node->data.fn_call_expr.params.at(5); + IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); + if (arg5_value == irb->codegen->invalid_instruction) + return arg5_value; + return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, - AtomicOrderUnordered, AtomicOrderUnordered); + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), + nullptr, AtomicOrderUnordered, AtomicOrderUnordered); } case BuiltinFnIdFence: { @@ -15730,10 +15733,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr } static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *ptr = instruction->ptr->other; if (type_is_invalid(ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + // TODO let this be volatile + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type); + if (type_is_invalid(casted_ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *cmp_value = instruction->cmp_value->other; if (type_is_invalid(cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -15758,28 +15771,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) return ira->codegen->builtin_types.entry_invalid; - if (ptr->value.type->id != TypeTableEntryIdPointer) { - ir_add_error(ira, instruction->ptr, - buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type; - - uint32_t align_bytes = ptr->value.type->data.pointer.alignment; - uint64_t size_bytes = type_size(ira->codegen, child_type); - if (align_bytes < size_bytes) { - ir_add_error(ira, instruction->ptr, - buf_sprintf("expected pointer alignment of at least %" ZIG_PRI_u64 ", found %" PRIu32, - size_bytes, align_bytes)); - return ira->codegen->builtin_types.entry_invalid; - } - - IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type); + IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type); if (type_is_invalid(casted_cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type); + IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type); if (type_is_invalid(casted_new_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -15804,9 +15800,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct return ira->codegen->builtin_types.entry_invalid; } - ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value, - success_order_value, failure_order_value, success_order, failure_order); - return ira->codegen->builtin_types.entry_bool; + if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { + zig_panic("TODO compile-time execution of cmpxchg"); + } + + IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, + nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, + operand_type, success_order, failure_order); + result->value.type = get_maybe_type(ira->codegen, operand_type); + ir_link_new_instruction(result, &instruction->base); + ir_add_alloca(ira, result, result->value.type); + return result->value.type; } static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { @@ -17981,6 +17985,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; + // TODO let this be volatile TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); if (type_is_invalid(casted_ptr->value.type)) diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index a56378ab3e..5905fa8167 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) { LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering) + LLVMAtomicOrdering failure_ordering, bool is_weak) { - return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val), - mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering))); + AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), + unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)); + inst->setWeak(is_weak); + return wrap(inst); } LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 0d267b1014..d34300b8ae 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering); + LLVMAtomicOrdering failure_ordering, bool is_weak); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char *name); diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index 323906e4a4..4cadabb728 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -1,12 +1,24 @@ -const assert = @import("std").debug.assert; +const std = @import("std"); +const assert = std.debug.assert; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; test "cmpxchg" { var x: i32 = 1234; - while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == 1234); + } assert(x == 5678); + + assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(x == 42); } test "fence" { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b22816a9a8..926e997c6e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1394,17 +1394,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} \\} - , ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success"); + , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success"); cases.add("atomic orderings of cmpxchg - success Monotonic or stricter", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} \\} - , ".tmp_source.zig:4:49: error: success atomic ordering must be Monotonic or stricter"); + , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter"); cases.add("negation overflow in function evaluation", \\const y = neg(-128); @@ -2460,11 +2460,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn entry() bool { \\ var x: i32 align(1) = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} \\ return x == 5678; \\} , - ".tmp_source.zig:4:23: error: expected pointer alignment of at least 4, found 1"); + ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'"); cases.add("wrong size to an array literal", \\comptime { @@ -2534,10 +2534,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add("wrong types given to atomic order args in cmpxchg", \\export fn entry() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, u32(1234), u32(1234))) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'"); + ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'"); cases.add("wrong types given to @export", \\extern fn entry() void { } -- cgit v1.2.3 From ca4341f7ba845e7af3c6f2be52cd60c51ec6d68f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 17:14:09 -0400 Subject: add --no-rosegment cli option this provides a workaround for #896 until valgrind adds support for clang/LLD (equivalent to gcc/gold -rosegment) --- src/all_types.hpp | 2 ++ src/link.cpp | 3 +++ src/main.cpp | 5 +++++ 3 files changed, 10 insertions(+) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 5a3590dd4d..88e0ba27a8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1705,6 +1705,8 @@ struct CodeGen { ZigList error_di_types; ZigList forbidden_libs; + + bool no_rosegment_workaround; }; enum VarLinkage { diff --git a/src/link.cpp b/src/link.cpp index 3c6e27e331..d454d77aae 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(g->linker_script); } + if (g->no_rosegment_workaround) { + lj->args.append("--no-rosegment"); + } lj->args.append("--gc-sections"); lj->args.append("-m"); diff --git a/src/main.cpp b/src/main.cpp index 3398fd1dea..9c36f9b091 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -74,6 +74,7 @@ static int usage(const char *arg0) { " -L[dir] alias for --library-path\n" " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" + " --no-rosegment compromise security to workaround valgrind bug\n" " -mconsole (windows) --subsystem console to the linker\n" " -mwindows (windows) --subsystem windows to the linker\n" " -framework [name] (darwin) link against framework\n" @@ -324,6 +325,7 @@ int main(int argc, char **argv) { ZigList test_exec_args = {0}; int comptime_args_end = 0; int runtime_args_start = argc; + bool no_rosegment_workaround = false; if (argc >= 2 && strcmp(argv[1], "build") == 0) { const char *zig_exe_path = arg0; @@ -507,6 +509,8 @@ int main(int argc, char **argv) { mconsole = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; + } else if (strcmp(arg, "--no-rosegment") == 0) { + no_rosegment_workaround = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { each_lib_rpath = true; } else if (strcmp(arg, "--enable-timing-info") == 0) { @@ -844,6 +848,7 @@ int main(int argc, char **argv) { codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_rdynamic(g, rdynamic); + g->no_rosegment_workaround = no_rosegment_workaround; if (mmacosx_version_min && mios_version_min) { fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); return EXIT_FAILURE; -- cgit v1.2.3 From 06909ceaab8ecb33d1f41049870797a3ae721610 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 22:21:54 -0400 Subject: support break in suspend blocks * you can label suspend blocks * labeled break supports suspend blocks See #803 --- doc/langref.html.in | 2 +- src/all_types.hpp | 13 ++++++++++ src/analyze.cpp | 9 +++++++ src/analyze.hpp | 1 + src/codegen.cpp | 1 + src/ir.cpp | 60 +++++++++++++++++++++++++++++++++++++++-------- src/parser.cpp | 25 ++++++++++++++++++-- test/cases/coroutines.zig | 18 ++++++++++++++ test/compile_errors.zig | 20 ++++++++++++++++ 9 files changed, 136 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index a5d31aada4..034b8c1629 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5918,7 +5918,7 @@ Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) diff --git a/src/all_types.hpp b/src/all_types.hpp index 88e0ba27a8..42ce01355c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -867,6 +867,7 @@ struct AstNodeAwaitExpr { }; struct AstNodeSuspend { + Buf *name; AstNode *block; AstNode *promise_symbol; }; @@ -1757,6 +1758,7 @@ enum ScopeId { ScopeIdVarDecl, ScopeIdCImport, ScopeIdLoop, + ScopeIdSuspend, ScopeIdFnDef, ScopeIdCompTime, ScopeIdCoroPrelude, @@ -1852,6 +1854,17 @@ struct ScopeLoop { ZigList *incoming_blocks; }; +// This scope is created for a suspend block in order to have labeled +// suspend for breaking out of a suspend and for detecting if a suspend +// block is inside a suspend block. +struct ScopeSuspend { + Scope base; + + Buf *name; + IrBasicBlock *resume_block; + bool reported_err; +}; + // This scope is created for a comptime expression. // NodeTypeCompTime, NodeTypeSwitchExpr struct ScopeCompTime { diff --git a/src/analyze.cpp b/src/analyze.cpp index ca18208ba9..d142b86326 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { return scope; } +ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { + assert(node->type == NodeTypeSuspend); + ScopeSuspend *scope = allocate(1); + init_scope(&scope->base, ScopeIdSuspend, node, parent); + scope->name = node->data.suspend.name; + return scope; +} + ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { ScopeFnDef *scope = allocate(1); init_scope(&scope->base, ScopeIdFnDef, node, parent); @@ -3616,6 +3624,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) { case ScopeIdVarDecl: case ScopeIdCImport: case ScopeIdLoop: + case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: scope = scope->parent; diff --git a/src/analyze.hpp b/src/analyze.hpp index aa4557666b..aca78f4e25 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent); Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var); ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent); ScopeLoop *create_loop_scope(AstNode *node, Scope *parent); +ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent); ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry); ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import); Scope *create_comptime_scope(AstNode *node, Scope *parent); diff --git a/src/codegen.cpp b/src/codegen.cpp index a7d373e9d0..5b51d9e755 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -654,6 +654,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { } case ScopeIdDeferExpr: case ScopeIdLoop: + case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: return get_di_scope(g, scope->parent); diff --git a/src/ir.cpp b/src/ir.cpp index 89193a4c27..dcfe3afb48 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2829,6 +2829,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock * ir_set_cursor_at_end(irb, basic_block); } +static ScopeSuspend *get_scope_suspend(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdSuspend) + return (ScopeSuspend *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; + } + return nullptr; +} + static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { while (scope) { if (scope->id == ScopeIdDeferExpr) @@ -5665,6 +5677,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop return ir_build_br(irb, break_scope, node, dest_block, is_comptime); } +static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) { + IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false); + + IrBasicBlock *dest_block = suspend_scope->resume_block; + ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); + + return ir_build_br(irb, break_scope, node, dest_block, is_comptime); +} + static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { assert(node->type == NodeTypeBreak); @@ -5704,6 +5725,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * assert(this_block_scope->end_block != nullptr); return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); } + } else if (search_scope->id == ScopeIdSuspend) { + ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope; + if (node->data.break_expr.name != nullptr && + (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name))) + { + return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope); + } } search_scope = search_scope->parent; } @@ -6290,14 +6318,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { - add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); + add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here")); scope_defer_expr->reported_err = true; } return irb->codegen->invalid_instruction; } + ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); + if (existing_suspend_scope) { + if (!existing_suspend_scope->reported_err) { + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block")); + add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here")); + existing_suspend_scope->reported_err = true; + } + return irb->codegen->invalid_instruction; + } Scope *outer_scope = irb->exec->begin_scope; + IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); + IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrInstruction *suspend_code; IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); @@ -6316,28 +6356,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod } else { child_scope = parent_scope; } + ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope); + suspend_scope->resume_block = resume_block; + child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); ir_gen_node(irb, node->data.suspend.block, child_scope); - suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false); + suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false)); } - IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); - IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); - IrInstructionSwitchBrCase *cases = allocate(2); - cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0); + cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0)); cases[0].block = resume_block; - cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1); + cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = cleanup_block; - ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false); + ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, + 2, cases, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); - return ir_build_const_void(irb, parent_scope, node); + return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); } static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope, diff --git a/src/parser.cpp b/src/parser.cpp index 2bd94033cc..4b70e904b8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = "suspend" "|" Symbol "|" body +SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; - Token *suspend_token = &pc->tokens->at(*token_index); + Token *name_token = nullptr; + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdSymbol) { + *token_index += 1; + Token *colon_token = &pc->tokens->at(*token_index); + if (colon_token->id == TokenIdColon) { + *token_index += 1; + name_token = token; + token = &pc->tokens->at(*token_index); + } else if (mandatory) { + ast_expect_token(pc, colon_token, TokenIdColon); + zig_unreachable(); + } else { + *token_index = orig_token_index; + return nullptr; + } + } + + Token *suspend_token = token; if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; } else if (mandatory) { @@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b } AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); + if (name_token != nullptr) { + node->data.suspend.name = token_buf(name_token); + } node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 6d28b98c9d..46055d7469 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -224,3 +224,21 @@ async fn printTrace(p: promise->error!void) void { } }; } + +test "break from suspend" { + var buf: [500]u8 = undefined; + var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + var my_result: i32 = 1; + const p = try async testBreakFromSuspend(&my_result); + cancel p; + std.debug.assert(my_result == 2); +} + +async fn testBreakFromSuspend(my_result: &i32) void { + s: suspend |p| { + break :s; + } + *my_result += 1; + suspend; + *my_result += 1; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 926e997c6e..6ac73d18a2 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,26 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("suspend inside suspend block", + \\const std = @import("std"); + \\ + \\export fn entry() void { + \\ var buf: [500]u8 = undefined; + \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + \\ const p = (async foo()) catch unreachable; + \\ cancel p; + \\} + \\ + \\async fn foo() void { + \\ suspend |p| { + \\ suspend |p1| { + \\ } + \\ } + \\} + , + ".tmp_source.zig:12:9: error: cannot suspend inside suspend block", + ".tmp_source.zig:11:5: note: other suspend block here"); + cases.add("assign inline fn to non-comptime var", \\export fn entry() void { \\ var a = b; -- cgit v1.2.3 From 6b4f6ebd89c4788fde09dd5cde17f3cb54c6c656 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 19 Apr 2018 20:11:16 +0200 Subject: Added field builtin function --- src/all_types.hpp | 3 ++- src/codegen.cpp | 2 +- src/ir.cpp | 38 ++++++++++++++++++++++++++++++++++---- src/ir_print.cpp | 3 ++- test/cases/reflection.zig | 24 ++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 42ce01355c..33fd41ba03 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1292,6 +1292,7 @@ enum BuiltinFnId { BuiltinFnIdMemberCount, BuiltinFnIdMemberType, BuiltinFnIdMemberName, + BuiltinFnIdField, BuiltinFnIdTypeof, BuiltinFnIdAddWithOverflow, BuiltinFnIdSubWithOverflow, @@ -2225,7 +2226,7 @@ struct IrInstructionFieldPtr { IrInstruction base; IrInstruction *container_ptr; - Buf *field_name; + IrInstruction *field_name_expr; bool is_const; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 5b51d9e755..b5c8fdecac 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6114,6 +6114,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1); create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); + create_builtin_fn(g, BuiltinFnIdField, "field", 2); create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); @@ -7185,4 +7186,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, } return pkg; } - diff --git a/src/ir.cpp b/src/ir.cpp index dcfe3afb48..1fb9f86a61 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1033,18 +1033,26 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_ return new_instruction; } -static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *container_ptr, Buf *field_name) +static IrInstruction *ir_build_field_ptr_inner(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *container_ptr, IrInstruction *field_name_expr) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; - instruction->field_name = field_name; + instruction->field_name_expr = field_name_expr; ir_ref_instruction(container_ptr, irb->current_basic_block); + ir_ref_instruction(field_name_expr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *container_ptr, Buf *field_name) +{ + IrInstruction *field_name_expr = ir_build_const_str_lit(irb, scope, source_node, field_name); + return ir_build_field_ptr_inner(irb, scope, source_node, container_ptr, field_name_expr); +} + static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *struct_ptr, TypeStructField *field) { @@ -4015,6 +4023,24 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_member_name(irb, scope, node, arg0_value, arg1_value); } + case BuiltinFnIdField: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *ptr_instruction = ir_build_field_ptr_inner(irb, scope, node, arg0_value, arg1_value); + //if (lval.is_ptr) + // return ptr_instruction; + + return ir_build_load_ptr(irb, scope, node, ptr_instruction); + } case BuiltinFnIdBreakpoint: return ir_build_breakpoint(irb, scope, node); case BuiltinFnIdReturnAddress: @@ -13458,7 +13484,11 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru zig_unreachable(); } - Buf *field_name = field_ptr_instruction->field_name; + IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; + Buf *field_name = ir_resolve_str(ira, field_name_expr); + if (!field_name) + return ira->codegen->builtin_types.entry_invalid; + AstNode *source_node = field_ptr_instruction->base.source_node; if (type_is_invalid(container_type)) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 45b666ae73..bb22c258e2 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -360,7 +360,8 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) { fprintf(irp->f, "fieldptr "); ir_print_other_instruction(irp, instruction->container_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field_name)); + fprintf(irp->f, "."); + ir_print_other_instruction(irp, instruction->field_name_expr); } static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) { diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 18a766d9fc..df723f9b0b 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -1,5 +1,6 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; +const reflection = this; test "reflection: array, pointer, nullable, error union type child" { comptime { @@ -56,7 +57,30 @@ test "reflection: enum member types and names" { } +test "reflection: @field" { + const f = Foo { + .one = 42, + .two = true, + .three = void{}, + }; + + assert(f.one == f.one); + assert(@field(f, "o" ++ "ne") == f.one); + assert(@field(f, "t" ++ "wo") == f.two); + assert(@field(f, "th" ++ "ree") == f.three); + assert(@field(Foo, "const" ++ "ant") == Foo.constant); + assert(@field(Bar, "O" ++ "ne") == Bar.One); + assert(@field(Bar, "O" ++ "ne") == Bar.One); + assert(@field(Bar, "O" ++ "ne") == Bar.One); + assert(@field(Bar, "T" ++ "wo") == Bar.Two); + assert(@field(Bar, "Th" ++ "ree") == Bar.Three); + assert(@field(Bar, "F" ++ "our") == Bar.Four); + assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); +} + const Foo = struct { + const constant = 52; + one: i32, two: bool, three: void, -- cgit v1.2.3 From 1b91478bffa021721cff2c19a6c8389c7dcb3e1d Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 19 Apr 2018 21:34:18 +0200 Subject: Optimized field ptr ir for hot path and fix assignment bug --- src/all_types.hpp | 1 + src/ir.cpp | 245 ++++++++++++++++++++++++++++++---------------- src/ir_print.cpp | 16 ++- test/cases/reflection.zig | 6 +- 4 files changed, 176 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 33fd41ba03..d1b2ad61d2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2226,6 +2226,7 @@ struct IrInstructionFieldPtr { IrInstruction base; IrInstruction *container_ptr; + Buf *field_name_buffer; IrInstruction *field_name_expr; bool is_const; }; diff --git a/src/ir.cpp b/src/ir.cpp index 1fb9f86a61..a34c990afe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -111,6 +111,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); +static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -1033,11 +1034,12 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_ return new_instruction; } -static IrInstruction *ir_build_field_ptr_inner(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, IrInstruction *field_name_expr) { IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->container_ptr = container_ptr; + instruction->field_name_buffer = nullptr; instruction->field_name_expr = field_name_expr; ir_ref_instruction(container_ptr, irb->current_basic_block); @@ -1049,8 +1051,14 @@ static IrInstruction *ir_build_field_ptr_inner(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, Buf *field_name) { - IrInstruction *field_name_expr = ir_build_const_str_lit(irb, scope, source_node, field_name); - return ir_build_field_ptr_inner(irb, scope, source_node, container_ptr, field_name_expr); + IrInstructionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_ptr = container_ptr; + instruction->field_name_buffer = field_name; + instruction->field_name_expr = nullptr; + + ir_ref_instruction(container_ptr, irb->current_basic_block); + + return &instruction->base; } static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -3532,7 +3540,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode return ir_build_load_ptr(irb, scope, node, ptr_instruction); } -static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { +static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeFieldAccessExpr); AstNode *container_ref_node = node->data.field_access_expr.struct_expr; @@ -3542,11 +3550,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; - IrInstruction *ptr_instruction = ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); - if (lval.is_ptr) - return ptr_instruction; - - return ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); } static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) { @@ -3577,7 +3581,7 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); } -static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; @@ -3609,7 +3613,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *arg = ir_gen_node(irb, arg_node, scope); if (arg == irb->codegen->invalid_instruction) return arg; - return ir_build_typeof(irb, scope, node, arg); + + IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg); + return ir_lval_wrap(irb, scope, type_of, lval); } case BuiltinFnIdSetCold: { @@ -3618,7 +3624,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_cold(irb, scope, node, arg0_value); + IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_cold, lval); } case BuiltinFnIdSetRuntimeSafety: { @@ -3627,7 +3634,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_runtime_safety(irb, scope, node, arg0_value); + IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_safety, lval); } case BuiltinFnIdSetFloatMode: { @@ -3641,7 +3649,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value); + IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, set_float_mode, lval); } case BuiltinFnIdSizeof: { @@ -3650,7 +3659,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_size_of(irb, scope, node, arg0_value); + IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, size_of, lval); } case BuiltinFnIdCtz: { @@ -3659,7 +3669,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_ctz(irb, scope, node, arg0_value); + IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ctz, lval); } case BuiltinFnIdClz: { @@ -3668,7 +3679,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_clz(irb, scope, node, arg0_value); + IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, clz, lval); } case BuiltinFnIdImport: { @@ -3677,11 +3689,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_import(irb, scope, node, arg0_value); + IrInstruction *import = ir_build_import(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, import, lval); } case BuiltinFnIdCImport: { - return ir_build_c_import(irb, scope, node); + IrInstruction *c_import = ir_build_c_import(irb, scope, node); + return ir_lval_wrap(irb, scope, c_import, lval); } case BuiltinFnIdCInclude: { @@ -3695,7 +3709,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_c_include(irb, scope, node, arg0_value); + IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, c_include, lval); } case BuiltinFnIdCDefine: { @@ -3714,7 +3729,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_c_define(irb, scope, node, arg0_value, arg1_value); + IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, c_define, lval); } case BuiltinFnIdCUndef: { @@ -3728,7 +3744,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_c_undef(irb, scope, node, arg0_value); + IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, c_undef, lval); } case BuiltinFnIdMaxValue: { @@ -3737,7 +3754,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_max_value(irb, scope, node, arg0_value); + IrInstruction *max_value = ir_build_max_value(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, max_value, lval); } case BuiltinFnIdMinValue: { @@ -3746,7 +3764,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_min_value(irb, scope, node, arg0_value); + IrInstruction *min_value = ir_build_min_value(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, min_value, lval); } case BuiltinFnIdCompileErr: { @@ -3755,7 +3774,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_compile_err(irb, scope, node, arg0_value); + IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, compile_err, lval); } case BuiltinFnIdCompileLog: { @@ -3768,7 +3788,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - return ir_build_compile_log(irb, scope, node, actual_param_count, args); + IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); + return ir_lval_wrap(irb, scope, compile_log, lval); } case BuiltinFnIdErrName: { @@ -3777,7 +3798,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_err_name(irb, scope, node, arg0_value); + IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, err_name, lval); } case BuiltinFnIdEmbedFile: { @@ -3786,7 +3808,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_embed_file(irb, scope, node, arg0_value); + IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, embed_file, lval); } case BuiltinFnIdCmpxchgWeak: case BuiltinFnIdCmpxchgStrong: @@ -3821,9 +3844,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, + IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: { @@ -3832,7 +3856,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); + IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered); + return ir_lval_wrap(irb, scope, fence, lval); } case BuiltinFnIdDivExact: { @@ -3846,7 +3871,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdDivTrunc: { @@ -3860,7 +3886,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdDivFloor: { @@ -3874,7 +3901,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdRem: { @@ -3888,7 +3916,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdMod: { @@ -3902,7 +3931,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdSqrt: { @@ -3916,7 +3946,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, ir_sqrt, lval); } case BuiltinFnIdTruncate: { @@ -3930,7 +3961,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_truncate(irb, scope, node, arg0_value, arg1_value); + IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, truncate, lval); } case BuiltinFnIdIntType: { @@ -3944,7 +3976,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_int_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, int_type, lval); } case BuiltinFnIdMemcpy: { @@ -3963,7 +3996,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_memcpy, lval); } case BuiltinFnIdMemset: { @@ -3982,7 +4016,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_memset, lval); } case BuiltinFnIdMemberCount: { @@ -3991,7 +4026,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_member_count(irb, scope, node, arg0_value); + IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, member_count, lval); } case BuiltinFnIdMemberType: { @@ -4006,7 +4042,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; - return ir_build_member_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, member_type, lval); } case BuiltinFnIdMemberName: { @@ -4021,7 +4058,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg1_value; - return ir_build_member_name(irb, scope, node, arg0_value, arg1_value); + IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, member_name, lval); } case BuiltinFnIdField: { @@ -4035,18 +4073,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_instruction = ir_build_field_ptr_inner(irb, scope, node, arg0_value, arg1_value); - //if (lval.is_ptr) - // return ptr_instruction; + IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); + + if (lval.is_ptr) + return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); } case BuiltinFnIdBreakpoint: - return ir_build_breakpoint(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); case BuiltinFnIdReturnAddress: - return ir_build_return_address(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); case BuiltinFnIdFrameAddress: - return ir_build_frame_address(irb, scope, node); + return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4054,16 +4093,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_align_of(irb, scope, node, arg0_value); + IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, align_of, lval); } case BuiltinFnIdAddWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval); case BuiltinFnIdSubWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval); case BuiltinFnIdMulWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval); case BuiltinFnIdShlWithOverflow: - return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl); + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval); case BuiltinFnIdTypeName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4071,7 +4111,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_type_name(irb, scope, node, arg0_value); + IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_name, lval); } case BuiltinFnIdCanImplicitCast: { @@ -4085,7 +4126,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, can_implicit_cast, lval); } case BuiltinFnIdPanic: { @@ -4094,7 +4136,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_panic(irb, scope, node, arg0_value); + IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, panic, lval); } case BuiltinFnIdPtrCast: { @@ -4108,7 +4151,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: { @@ -4122,7 +4166,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, bit_cast, lval); } case BuiltinFnIdIntToPtr: { @@ -4136,7 +4181,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); + IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, int_to_ptr, lval); } case BuiltinFnIdPtrToInt: { @@ -4145,7 +4191,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_ptr_to_int(irb, scope, node, arg0_value); + IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ptr_to_int, lval); } case BuiltinFnIdTagName: { @@ -4155,7 +4202,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return arg0_value; IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value); - return ir_build_tag_name(irb, scope, node, actual_tag); + IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag); + return ir_lval_wrap(irb, scope, tag_name, lval); } case BuiltinFnIdTagType: { @@ -4164,7 +4212,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_tag_type(irb, scope, node, arg0_value); + IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, tag_type, lval); } case BuiltinFnIdFieldParentPtr: { @@ -4183,7 +4232,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); + IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr); + return ir_lval_wrap(irb, scope, field_parent_ptr, lval); } case BuiltinFnIdOffsetOf: { @@ -4197,7 +4247,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value); + IrInstruction *offset_of = ir_build_offset_of(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, offset_of, lval); } case BuiltinFnIdInlineCall: case BuiltinFnIdNoInlineCall: @@ -4223,7 +4274,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr); + return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: { @@ -4232,7 +4284,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_type_id(irb, scope, node, arg0_value); + IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_id, lval); } case BuiltinFnIdShlExact: { @@ -4246,7 +4299,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdShrExact: { @@ -4260,7 +4314,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); + IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval); } case BuiltinFnIdSetEvalBranchQuota: { @@ -4269,7 +4324,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); + IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval); } case BuiltinFnIdAlignCast: { @@ -4283,10 +4339,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, align_cast, lval); } case BuiltinFnIdOpaqueType: - return ir_build_opaque_type(irb, scope, node); + { + IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node); + return ir_lval_wrap(irb, scope, opaque_type, lval); + } case BuiltinFnIdSetAlignStack: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -4294,7 +4354,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; - return ir_build_set_align_stack(irb, scope, node, arg0_value); + IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_align_stack, lval); } case BuiltinFnIdArgType: { @@ -4308,7 +4369,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); + IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, arg_type, lval); } case BuiltinFnIdExport: { @@ -4327,11 +4389,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg2_value == irb->codegen->invalid_instruction) return arg2_value; - return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); + IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_export, lval); } case BuiltinFnIdErrorReturnTrace: { - return ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); + IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null); + return ir_lval_wrap(irb, scope, error_return_trace, lval); } case BuiltinFnIdAtomicRmw: { @@ -4390,11 +4454,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo zig_unreachable(); } -static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { +static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) - return ir_gen_builtin_fn_call(irb, scope, node); + return ir_gen_builtin_fn_call(irb, scope, node, lval); AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); @@ -4420,7 +4484,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator); + return ir_lval_wrap(irb, scope, fn_call, lval); } static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -6436,7 +6501,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSymbol: return ir_gen_symbol(irb, scope, node, lval); case NodeTypeFnCallExpr: - return ir_lval_wrap(irb, scope, ir_gen_fn_call(irb, scope, node), lval); + return ir_gen_fn_call(irb, scope, node, lval); case NodeTypeIfBoolExpr: return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: @@ -6456,7 +6521,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeReturnExpr: return ir_gen_return(irb, scope, node, lval); case NodeTypeFieldAccessExpr: - return ir_gen_field_access(irb, scope, node, lval); + { + IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); + if (lval.is_ptr) + return ptr_instruction; + + return ir_build_load_ptr(irb, scope, node, ptr_instruction); + } case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: @@ -13484,10 +13555,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru zig_unreachable(); } - IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; - Buf *field_name = ir_resolve_str(ira, field_name_expr); - if (!field_name) - return ira->codegen->builtin_types.entry_invalid; + Buf *field_name = field_ptr_instruction->field_name_buffer; + if (!field_name) { + IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other; + field_name = ir_resolve_str(ira, field_name_expr); + if (!field_name) + return ira->codegen->builtin_types.entry_invalid; + } + AstNode *source_node = field_ptr_instruction->base.source_node; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index bb22c258e2..a77ae244d4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -358,10 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins } static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) { - fprintf(irp->f, "fieldptr "); - ir_print_other_instruction(irp, instruction->container_ptr); - fprintf(irp->f, "."); - ir_print_other_instruction(irp, instruction->field_name_expr); + if (instruction->field_name_buffer) { + fprintf(irp->f, "fieldptr "); + ir_print_other_instruction(irp, instruction->container_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer)); + } else { + assert(instruction->field_name_expr); + fprintf(irp->f, "@field("); + ir_print_other_instruction(irp, instruction->container_ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->field_name_expr); + fprintf(irp->f, ")"); + } } static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) { diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index df723f9b0b..0abc46c9de 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -58,7 +58,7 @@ test "reflection: enum member types and names" { } test "reflection: @field" { - const f = Foo { + var f = Foo { .one = 42, .two = true, .three = void{}, @@ -70,12 +70,12 @@ test "reflection: @field" { assert(@field(f, "th" ++ "ree") == f.three); assert(@field(Foo, "const" ++ "ant") == Foo.constant); assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "O" ++ "ne") == Bar.One); assert(@field(Bar, "T" ++ "wo") == Bar.Two); assert(@field(Bar, "Th" ++ "ree") == Bar.Three); assert(@field(Bar, "F" ++ "our") == Bar.Four); assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + assert(f.one == 4); } const Foo = struct { -- cgit v1.2.3 From 6e57243a79b08f37b80122f0eb24d073d6e2e95c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Apr 2018 02:15:09 -0400 Subject: zig fmt: preserve comments in front of test blocks * refactor std.zig.parser * fix compiler crashing for some compile errors * take advantage of @field in std.zig.ast * move ast.NodeFoo to ast.Node.Foo * comment preservation is more explicit See #911 --- src/ir.cpp | 4 + std/zig/ast.zig | 2693 +++++++++++++++++++++++++--------------------------- std/zig/parser.zig | 975 ++++++++++--------- 3 files changed, 1820 insertions(+), 1852 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index a34c990afe..865a6823be 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6523,6 +6523,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeFieldAccessExpr: { IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); + if (ptr_instruction == irb->codegen->invalid_instruction) + return ptr_instruction; if (lval.is_ptr) return ptr_instruction; @@ -15556,6 +15558,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, } ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 17a19e6213..76977a979a 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -6,7 +6,6 @@ const mem = std.mem; pub const Node = struct { id: Id, - comment: ?&NodeLineComment, pub const Id = enum { // Top level @@ -74,1750 +73,1686 @@ pub const Node = struct { FieldInitializer, }; - const IdTypePair = struct { - id: Id, - Type: type, - }; - - // TODO: When @field exists, we could generate this by iterating over all members of `Id`, - // and making an array of `IdTypePair { .id = @field(Id, @memberName(Id, i)), .Type = @field(ast, "Node" ++ @memberName(Id, i)) }` - const idTypeTable = []IdTypePair { - IdTypePair { .id = Id.Root, .Type = NodeRoot }, - IdTypePair { .id = Id.Use, .Type = NodeUse }, - IdTypePair { .id = Id.TestDecl, .Type = NodeTestDecl }, - - IdTypePair { .id = Id.VarDecl, .Type = NodeVarDecl }, - IdTypePair { .id = Id.Defer, .Type = NodeDefer }, - - IdTypePair { .id = Id.InfixOp, .Type = NodeInfixOp }, - IdTypePair { .id = Id.PrefixOp, .Type = NodePrefixOp }, - IdTypePair { .id = Id.SuffixOp, .Type = NodeSuffixOp }, - - IdTypePair { .id = Id.Switch, .Type = NodeSwitch }, - IdTypePair { .id = Id.While, .Type = NodeWhile }, - IdTypePair { .id = Id.For, .Type = NodeFor }, - IdTypePair { .id = Id.If, .Type = NodeIf }, - IdTypePair { .id = Id.ControlFlowExpression, .Type = NodeControlFlowExpression }, - IdTypePair { .id = Id.Suspend, .Type = NodeSuspend }, - - IdTypePair { .id = Id.VarType, .Type = NodeVarType }, - IdTypePair { .id = Id.ErrorType, .Type = NodeErrorType }, - IdTypePair { .id = Id.FnProto, .Type = NodeFnProto }, - - IdTypePair { .id = Id.IntegerLiteral, .Type = NodeIntegerLiteral }, - IdTypePair { .id = Id.FloatLiteral, .Type = NodeFloatLiteral }, - IdTypePair { .id = Id.StringLiteral, .Type = NodeStringLiteral }, - IdTypePair { .id = Id.MultilineStringLiteral, .Type = NodeMultilineStringLiteral }, - IdTypePair { .id = Id.CharLiteral, .Type = NodeCharLiteral }, - IdTypePair { .id = Id.BoolLiteral, .Type = NodeBoolLiteral }, - IdTypePair { .id = Id.NullLiteral, .Type = NodeNullLiteral }, - IdTypePair { .id = Id.UndefinedLiteral, .Type = NodeUndefinedLiteral }, - IdTypePair { .id = Id.ThisLiteral, .Type = NodeThisLiteral }, - IdTypePair { .id = Id.Unreachable, .Type = NodeUnreachable }, - IdTypePair { .id = Id.Identifier, .Type = NodeIdentifier }, - IdTypePair { .id = Id.GroupedExpression, .Type = NodeGroupedExpression }, - IdTypePair { .id = Id.BuiltinCall, .Type = NodeBuiltinCall }, - IdTypePair { .id = Id.ErrorSetDecl, .Type = NodeErrorSetDecl }, - IdTypePair { .id = Id.ContainerDecl, .Type = NodeContainerDecl }, - IdTypePair { .id = Id.Asm, .Type = NodeAsm }, - IdTypePair { .id = Id.Comptime, .Type = NodeComptime }, - IdTypePair { .id = Id.Block, .Type = NodeBlock }, - - IdTypePair { .id = Id.LineComment, .Type = NodeLineComment }, - IdTypePair { .id = Id.SwitchCase, .Type = NodeSwitchCase }, - IdTypePair { .id = Id.SwitchElse, .Type = NodeSwitchElse }, - IdTypePair { .id = Id.Else, .Type = NodeElse }, - IdTypePair { .id = Id.Payload, .Type = NodePayload }, - IdTypePair { .id = Id.PointerPayload, .Type = NodePointerPayload }, - IdTypePair { .id = Id.PointerIndexPayload, .Type = NodePointerIndexPayload }, - IdTypePair { .id = Id.StructField, .Type = NodeStructField }, - IdTypePair { .id = Id.UnionTag, .Type = NodeUnionTag }, - IdTypePair { .id = Id.EnumTag, .Type = NodeEnumTag }, - IdTypePair { .id = Id.AsmInput, .Type = NodeAsmInput }, - IdTypePair { .id = Id.AsmOutput, .Type = NodeAsmOutput }, - IdTypePair { .id = Id.AsyncAttribute, .Type = NodeAsyncAttribute }, - IdTypePair { .id = Id.ParamDecl, .Type = NodeParamDecl }, - IdTypePair { .id = Id.FieldInitializer, .Type = NodeFieldInitializer }, - }; - - pub fn IdToType(comptime id: Id) type { - inline for (idTypeTable) |id_type_pair| { - if (id == id_type_pair.id) - return id_type_pair.Type; - } - - unreachable; - } - - pub fn typeToId(comptime T: type) Id { - inline for (idTypeTable) |id_type_pair| { - if (T == id_type_pair.Type) - return id_type_pair.id; - } - - unreachable; - } - pub fn iterate(base: &Node, index: usize) ?&Node { - inline for (idTypeTable) |id_type_pair| { - if (base.id == id_type_pair.id) - return @fieldParentPtr(id_type_pair.Type, "base", base).iterate(index); + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Node, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).iterate(index); + } } - unreachable; } pub fn firstToken(base: &Node) Token { - inline for (idTypeTable) |id_type_pair| { - if (base.id == id_type_pair.id) - return @fieldParentPtr(id_type_pair.Type, "base", base).firstToken(); + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Node, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).firstToken(); + } } - unreachable; } pub fn lastToken(base: &Node) Token { - inline for (idTypeTable) |id_type_pair| { - if (base.id == id_type_pair.id) - return @fieldParentPtr(id_type_pair.Type, "base", base).lastToken(); + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (base.id == @field(Id, @memberName(Id, i))) { + const T = @field(Node, @memberName(Id, i)); + return @fieldParentPtr(T, "base", base).lastToken(); + } } - unreachable; } -}; -pub const NodeRoot = struct { - base: Node, - decls: ArrayList(&Node), - eof_token: Token, - - pub fn iterate(self: &NodeRoot, index: usize) ?&Node { - if (index < self.decls.len) { - return self.decls.items[self.decls.len - index - 1]; + pub fn typeToId(comptime T: type) Id { + comptime var i = 0; + inline while (i < @memberCount(Id)) : (i += 1) { + if (T == @field(Node, @memberName(Id, i))) { + return @field(Id, @memberName(Id, i)); + } } - return null; - } - - pub fn firstToken(self: &NodeRoot) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); + unreachable; } - pub fn lastToken(self: &NodeRoot) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); - } -}; + pub const Root = struct { + base: Node, + decls: ArrayList(&Node), + eof_token: Token, -pub const NodeVarDecl = struct { - base: Node, - visib_token: ?Token, - name_token: Token, - eq_token: Token, - mut_token: Token, - comptime_token: ?Token, - extern_export_token: ?Token, - lib_name: ?&Node, - type_node: ?&Node, - align_node: ?&Node, - init_node: ?&Node, - semicolon_token: Token, - - pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node { - var i = index; - - if (self.type_node) |type_node| { - if (i < 1) return type_node; - i -= 1; + pub fn iterate(self: &Root, index: usize) ?&Node { + if (index < self.decls.len) { + return self.decls.items[self.decls.len - index - 1]; + } + return null; } - if (self.align_node) |align_node| { - if (i < 1) return align_node; - i -= 1; + pub fn firstToken(self: &Root) Token { + return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); } - if (self.init_node) |init_node| { - if (i < 1) return init_node; - i -= 1; + pub fn lastToken(self: &Root) Token { + return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); } + }; - return null; - } - - pub fn firstToken(self: &NodeVarDecl) Token { - if (self.visib_token) |visib_token| return visib_token; - if (self.comptime_token) |comptime_token| return comptime_token; - if (self.extern_export_token) |extern_export_token| return extern_export_token; - assert(self.lib_name == null); - return self.mut_token; - } - - pub fn lastToken(self: &NodeVarDecl) Token { - return self.semicolon_token; - } -}; - -pub const NodeUse = struct { - base: Node, - visib_token: ?Token, - expr: &Node, - semicolon_token: Token, - - pub fn iterate(self: &NodeUse, index: usize) ?&Node { - var i = index; + pub const VarDecl = struct { + base: Node, + comments: ?&LineComment, + visib_token: ?Token, + name_token: Token, + eq_token: Token, + mut_token: Token, + comptime_token: ?Token, + extern_export_token: ?Token, + lib_name: ?&Node, + type_node: ?&Node, + align_node: ?&Node, + init_node: ?&Node, + semicolon_token: Token, + + pub fn iterate(self: &VarDecl, index: usize) ?&Node { + var i = index; + + if (self.type_node) |type_node| { + if (i < 1) return type_node; + i -= 1; + } - if (i < 1) return self.expr; - i -= 1; + if (self.align_node) |align_node| { + if (i < 1) return align_node; + i -= 1; + } - return null; - } + if (self.init_node) |init_node| { + if (i < 1) return init_node; + i -= 1; + } - pub fn firstToken(self: &NodeUse) Token { - if (self.visib_token) |visib_token| return visib_token; - return self.expr.firstToken(); - } + return null; + } - pub fn lastToken(self: &NodeUse) Token { - return self.semicolon_token; - } -}; + pub fn firstToken(self: &VarDecl) Token { + if (self.visib_token) |visib_token| return visib_token; + if (self.comptime_token) |comptime_token| return comptime_token; + if (self.extern_export_token) |extern_export_token| return extern_export_token; + assert(self.lib_name == null); + return self.mut_token; + } -pub const NodeErrorSetDecl = struct { - base: Node, - error_token: Token, - decls: ArrayList(&Node), - rbrace_token: Token, + pub fn lastToken(self: &VarDecl) Token { + return self.semicolon_token; + } + }; - pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node { - var i = index; + pub const Use = struct { + base: Node, + visib_token: ?Token, + expr: &Node, + semicolon_token: Token, - if (i < self.decls.len) return self.decls.at(i); - i -= self.decls.len; + pub fn iterate(self: &Use, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.expr; + i -= 1; - pub fn firstToken(self: &NodeErrorSetDecl) Token { - return self.error_token; - } + return null; + } - pub fn lastToken(self: &NodeErrorSetDecl) Token { - return self.rbrace_token; - } -}; + pub fn firstToken(self: &Use) Token { + if (self.visib_token) |visib_token| return visib_token; + return self.expr.firstToken(); + } -pub const NodeContainerDecl = struct { - base: Node, - ltoken: Token, - layout: Layout, - kind: Kind, - init_arg_expr: InitArg, - fields_and_decls: ArrayList(&Node), - rbrace_token: Token, - - const Layout = enum { - Auto, - Extern, - Packed, + pub fn lastToken(self: &Use) Token { + return self.semicolon_token; + } }; - const Kind = enum { - Struct, - Enum, - Union, - }; + pub const ErrorSetDecl = struct { + base: Node, + error_token: Token, + decls: ArrayList(&Node), + rbrace_token: Token, - const InitArg = union(enum) { - None, - Enum, - Type: &Node, - }; + pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeContainerDecl, index: usize) ?&Node { - var i = index; + if (i < self.decls.len) return self.decls.at(i); + i -= self.decls.len; - switch (self.init_arg_expr) { - InitArg.Type => |t| { - if (i < 1) return t; - i -= 1; - }, - InitArg.None, - InitArg.Enum => { } + return null; } - if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i); - i -= self.fields_and_decls.len; + pub fn firstToken(self: &ErrorSetDecl) Token { + return self.error_token; + } - return null; - } + pub fn lastToken(self: &ErrorSetDecl) Token { + return self.rbrace_token; + } + }; - pub fn firstToken(self: &NodeContainerDecl) Token { - return self.ltoken; - } + pub const ContainerDecl = struct { + base: Node, + ltoken: Token, + layout: Layout, + kind: Kind, + init_arg_expr: InitArg, + fields_and_decls: ArrayList(&Node), + rbrace_token: Token, + + const Layout = enum { + Auto, + Extern, + Packed, + }; - pub fn lastToken(self: &NodeContainerDecl) Token { - return self.rbrace_token; - } -}; + const Kind = enum { + Struct, + Enum, + Union, + }; + + const InitArg = union(enum) { + None, + Enum, + Type: &Node, + }; -pub const NodeStructField = struct { - base: Node, - visib_token: ?Token, - name_token: Token, - type_expr: &Node, + pub fn iterate(self: &ContainerDecl, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeStructField, index: usize) ?&Node { - var i = index; + switch (self.init_arg_expr) { + InitArg.Type => |t| { + if (i < 1) return t; + i -= 1; + }, + InitArg.None, + InitArg.Enum => { } + } - if (i < 1) return self.type_expr; - i -= 1; + if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i); + i -= self.fields_and_decls.len; - return null; - } + return null; + } - pub fn firstToken(self: &NodeStructField) Token { - if (self.visib_token) |visib_token| return visib_token; - return self.name_token; - } + pub fn firstToken(self: &ContainerDecl) Token { + return self.ltoken; + } - pub fn lastToken(self: &NodeStructField) Token { - return self.type_expr.lastToken(); - } -}; + pub fn lastToken(self: &ContainerDecl) Token { + return self.rbrace_token; + } + }; -pub const NodeUnionTag = struct { - base: Node, - name_token: Token, - type_expr: ?&Node, + pub const StructField = struct { + base: Node, + visib_token: ?Token, + name_token: Token, + type_expr: &Node, - pub fn iterate(self: &NodeUnionTag, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &StructField, index: usize) ?&Node { + var i = index; - if (self.type_expr) |type_expr| { - if (i < 1) return type_expr; + if (i < 1) return self.type_expr; i -= 1; - } - return null; - } + return null; + } - pub fn firstToken(self: &NodeUnionTag) Token { - return self.name_token; - } + pub fn firstToken(self: &StructField) Token { + if (self.visib_token) |visib_token| return visib_token; + return self.name_token; + } - pub fn lastToken(self: &NodeUnionTag) Token { - if (self.type_expr) |type_expr| { - return type_expr.lastToken(); + pub fn lastToken(self: &StructField) Token { + return self.type_expr.lastToken(); } + }; - return self.name_token; - } -}; + pub const UnionTag = struct { + base: Node, + name_token: Token, + type_expr: ?&Node, -pub const NodeEnumTag = struct { - base: Node, - name_token: Token, - value: ?&Node, + pub fn iterate(self: &UnionTag, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeEnumTag, index: usize) ?&Node { - var i = index; + if (self.type_expr) |type_expr| { + if (i < 1) return type_expr; + i -= 1; + } - if (self.value) |value| { - if (i < 1) return value; - i -= 1; + return null; } - return null; - } + pub fn firstToken(self: &UnionTag) Token { + return self.name_token; + } - pub fn firstToken(self: &NodeEnumTag) Token { - return self.name_token; - } + pub fn lastToken(self: &UnionTag) Token { + if (self.type_expr) |type_expr| { + return type_expr.lastToken(); + } - pub fn lastToken(self: &NodeEnumTag) Token { - if (self.value) |value| { - return value.lastToken(); + return self.name_token; } + }; - return self.name_token; - } -}; - -pub const NodeIdentifier = struct { - base: Node, - token: Token, + pub const EnumTag = struct { + base: Node, + name_token: Token, + value: ?&Node, - pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &EnumTag, index: usize) ?&Node { + var i = index; - pub fn firstToken(self: &NodeIdentifier) Token { - return self.token; - } + if (self.value) |value| { + if (i < 1) return value; + i -= 1; + } - pub fn lastToken(self: &NodeIdentifier) Token { - return self.token; - } -}; + return null; + } -pub const NodeAsyncAttribute = struct { - base: Node, - async_token: Token, - allocator_type: ?&Node, - rangle_bracket: ?Token, + pub fn firstToken(self: &EnumTag) Token { + return self.name_token; + } - pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node { - var i = index; + pub fn lastToken(self: &EnumTag) Token { + if (self.value) |value| { + return value.lastToken(); + } - if (self.allocator_type) |allocator_type| { - if (i < 1) return allocator_type; - i -= 1; + return self.name_token; } + }; - return null; - } - - pub fn firstToken(self: &NodeAsyncAttribute) Token { - return self.async_token; - } + pub const Identifier = struct { + base: Node, + token: Token, - pub fn lastToken(self: &NodeAsyncAttribute) Token { - if (self.rangle_bracket) |rangle_bracket| { - return rangle_bracket; + pub fn iterate(self: &Identifier, index: usize) ?&Node { + return null; } - return self.async_token; - } -}; + pub fn firstToken(self: &Identifier) Token { + return self.token; + } -pub const NodeFnProto = struct { - base: Node, - visib_token: ?Token, - fn_token: Token, - name_token: ?Token, - params: ArrayList(&Node), - return_type: ReturnType, - var_args_token: ?Token, - extern_export_inline_token: ?Token, - cc_token: ?Token, - async_attr: ?&NodeAsyncAttribute, - body_node: ?&Node, - lib_name: ?&Node, // populated if this is an extern declaration - align_expr: ?&Node, // populated if align(A) is present - - pub const ReturnType = union(enum) { - Explicit: &Node, - InferErrorSet: &Node, + pub fn lastToken(self: &Identifier) Token { + return self.token; + } }; - pub fn iterate(self: &NodeFnProto, index: usize) ?&Node { - var i = index; + pub const AsyncAttribute = struct { + base: Node, + async_token: Token, + allocator_type: ?&Node, + rangle_bracket: ?Token, - if (self.body_node) |body_node| { - if (i < 1) return body_node; - i -= 1; - } + pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node { + var i = index; - switch (self.return_type) { - // TODO allow this and next prong to share bodies since the types are the same - ReturnType.Explicit => |node| { - if (i < 1) return node; + if (self.allocator_type) |allocator_type| { + if (i < 1) return allocator_type; i -= 1; - }, - ReturnType.InferErrorSet => |node| { - if (i < 1) return node; - i -= 1; - }, + } + + return null; } - if (self.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; + pub fn firstToken(self: &AsyncAttribute) Token { + return self.async_token; } - if (i < self.params.len) return self.params.items[self.params.len - i - 1]; - i -= self.params.len; + pub fn lastToken(self: &AsyncAttribute) Token { + if (self.rangle_bracket) |rangle_bracket| { + return rangle_bracket; + } - if (self.lib_name) |lib_name| { - if (i < 1) return lib_name; - i -= 1; + return self.async_token; } + }; - return null; - } + pub const FnProto = struct { + base: Node, + comments: ?&LineComment, + visib_token: ?Token, + fn_token: Token, + name_token: ?Token, + params: ArrayList(&Node), + return_type: ReturnType, + var_args_token: ?Token, + extern_export_inline_token: ?Token, + cc_token: ?Token, + async_attr: ?&AsyncAttribute, + body_node: ?&Node, + lib_name: ?&Node, // populated if this is an extern declaration + align_expr: ?&Node, // populated if align(A) is present + + pub const ReturnType = union(enum) { + Explicit: &Node, + InferErrorSet: &Node, + }; - pub fn firstToken(self: &NodeFnProto) Token { - if (self.visib_token) |visib_token| return visib_token; - if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; - assert(self.lib_name == null); - if (self.cc_token) |cc_token| return cc_token; - return self.fn_token; - } + pub fn iterate(self: &FnProto, index: usize) ?&Node { + var i = index; - pub fn lastToken(self: &NodeFnProto) Token { - if (self.body_node) |body_node| return body_node.lastToken(); - switch (self.return_type) { - // TODO allow this and next prong to share bodies since the types are the same - ReturnType.Explicit => |node| return node.lastToken(), - ReturnType.InferErrorSet => |node| return node.lastToken(), - } - } -}; + if (self.body_node) |body_node| { + if (i < 1) return body_node; + i -= 1; + } -pub const NodeParamDecl = struct { - base: Node, - comptime_token: ?Token, - noalias_token: ?Token, - name_token: ?Token, - type_node: &Node, - var_args_token: ?Token, + switch (self.return_type) { + // TODO allow this and next prong to share bodies since the types are the same + ReturnType.Explicit => |node| { + if (i < 1) return node; + i -= 1; + }, + ReturnType.InferErrorSet => |node| { + if (i < 1) return node; + i -= 1; + }, + } - pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node { - var i = index; + if (self.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } - if (i < 1) return self.type_node; - i -= 1; + if (i < self.params.len) return self.params.items[self.params.len - i - 1]; + i -= self.params.len; - return null; - } + if (self.lib_name) |lib_name| { + if (i < 1) return lib_name; + i -= 1; + } - pub fn firstToken(self: &NodeParamDecl) Token { - if (self.comptime_token) |comptime_token| return comptime_token; - if (self.noalias_token) |noalias_token| return noalias_token; - if (self.name_token) |name_token| return name_token; - return self.type_node.firstToken(); - } + return null; + } - pub fn lastToken(self: &NodeParamDecl) Token { - if (self.var_args_token) |var_args_token| return var_args_token; - return self.type_node.lastToken(); - } -}; + pub fn firstToken(self: &FnProto) Token { + if (self.visib_token) |visib_token| return visib_token; + if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; + assert(self.lib_name == null); + if (self.cc_token) |cc_token| return cc_token; + return self.fn_token; + } -pub const NodeBlock = struct { - base: Node, - label: ?Token, - lbrace: Token, - statements: ArrayList(&Node), - rbrace: Token, + pub fn lastToken(self: &FnProto) Token { + if (self.body_node) |body_node| return body_node.lastToken(); + switch (self.return_type) { + // TODO allow this and next prong to share bodies since the types are the same + ReturnType.Explicit => |node| return node.lastToken(), + ReturnType.InferErrorSet => |node| return node.lastToken(), + } + } + }; - pub fn iterate(self: &NodeBlock, index: usize) ?&Node { - var i = index; + pub const ParamDecl = struct { + base: Node, + comptime_token: ?Token, + noalias_token: ?Token, + name_token: ?Token, + type_node: &Node, + var_args_token: ?Token, - if (i < self.statements.len) return self.statements.items[i]; - i -= self.statements.len; + pub fn iterate(self: &ParamDecl, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.type_node; + i -= 1; - pub fn firstToken(self: &NodeBlock) Token { - if (self.label) |label| { - return label; + return null; } - return self.lbrace; - } + pub fn firstToken(self: &ParamDecl) Token { + if (self.comptime_token) |comptime_token| return comptime_token; + if (self.noalias_token) |noalias_token| return noalias_token; + if (self.name_token) |name_token| return name_token; + return self.type_node.firstToken(); + } - pub fn lastToken(self: &NodeBlock) Token { - return self.rbrace; - } -}; + pub fn lastToken(self: &ParamDecl) Token { + if (self.var_args_token) |var_args_token| return var_args_token; + return self.type_node.lastToken(); + } + }; -pub const NodeDefer = struct { - base: Node, - defer_token: Token, - kind: Kind, - expr: &Node, + pub const Block = struct { + base: Node, + label: ?Token, + lbrace: Token, + statements: ArrayList(&Node), + rbrace: Token, - const Kind = enum { - Error, - Unconditional, - }; + pub fn iterate(self: &Block, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeDefer, index: usize) ?&Node { - var i = index; + if (i < self.statements.len) return self.statements.items[i]; + i -= self.statements.len; - if (i < 1) return self.expr; - i -= 1; + return null; + } - return null; - } + pub fn firstToken(self: &Block) Token { + if (self.label) |label| { + return label; + } - pub fn firstToken(self: &NodeDefer) Token { - return self.defer_token; - } + return self.lbrace; + } - pub fn lastToken(self: &NodeDefer) Token { - return self.expr.lastToken(); - } -}; + pub fn lastToken(self: &Block) Token { + return self.rbrace; + } + }; -pub const NodeComptime = struct { - base: Node, - comptime_token: Token, - expr: &Node, + pub const Defer = struct { + base: Node, + defer_token: Token, + kind: Kind, + expr: &Node, - pub fn iterate(self: &NodeComptime, index: usize) ?&Node { - var i = index; + const Kind = enum { + Error, + Unconditional, + }; - if (i < 1) return self.expr; - i -= 1; + pub fn iterate(self: &Defer, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.expr; + i -= 1; - pub fn firstToken(self: &NodeComptime) Token { - return self.comptime_token; - } + return null; + } - pub fn lastToken(self: &NodeComptime) Token { - return self.expr.lastToken(); - } -}; + pub fn firstToken(self: &Defer) Token { + return self.defer_token; + } -pub const NodePayload = struct { - base: Node, - lpipe: Token, - error_symbol: &Node, - rpipe: Token, + pub fn lastToken(self: &Defer) Token { + return self.expr.lastToken(); + } + }; - pub fn iterate(self: &NodePayload, index: usize) ?&Node { - var i = index; + pub const Comptime = struct { + base: Node, + comptime_token: Token, + expr: &Node, - if (i < 1) return self.error_symbol; - i -= 1; + pub fn iterate(self: &Comptime, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.expr; + i -= 1; - pub fn firstToken(self: &NodePayload) Token { - return self.lpipe; - } + return null; + } - pub fn lastToken(self: &NodePayload) Token { - return self.rpipe; - } -}; + pub fn firstToken(self: &Comptime) Token { + return self.comptime_token; + } -pub const NodePointerPayload = struct { - base: Node, - lpipe: Token, - ptr_token: ?Token, - value_symbol: &Node, - rpipe: Token, + pub fn lastToken(self: &Comptime) Token { + return self.expr.lastToken(); + } + }; - pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node { - var i = index; + pub const Payload = struct { + base: Node, + lpipe: Token, + error_symbol: &Node, + rpipe: Token, - if (i < 1) return self.value_symbol; - i -= 1; + pub fn iterate(self: &Payload, index: usize) ?&Node { + var i = index; - return null; - } + if (i < 1) return self.error_symbol; + i -= 1; - pub fn firstToken(self: &NodePointerPayload) Token { - return self.lpipe; - } + return null; + } - pub fn lastToken(self: &NodePointerPayload) Token { - return self.rpipe; - } -}; + pub fn firstToken(self: &Payload) Token { + return self.lpipe; + } -pub const NodePointerIndexPayload = struct { - base: Node, - lpipe: Token, - ptr_token: ?Token, - value_symbol: &Node, - index_symbol: ?&Node, - rpipe: Token, + pub fn lastToken(self: &Payload) Token { + return self.rpipe; + } + }; - pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node { - var i = index; + pub const PointerPayload = struct { + base: Node, + lpipe: Token, + ptr_token: ?Token, + value_symbol: &Node, + rpipe: Token, - if (i < 1) return self.value_symbol; - i -= 1; + pub fn iterate(self: &PointerPayload, index: usize) ?&Node { + var i = index; - if (self.index_symbol) |index_symbol| { - if (i < 1) return index_symbol; + if (i < 1) return self.value_symbol; i -= 1; - } - return null; - } + return null; + } - pub fn firstToken(self: &NodePointerIndexPayload) Token { - return self.lpipe; - } + pub fn firstToken(self: &PointerPayload) Token { + return self.lpipe; + } - pub fn lastToken(self: &NodePointerIndexPayload) Token { - return self.rpipe; - } -}; + pub fn lastToken(self: &PointerPayload) Token { + return self.rpipe; + } + }; -pub const NodeElse = struct { - base: Node, - else_token: Token, - payload: ?&Node, - body: &Node, + pub const PointerIndexPayload = struct { + base: Node, + lpipe: Token, + ptr_token: ?Token, + value_symbol: &Node, + index_symbol: ?&Node, + rpipe: Token, - pub fn iterate(self: &NodeElse, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node { + var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.value_symbol; i -= 1; - } - if (i < 1) return self.body; - i -= 1; - - return null; - } + if (self.index_symbol) |index_symbol| { + if (i < 1) return index_symbol; + i -= 1; + } - pub fn firstToken(self: &NodeElse) Token { - return self.else_token; - } + return null; + } - pub fn lastToken(self: &NodeElse) Token { - return self.body.lastToken(); - } -}; + pub fn firstToken(self: &PointerIndexPayload) Token { + return self.lpipe; + } -pub const NodeSwitch = struct { - base: Node, - switch_token: Token, - expr: &Node, - cases: ArrayList(&NodeSwitchCase), - rbrace: Token, + pub fn lastToken(self: &PointerIndexPayload) Token { + return self.rpipe; + } + }; - pub fn iterate(self: &NodeSwitch, index: usize) ?&Node { - var i = index; + pub const Else = struct { + base: Node, + else_token: Token, + payload: ?&Node, + body: &Node, - if (i < 1) return self.expr; - i -= 1; + pub fn iterate(self: &Else, index: usize) ?&Node { + var i = index; - if (i < self.cases.len) return &self.cases.at(i).base; - i -= self.cases.len; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - return null; - } + if (i < 1) return self.body; + i -= 1; - pub fn firstToken(self: &NodeSwitch) Token { - return self.switch_token; - } + return null; + } - pub fn lastToken(self: &NodeSwitch) Token { - return self.rbrace; - } -}; + pub fn firstToken(self: &Else) Token { + return self.else_token; + } -pub const NodeSwitchCase = struct { - base: Node, - items: ArrayList(&Node), - payload: ?&Node, - expr: &Node, + pub fn lastToken(self: &Else) Token { + return self.body.lastToken(); + } + }; - pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node { - var i = index; + pub const Switch = struct { + base: Node, + switch_token: Token, + expr: &Node, + cases: ArrayList(&SwitchCase), + rbrace: Token, - if (i < self.items.len) return self.items.at(i); - i -= self.items.len; + pub fn iterate(self: &Switch, index: usize) ?&Node { + var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.expr; i -= 1; - } - if (i < 1) return self.expr; - i -= 1; + if (i < self.cases.len) return &self.cases.at(i).base; + i -= self.cases.len; - return null; - } + return null; + } - pub fn firstToken(self: &NodeSwitchCase) Token { - return self.items.at(0).firstToken(); - } + pub fn firstToken(self: &Switch) Token { + return self.switch_token; + } - pub fn lastToken(self: &NodeSwitchCase) Token { - return self.expr.lastToken(); - } -}; + pub fn lastToken(self: &Switch) Token { + return self.rbrace; + } + }; -pub const NodeSwitchElse = struct { - base: Node, - token: Token, + pub const SwitchCase = struct { + base: Node, + items: ArrayList(&Node), + payload: ?&Node, + expr: &Node, - pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &SwitchCase, index: usize) ?&Node { + var i = index; - pub fn firstToken(self: &NodeSwitchElse) Token { - return self.token; - } + if (i < self.items.len) return self.items.at(i); + i -= self.items.len; - pub fn lastToken(self: &NodeSwitchElse) Token { - return self.token; - } -}; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } -pub const NodeWhile = struct { - base: Node, - label: ?Token, - inline_token: ?Token, - while_token: Token, - condition: &Node, - payload: ?&Node, - continue_expr: ?&Node, - body: &Node, - @"else": ?&NodeElse, - - pub fn iterate(self: &NodeWhile, index: usize) ?&Node { - var i = index; - - if (i < 1) return self.condition; - i -= 1; - - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.expr; i -= 1; - } - if (self.continue_expr) |continue_expr| { - if (i < 1) return continue_expr; - i -= 1; + return null; } - if (i < 1) return self.body; - i -= 1; + pub fn firstToken(self: &SwitchCase) Token { + return self.items.at(0).firstToken(); + } - if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; - i -= 1; + pub fn lastToken(self: &SwitchCase) Token { + return self.expr.lastToken(); } + }; - return null; - } + pub const SwitchElse = struct { + base: Node, + token: Token, - pub fn firstToken(self: &NodeWhile) Token { - if (self.label) |label| { - return label; + pub fn iterate(self: &SwitchElse, index: usize) ?&Node { + return null; } - if (self.inline_token) |inline_token| { - return inline_token; + pub fn firstToken(self: &SwitchElse) Token { + return self.token; } - return self.while_token; - } - - pub fn lastToken(self: &NodeWhile) Token { - if (self.@"else") |@"else"| { - return @"else".body.lastToken(); + pub fn lastToken(self: &SwitchElse) Token { + return self.token; } + }; - return self.body.lastToken(); - } -}; - -pub const NodeFor = struct { - base: Node, - label: ?Token, - inline_token: ?Token, - for_token: Token, - array_expr: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&NodeElse, + pub const While = struct { + base: Node, + label: ?Token, + inline_token: ?Token, + while_token: Token, + condition: &Node, + payload: ?&Node, + continue_expr: ?&Node, + body: &Node, + @"else": ?&Else, + + pub fn iterate(self: &While, index: usize) ?&Node { + var i = index; + + if (i < 1) return self.condition; + i -= 1; - pub fn iterate(self: &NodeFor, index: usize) ?&Node { - var i = index; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - if (i < 1) return self.array_expr; - i -= 1; + if (self.continue_expr) |continue_expr| { + if (i < 1) return continue_expr; + i -= 1; + } - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.body; i -= 1; - } - if (i < 1) return self.body; - i -= 1; + if (self.@"else") |@"else"| { + if (i < 1) return &@"else".base; + i -= 1; + } - if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; - i -= 1; + return null; } - return null; - } + pub fn firstToken(self: &While) Token { + if (self.label) |label| { + return label; + } - pub fn firstToken(self: &NodeFor) Token { - if (self.label) |label| { - return label; - } + if (self.inline_token) |inline_token| { + return inline_token; + } - if (self.inline_token) |inline_token| { - return inline_token; + return self.while_token; } - return self.for_token; - } + pub fn lastToken(self: &While) Token { + if (self.@"else") |@"else"| { + return @"else".body.lastToken(); + } - pub fn lastToken(self: &NodeFor) Token { - if (self.@"else") |@"else"| { - return @"else".body.lastToken(); + return self.body.lastToken(); } + }; - return self.body.lastToken(); - } -}; + pub const For = struct { + base: Node, + label: ?Token, + inline_token: ?Token, + for_token: Token, + array_expr: &Node, + payload: ?&Node, + body: &Node, + @"else": ?&Else, -pub const NodeIf = struct { - base: Node, - if_token: Token, - condition: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&NodeElse, + pub fn iterate(self: &For, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeIf, index: usize) ?&Node { - var i = index; + if (i < 1) return self.array_expr; + i -= 1; - if (i < 1) return self.condition; - i -= 1; + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - if (self.payload) |payload| { - if (i < 1) return payload; + if (i < 1) return self.body; i -= 1; - } - if (i < 1) return self.body; - i -= 1; + if (self.@"else") |@"else"| { + if (i < 1) return &@"else".base; + i -= 1; + } - if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; - i -= 1; + return null; } - return null; - } + pub fn firstToken(self: &For) Token { + if (self.label) |label| { + return label; + } - pub fn firstToken(self: &NodeIf) Token { - return self.if_token; - } + if (self.inline_token) |inline_token| { + return inline_token; + } - pub fn lastToken(self: &NodeIf) Token { - if (self.@"else") |@"else"| { - return @"else".body.lastToken(); + return self.for_token; } - return self.body.lastToken(); - } -}; + pub fn lastToken(self: &For) Token { + if (self.@"else") |@"else"| { + return @"else".body.lastToken(); + } -pub const NodeInfixOp = struct { - base: Node, - op_token: Token, - lhs: &Node, - op: InfixOp, - rhs: &Node, - - const InfixOp = union(enum) { - Add, - AddWrap, - ArrayCat, - ArrayMult, - Assign, - AssignBitAnd, - AssignBitOr, - AssignBitShiftLeft, - AssignBitShiftRight, - AssignBitXor, - AssignDiv, - AssignMinus, - AssignMinusWrap, - AssignMod, - AssignPlus, - AssignPlusWrap, - AssignTimes, - AssignTimesWarp, - BangEqual, - BitAnd, - BitOr, - BitShiftLeft, - BitShiftRight, - BitXor, - BoolAnd, - BoolOr, - Catch: ?&Node, - Div, - EqualEqual, - ErrorUnion, - GreaterOrEqual, - GreaterThan, - LessOrEqual, - LessThan, - MergeErrorSets, - Mod, - Mult, - MultWrap, - Period, - Range, - Sub, - SubWrap, - UnwrapMaybe, + return self.body.lastToken(); + } }; - pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node { - var i = index; + pub const If = struct { + base: Node, + if_token: Token, + condition: &Node, + payload: ?&Node, + body: &Node, + @"else": ?&Else, - if (i < 1) return self.lhs; - i -= 1; + pub fn iterate(self: &If, index: usize) ?&Node { + var i = index; - switch (self.op) { - InfixOp.Catch => |maybe_payload| { - if (maybe_payload) |payload| { - if (i < 1) return payload; - i -= 1; - } - }, - - InfixOp.Add, - InfixOp.AddWrap, - InfixOp.ArrayCat, - InfixOp.ArrayMult, - InfixOp.Assign, - InfixOp.AssignBitAnd, - InfixOp.AssignBitOr, - InfixOp.AssignBitShiftLeft, - InfixOp.AssignBitShiftRight, - InfixOp.AssignBitXor, - InfixOp.AssignDiv, - InfixOp.AssignMinus, - InfixOp.AssignMinusWrap, - InfixOp.AssignMod, - InfixOp.AssignPlus, - InfixOp.AssignPlusWrap, - InfixOp.AssignTimes, - InfixOp.AssignTimesWarp, - InfixOp.BangEqual, - InfixOp.BitAnd, - InfixOp.BitOr, - InfixOp.BitShiftLeft, - InfixOp.BitShiftRight, - InfixOp.BitXor, - InfixOp.BoolAnd, - InfixOp.BoolOr, - InfixOp.Div, - InfixOp.EqualEqual, - InfixOp.ErrorUnion, - InfixOp.GreaterOrEqual, - InfixOp.GreaterThan, - InfixOp.LessOrEqual, - InfixOp.LessThan, - InfixOp.MergeErrorSets, - InfixOp.Mod, - InfixOp.Mult, - InfixOp.MultWrap, - InfixOp.Period, - InfixOp.Range, - InfixOp.Sub, - InfixOp.SubWrap, - InfixOp.UnwrapMaybe => {}, - } - - if (i < 1) return self.rhs; - i -= 1; - - return null; - } + if (i < 1) return self.condition; + i -= 1; - pub fn firstToken(self: &NodeInfixOp) Token { - return self.lhs.firstToken(); - } + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - pub fn lastToken(self: &NodeInfixOp) Token { - return self.rhs.lastToken(); - } -}; + if (i < 1) return self.body; + i -= 1; -pub const NodePrefixOp = struct { - base: Node, - op_token: Token, - op: PrefixOp, - rhs: &Node, - - const PrefixOp = union(enum) { - AddrOf: AddrOfInfo, - ArrayType: &Node, - Await, - BitNot, - BoolNot, - Cancel, - Deref, - MaybeType, - Negation, - NegationWrap, - Resume, - SliceType: AddrOfInfo, - Try, - UnwrapMaybe, - }; + if (self.@"else") |@"else"| { + if (i < 1) return &@"else".base; + i -= 1; + } - const AddrOfInfo = struct { - align_expr: ?&Node, - bit_offset_start_token: ?Token, - bit_offset_end_token: ?Token, - const_token: ?Token, - volatile_token: ?Token, - }; + return null; + } - pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node { - var i = index; + pub fn firstToken(self: &If) Token { + return self.if_token; + } - switch (self.op) { - PrefixOp.SliceType => |addr_of_info| { - if (addr_of_info.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; - } - }, - PrefixOp.AddrOf => |addr_of_info| { - if (addr_of_info.align_expr) |align_expr| { - if (i < 1) return align_expr; - i -= 1; - } - }, - PrefixOp.ArrayType => |size_expr| { - if (i < 1) return size_expr; - i -= 1; - }, - PrefixOp.Await, - PrefixOp.BitNot, - PrefixOp.BoolNot, - PrefixOp.Cancel, - PrefixOp.Deref, - PrefixOp.MaybeType, - PrefixOp.Negation, - PrefixOp.NegationWrap, - PrefixOp.Try, - PrefixOp.Resume, - PrefixOp.UnwrapMaybe => {}, - } - - if (i < 1) return self.rhs; - i -= 1; - - return null; - } + pub fn lastToken(self: &If) Token { + if (self.@"else") |@"else"| { + return @"else".body.lastToken(); + } - pub fn firstToken(self: &NodePrefixOp) Token { - return self.op_token; - } + return self.body.lastToken(); + } + }; - pub fn lastToken(self: &NodePrefixOp) Token { - return self.rhs.lastToken(); - } -}; + pub const InfixOp = struct { + base: Node, + op_token: Token, + lhs: &Node, + op: Op, + rhs: &Node, + + pub const Op = union(enum) { + Add, + AddWrap, + ArrayCat, + ArrayMult, + Assign, + AssignBitAnd, + AssignBitOr, + AssignBitShiftLeft, + AssignBitShiftRight, + AssignBitXor, + AssignDiv, + AssignMinus, + AssignMinusWrap, + AssignMod, + AssignPlus, + AssignPlusWrap, + AssignTimes, + AssignTimesWarp, + BangEqual, + BitAnd, + BitOr, + BitShiftLeft, + BitShiftRight, + BitXor, + BoolAnd, + BoolOr, + Catch: ?&Node, + Div, + EqualEqual, + ErrorUnion, + GreaterOrEqual, + GreaterThan, + LessOrEqual, + LessThan, + MergeErrorSets, + Mod, + Mult, + MultWrap, + Period, + Range, + Sub, + SubWrap, + UnwrapMaybe, + }; -pub const NodeFieldInitializer = struct { - base: Node, - period_token: Token, - name_token: Token, - expr: &Node, + pub fn iterate(self: &InfixOp, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeFieldInitializer, index: usize) ?&Node { - var i = index; + if (i < 1) return self.lhs; + i -= 1; - if (i < 1) return self.expr; - i -= 1; + switch (self.op) { + Op.Catch => |maybe_payload| { + if (maybe_payload) |payload| { + if (i < 1) return payload; + i -= 1; + } + }, + + Op.Add, + Op.AddWrap, + Op.ArrayCat, + Op.ArrayMult, + Op.Assign, + Op.AssignBitAnd, + Op.AssignBitOr, + Op.AssignBitShiftLeft, + Op.AssignBitShiftRight, + Op.AssignBitXor, + Op.AssignDiv, + Op.AssignMinus, + Op.AssignMinusWrap, + Op.AssignMod, + Op.AssignPlus, + Op.AssignPlusWrap, + Op.AssignTimes, + Op.AssignTimesWarp, + Op.BangEqual, + Op.BitAnd, + Op.BitOr, + Op.BitShiftLeft, + Op.BitShiftRight, + Op.BitXor, + Op.BoolAnd, + Op.BoolOr, + Op.Div, + Op.EqualEqual, + Op.ErrorUnion, + Op.GreaterOrEqual, + Op.GreaterThan, + Op.LessOrEqual, + Op.LessThan, + Op.MergeErrorSets, + Op.Mod, + Op.Mult, + Op.MultWrap, + Op.Period, + Op.Range, + Op.Sub, + Op.SubWrap, + Op.UnwrapMaybe => {}, + } - return null; - } + if (i < 1) return self.rhs; + i -= 1; - pub fn firstToken(self: &NodeFieldInitializer) Token { - return self.period_token; - } + return null; + } - pub fn lastToken(self: &NodeFieldInitializer) Token { - return self.expr.lastToken(); - } -}; + pub fn firstToken(self: &InfixOp) Token { + return self.lhs.firstToken(); + } -pub const NodeSuffixOp = struct { - base: Node, - lhs: &Node, - op: SuffixOp, - rtoken: Token, - - const SuffixOp = union(enum) { - Call: CallInfo, - ArrayAccess: &Node, - Slice: SliceRange, - ArrayInitializer: ArrayList(&Node), - StructInitializer: ArrayList(&NodeFieldInitializer), + pub fn lastToken(self: &InfixOp) Token { + return self.rhs.lastToken(); + } }; - const CallInfo = struct { - params: ArrayList(&Node), - async_attr: ?&NodeAsyncAttribute, - }; + pub const PrefixOp = struct { + base: Node, + op_token: Token, + op: Op, + rhs: &Node, + + const Op = union(enum) { + AddrOf: AddrOfInfo, + ArrayType: &Node, + Await, + BitNot, + BoolNot, + Cancel, + Deref, + MaybeType, + Negation, + NegationWrap, + Resume, + SliceType: AddrOfInfo, + Try, + UnwrapMaybe, + }; - const SliceRange = struct { - start: &Node, - end: ?&Node, - }; + const AddrOfInfo = struct { + align_expr: ?&Node, + bit_offset_start_token: ?Token, + bit_offset_end_token: ?Token, + const_token: ?Token, + volatile_token: ?Token, + }; - pub fn iterate(self: &NodeSuffixOp, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &PrefixOp, index: usize) ?&Node { + var i = index; + + switch (self.op) { + Op.SliceType => |addr_of_info| { + if (addr_of_info.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } + }, + Op.AddrOf => |addr_of_info| { + if (addr_of_info.align_expr) |align_expr| { + if (i < 1) return align_expr; + i -= 1; + } + }, + Op.ArrayType => |size_expr| { + if (i < 1) return size_expr; + i -= 1; + }, + Op.Await, + Op.BitNot, + Op.BoolNot, + Op.Cancel, + Op.Deref, + Op.MaybeType, + Op.Negation, + Op.NegationWrap, + Op.Try, + Op.Resume, + Op.UnwrapMaybe => {}, + } - if (i < 1) return self.lhs; - i -= 1; + if (i < 1) return self.rhs; + i -= 1; - switch (self.op) { - SuffixOp.Call => |call_info| { - if (i < call_info.params.len) return call_info.params.at(i); - i -= call_info.params.len; - }, - SuffixOp.ArrayAccess => |index_expr| { - if (i < 1) return index_expr; - i -= 1; - }, - SuffixOp.Slice => |range| { - if (i < 1) return range.start; - i -= 1; + return null; + } - if (range.end) |end| { - if (i < 1) return end; - i -= 1; - } - }, - SuffixOp.ArrayInitializer => |exprs| { - if (i < exprs.len) return exprs.at(i); - i -= exprs.len; - }, - SuffixOp.StructInitializer => |fields| { - if (i < fields.len) return &fields.at(i).base; - i -= fields.len; - }, - } - - return null; - } + pub fn firstToken(self: &PrefixOp) Token { + return self.op_token; + } - pub fn firstToken(self: &NodeSuffixOp) Token { - return self.lhs.firstToken(); - } + pub fn lastToken(self: &PrefixOp) Token { + return self.rhs.lastToken(); + } + }; - pub fn lastToken(self: &NodeSuffixOp) Token { - return self.rtoken; - } -}; + pub const FieldInitializer = struct { + base: Node, + period_token: Token, + name_token: Token, + expr: &Node, -pub const NodeGroupedExpression = struct { - base: Node, - lparen: Token, - expr: &Node, - rparen: Token, + pub fn iterate(self: &FieldInitializer, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeGroupedExpression, index: usize) ?&Node { - var i = index; + if (i < 1) return self.expr; + i -= 1; - if (i < 1) return self.expr; - i -= 1; + return null; + } - return null; - } + pub fn firstToken(self: &FieldInitializer) Token { + return self.period_token; + } - pub fn firstToken(self: &NodeGroupedExpression) Token { - return self.lparen; - } + pub fn lastToken(self: &FieldInitializer) Token { + return self.expr.lastToken(); + } + }; - pub fn lastToken(self: &NodeGroupedExpression) Token { - return self.rparen; - } -}; + pub const SuffixOp = struct { + base: Node, + lhs: &Node, + op: Op, + rtoken: Token, + + const Op = union(enum) { + Call: CallInfo, + ArrayAccess: &Node, + Slice: SliceRange, + ArrayInitializer: ArrayList(&Node), + StructInitializer: ArrayList(&FieldInitializer), + }; + + const CallInfo = struct { + params: ArrayList(&Node), + async_attr: ?&AsyncAttribute, + }; -pub const NodeControlFlowExpression = struct { - base: Node, - ltoken: Token, - kind: Kind, - rhs: ?&Node, + const SliceRange = struct { + start: &Node, + end: ?&Node, + }; - const Kind = union(enum) { - Break: ?&Node, - Continue: ?&Node, - Return, - }; + pub fn iterate(self: &SuffixOp, index: usize) ?&Node { + var i = index; - pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node { - var i = index; + if (i < 1) return self.lhs; + i -= 1; - switch (self.kind) { - Kind.Break => |maybe_label| { - if (maybe_label) |label| { - if (i < 1) return label; + switch (self.op) { + Op.Call => |call_info| { + if (i < call_info.params.len) return call_info.params.at(i); + i -= call_info.params.len; + }, + Op.ArrayAccess => |index_expr| { + if (i < 1) return index_expr; i -= 1; - } - }, - Kind.Continue => |maybe_label| { - if (maybe_label) |label| { - if (i < 1) return label; + }, + Op.Slice => |range| { + if (i < 1) return range.start; i -= 1; - } - }, - Kind.Return => {}, + + if (range.end) |end| { + if (i < 1) return end; + i -= 1; + } + }, + Op.ArrayInitializer => |exprs| { + if (i < exprs.len) return exprs.at(i); + i -= exprs.len; + }, + Op.StructInitializer => |fields| { + if (i < fields.len) return &fields.at(i).base; + i -= fields.len; + }, + } + + return null; } - if (self.rhs) |rhs| { - if (i < 1) return rhs; - i -= 1; + pub fn firstToken(self: &SuffixOp) Token { + return self.lhs.firstToken(); } - return null; - } + pub fn lastToken(self: &SuffixOp) Token { + return self.rtoken; + } + }; - pub fn firstToken(self: &NodeControlFlowExpression) Token { - return self.ltoken; - } + pub const GroupedExpression = struct { + base: Node, + lparen: Token, + expr: &Node, + rparen: Token, + + pub fn iterate(self: &GroupedExpression, index: usize) ?&Node { + var i = index; - pub fn lastToken(self: &NodeControlFlowExpression) Token { - if (self.rhs) |rhs| { - return rhs.lastToken(); + if (i < 1) return self.expr; + i -= 1; + + return null; } - switch (self.kind) { - Kind.Break => |maybe_label| { - if (maybe_label) |label| { - return label.lastToken(); - } - }, - Kind.Continue => |maybe_label| { - if (maybe_label) |label| { - return label.lastToken(); - } - }, - Kind.Return => return self.ltoken, + pub fn firstToken(self: &GroupedExpression) Token { + return self.lparen; } - return self.ltoken; - } -}; + pub fn lastToken(self: &GroupedExpression) Token { + return self.rparen; + } + }; -pub const NodeSuspend = struct { - base: Node, - suspend_token: Token, - payload: ?&Node, - body: ?&Node, + pub const ControlFlowExpression = struct { + base: Node, + ltoken: Token, + kind: Kind, + rhs: ?&Node, - pub fn iterate(self: &NodeSuspend, index: usize) ?&Node { - var i = index; + const Kind = union(enum) { + Break: ?&Node, + Continue: ?&Node, + Return, + }; - if (self.payload) |payload| { - if (i < 1) return payload; - i -= 1; + pub fn iterate(self: &ControlFlowExpression, index: usize) ?&Node { + var i = index; + + switch (self.kind) { + Kind.Break => |maybe_label| { + if (maybe_label) |label| { + if (i < 1) return label; + i -= 1; + } + }, + Kind.Continue => |maybe_label| { + if (maybe_label) |label| { + if (i < 1) return label; + i -= 1; + } + }, + Kind.Return => {}, + } + + if (self.rhs) |rhs| { + if (i < 1) return rhs; + i -= 1; + } + + return null; } - if (self.body) |body| { - if (i < 1) return body; - i -= 1; + pub fn firstToken(self: &ControlFlowExpression) Token { + return self.ltoken; } - return null; - } + pub fn lastToken(self: &ControlFlowExpression) Token { + if (self.rhs) |rhs| { + return rhs.lastToken(); + } - pub fn firstToken(self: &NodeSuspend) Token { - return self.suspend_token; - } + switch (self.kind) { + Kind.Break => |maybe_label| { + if (maybe_label) |label| { + return label.lastToken(); + } + }, + Kind.Continue => |maybe_label| { + if (maybe_label) |label| { + return label.lastToken(); + } + }, + Kind.Return => return self.ltoken, + } - pub fn lastToken(self: &NodeSuspend) Token { - if (self.body) |body| { - return body.lastToken(); + return self.ltoken; } + }; + + pub const Suspend = struct { + base: Node, + suspend_token: Token, + payload: ?&Node, + body: ?&Node, + + pub fn iterate(self: &Suspend, index: usize) ?&Node { + var i = index; + + if (self.payload) |payload| { + if (i < 1) return payload; + i -= 1; + } - if (self.payload) |payload| { - return payload.lastToken(); + if (self.body) |body| { + if (i < 1) return body; + i -= 1; + } + + return null; } - return self.suspend_token; - } -}; + pub fn firstToken(self: &Suspend) Token { + return self.suspend_token; + } -pub const NodeIntegerLiteral = struct { - base: Node, - token: Token, + pub fn lastToken(self: &Suspend) Token { + if (self.body) |body| { + return body.lastToken(); + } - pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node { - return null; - } + if (self.payload) |payload| { + return payload.lastToken(); + } - pub fn firstToken(self: &NodeIntegerLiteral) Token { - return self.token; - } + return self.suspend_token; + } + }; - pub fn lastToken(self: &NodeIntegerLiteral) Token { - return self.token; - } -}; + pub const IntegerLiteral = struct { + base: Node, + token: Token, -pub const NodeFloatLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &IntegerLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeFloatLiteral) Token { - return self.token; - } + pub fn lastToken(self: &IntegerLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeFloatLiteral) Token { - return self.token; - } -}; + pub const FloatLiteral = struct { + base: Node, + token: Token, -pub const NodeBuiltinCall = struct { - base: Node, - builtin_token: Token, - params: ArrayList(&Node), - rparen_token: Token, + pub fn iterate(self: &FloatLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node { - var i = index; + pub fn firstToken(self: &FloatLiteral) Token { + return self.token; + } - if (i < self.params.len) return self.params.at(i); - i -= self.params.len; + pub fn lastToken(self: &FloatLiteral) Token { + return self.token; + } + }; - return null; - } + pub const BuiltinCall = struct { + base: Node, + builtin_token: Token, + params: ArrayList(&Node), + rparen_token: Token, - pub fn firstToken(self: &NodeBuiltinCall) Token { - return self.builtin_token; - } + pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { + var i = index; - pub fn lastToken(self: &NodeBuiltinCall) Token { - return self.rparen_token; - } -}; + if (i < self.params.len) return self.params.at(i); + i -= self.params.len; -pub const NodeStringLiteral = struct { - base: Node, - token: Token, + return null; + } - pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &BuiltinCall) Token { + return self.builtin_token; + } - pub fn firstToken(self: &NodeStringLiteral) Token { - return self.token; - } + pub fn lastToken(self: &BuiltinCall) Token { + return self.rparen_token; + } + }; - pub fn lastToken(self: &NodeStringLiteral) Token { - return self.token; - } -}; + pub const StringLiteral = struct { + base: Node, + token: Token, -pub const NodeMultilineStringLiteral = struct { - base: Node, - tokens: ArrayList(Token), + pub fn iterate(self: &StringLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeMultilineStringLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &StringLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeMultilineStringLiteral) Token { - return self.tokens.at(0); - } + pub fn lastToken(self: &StringLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeMultilineStringLiteral) Token { - return self.tokens.at(self.tokens.len - 1); - } -}; + pub const MultilineStringLiteral = struct { + base: Node, + tokens: ArrayList(Token), -pub const NodeCharLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeCharLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &MultilineStringLiteral) Token { + return self.tokens.at(0); + } - pub fn firstToken(self: &NodeCharLiteral) Token { - return self.token; - } + pub fn lastToken(self: &MultilineStringLiteral) Token { + return self.tokens.at(self.tokens.len - 1); + } + }; - pub fn lastToken(self: &NodeCharLiteral) Token { - return self.token; - } -}; + pub const CharLiteral = struct { + base: Node, + token: Token, -pub const NodeBoolLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &CharLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeBoolLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &CharLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeBoolLiteral) Token { - return self.token; - } + pub fn lastToken(self: &CharLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeBoolLiteral) Token { - return self.token; - } -}; + pub const BoolLiteral = struct { + base: Node, + token: Token, -pub const NodeNullLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &BoolLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeNullLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &BoolLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeNullLiteral) Token { - return self.token; - } + pub fn lastToken(self: &BoolLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeNullLiteral) Token { - return self.token; - } -}; + pub const NullLiteral = struct { + base: Node, + token: Token, -pub const NodeUndefinedLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &NullLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeUndefinedLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &NullLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeUndefinedLiteral) Token { - return self.token; - } + pub fn lastToken(self: &NullLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeUndefinedLiteral) Token { - return self.token; - } -}; + pub const UndefinedLiteral = struct { + base: Node, + token: Token, -pub const NodeThisLiteral = struct { - base: Node, - token: Token, + pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node { + return null; + } - pub fn iterate(self: &NodeThisLiteral, index: usize) ?&Node { - return null; - } + pub fn firstToken(self: &UndefinedLiteral) Token { + return self.token; + } - pub fn firstToken(self: &NodeThisLiteral) Token { - return self.token; - } + pub fn lastToken(self: &UndefinedLiteral) Token { + return self.token; + } + }; - pub fn lastToken(self: &NodeThisLiteral) Token { - return self.token; - } -}; + pub const ThisLiteral = struct { + base: Node, + token: Token, -pub const NodeAsmOutput = struct { - base: Node, - symbolic_name: &Node, - constraint: &Node, - kind: Kind, + pub fn iterate(self: &ThisLiteral, index: usize) ?&Node { + return null; + } - const Kind = union(enum) { - Variable: &NodeIdentifier, - Return: &Node + pub fn firstToken(self: &ThisLiteral) Token { + return self.token; + } + + pub fn lastToken(self: &ThisLiteral) Token { + return self.token; + } }; - pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node { - var i = index; + pub const AsmOutput = struct { + base: Node, + symbolic_name: &Node, + constraint: &Node, + kind: Kind, - if (i < 1) return self.symbolic_name; - i -= 1; + const Kind = union(enum) { + Variable: &Identifier, + Return: &Node + }; - if (i < 1) return self.constraint; - i -= 1; + pub fn iterate(self: &AsmOutput, index: usize) ?&Node { + var i = index; - switch (self.kind) { - Kind.Variable => |variable_name| { - if (i < 1) return &variable_name.base; - i -= 1; - }, - Kind.Return => |return_type| { - if (i < 1) return return_type; - i -= 1; + if (i < 1) return self.symbolic_name; + i -= 1; + + if (i < 1) return self.constraint; + i -= 1; + + switch (self.kind) { + Kind.Variable => |variable_name| { + if (i < 1) return &variable_name.base; + i -= 1; + }, + Kind.Return => |return_type| { + if (i < 1) return return_type; + i -= 1; + } } - } - return null; - } + return null; + } - pub fn firstToken(self: &NodeAsmOutput) Token { - return self.symbolic_name.firstToken(); - } + pub fn firstToken(self: &AsmOutput) Token { + return self.symbolic_name.firstToken(); + } - pub fn lastToken(self: &NodeAsmOutput) Token { - return switch (self.kind) { - Kind.Variable => |variable_name| variable_name.lastToken(), - Kind.Return => |return_type| return_type.lastToken(), - }; - } -}; + pub fn lastToken(self: &AsmOutput) Token { + return switch (self.kind) { + Kind.Variable => |variable_name| variable_name.lastToken(), + Kind.Return => |return_type| return_type.lastToken(), + }; + } + }; -pub const NodeAsmInput = struct { - base: Node, - symbolic_name: &Node, - constraint: &Node, - expr: &Node, + pub const AsmInput = struct { + base: Node, + symbolic_name: &Node, + constraint: &Node, + expr: &Node, - pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &AsmInput, index: usize) ?&Node { + var i = index; - if (i < 1) return self.symbolic_name; - i -= 1; + if (i < 1) return self.symbolic_name; + i -= 1; - if (i < 1) return self.constraint; - i -= 1; + if (i < 1) return self.constraint; + i -= 1; - if (i < 1) return self.expr; - i -= 1; + if (i < 1) return self.expr; + i -= 1; - return null; - } + return null; + } - pub fn firstToken(self: &NodeAsmInput) Token { - return self.symbolic_name.firstToken(); - } + pub fn firstToken(self: &AsmInput) Token { + return self.symbolic_name.firstToken(); + } - pub fn lastToken(self: &NodeAsmInput) Token { - return self.expr.lastToken(); - } -}; + pub fn lastToken(self: &AsmInput) Token { + return self.expr.lastToken(); + } + }; -pub const NodeAsm = struct { - base: Node, - asm_token: Token, - volatile_token: ?Token, - template: &Node, - //tokens: ArrayList(AsmToken), - outputs: ArrayList(&NodeAsmOutput), - inputs: ArrayList(&NodeAsmInput), - cloppers: ArrayList(&Node), - rparen: Token, + pub const Asm = struct { + base: Node, + asm_token: Token, + volatile_token: ?Token, + template: &Node, + //tokens: ArrayList(AsmToken), + outputs: ArrayList(&AsmOutput), + inputs: ArrayList(&AsmInput), + cloppers: ArrayList(&Node), + rparen: Token, - pub fn iterate(self: &NodeAsm, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &Asm, index: usize) ?&Node { + var i = index; - if (i < self.outputs.len) return &self.outputs.at(index).base; - i -= self.outputs.len; + if (i < self.outputs.len) return &self.outputs.at(index).base; + i -= self.outputs.len; - if (i < self.inputs.len) return &self.inputs.at(index).base; - i -= self.inputs.len; + if (i < self.inputs.len) return &self.inputs.at(index).base; + i -= self.inputs.len; - if (i < self.cloppers.len) return self.cloppers.at(index); - i -= self.cloppers.len; + if (i < self.cloppers.len) return self.cloppers.at(index); + i -= self.cloppers.len; - return null; - } + return null; + } - pub fn firstToken(self: &NodeAsm) Token { - return self.asm_token; - } + pub fn firstToken(self: &Asm) Token { + return self.asm_token; + } - pub fn lastToken(self: &NodeAsm) Token { - return self.rparen; - } -}; + pub fn lastToken(self: &Asm) Token { + return self.rparen; + } + }; -pub const NodeUnreachable = struct { - base: Node, - token: Token, + pub const Unreachable = struct { + base: Node, + token: Token, - pub fn iterate(self: &NodeUnreachable, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &Unreachable, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeUnreachable) Token { - return self.token; - } + pub fn firstToken(self: &Unreachable) Token { + return self.token; + } - pub fn lastToken(self: &NodeUnreachable) Token { - return self.token; - } -}; + pub fn lastToken(self: &Unreachable) Token { + return self.token; + } + }; -pub const NodeErrorType = struct { - base: Node, - token: Token, + pub const ErrorType = struct { + base: Node, + token: Token, - pub fn iterate(self: &NodeErrorType, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &ErrorType, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeErrorType) Token { - return self.token; - } + pub fn firstToken(self: &ErrorType) Token { + return self.token; + } - pub fn lastToken(self: &NodeErrorType) Token { - return self.token; - } -}; + pub fn lastToken(self: &ErrorType) Token { + return self.token; + } + }; -pub const NodeVarType = struct { - base: Node, - token: Token, + pub const VarType = struct { + base: Node, + token: Token, - pub fn iterate(self: &NodeVarType, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &VarType, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeVarType) Token { - return self.token; - } + pub fn firstToken(self: &VarType) Token { + return self.token; + } - pub fn lastToken(self: &NodeVarType) Token { - return self.token; - } -}; + pub fn lastToken(self: &VarType) Token { + return self.token; + } + }; -pub const NodeLineComment = struct { - base: Node, - lines: ArrayList(Token), + pub const LineComment = struct { + base: Node, + lines: ArrayList(Token), - pub fn iterate(self: &NodeLineComment, index: usize) ?&Node { - return null; - } + pub fn iterate(self: &LineComment, index: usize) ?&Node { + return null; + } - pub fn firstToken(self: &NodeLineComment) Token { - return self.lines.at(0); - } + pub fn firstToken(self: &LineComment) Token { + return self.lines.at(0); + } - pub fn lastToken(self: &NodeLineComment) Token { - return self.lines.at(self.lines.len - 1); - } -}; + pub fn lastToken(self: &LineComment) Token { + return self.lines.at(self.lines.len - 1); + } + }; -pub const NodeTestDecl = struct { - base: Node, - test_token: Token, - name: &Node, - body_node: &Node, + pub const TestDecl = struct { + base: Node, + comments: ?&LineComment, + test_token: Token, + name: &Node, + body_node: &Node, - pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node { - var i = index; + pub fn iterate(self: &TestDecl, index: usize) ?&Node { + var i = index; - if (i < 1) return self.body_node; - i -= 1; + if (i < 1) return self.body_node; + i -= 1; - return null; - } + return null; + } - pub fn firstToken(self: &NodeTestDecl) Token { - return self.test_token; - } + pub fn firstToken(self: &TestDecl) Token { + return self.test_token; + } - pub fn lastToken(self: &NodeTestDecl) Token { - return self.body_node.lastToken(); - } + pub fn lastToken(self: &TestDecl) Token { + return self.body_node.lastToken(); + } + }; }; + diff --git a/std/zig/parser.zig b/std/zig/parser.zig index c0708581ea..017d293491 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -18,10 +18,10 @@ pub const Parser = struct { put_back_tokens: [2]Token, put_back_count: usize, source_file_name: []const u8, - pending_line_comment_node: ?&ast.NodeLineComment, + pending_line_comment_node: ?&ast.Node.LineComment, pub const Tree = struct { - root_node: &ast.NodeRoot, + root_node: &ast.Node.Root, arena_allocator: std.heap.ArenaAllocator, pub fn deinit(self: &Tree) void { @@ -66,22 +66,24 @@ pub const Parser = struct { extern_export_token: ?Token, lib_name: ?&ast.Node, list: &ArrayList(&ast.Node), + comments: ?&ast.Node.LineComment, }; const TopLevelExternOrFieldCtx = struct { visib_token: Token, - container_decl: &ast.NodeContainerDecl, + container_decl: &ast.Node.ContainerDecl, }; const ExternTypeCtx = struct { opt_ctx: OptionalCtx, extern_token: Token, + comments: ?&ast.Node.LineComment, }; const ContainerKindCtx = struct { opt_ctx: OptionalCtx, ltoken: Token, - layout: ast.NodeContainerDecl.Layout, + layout: ast.Node.ContainerDecl.Layout, }; const ExpectTokenSave = struct { @@ -132,7 +134,7 @@ pub const Parser = struct { const AsyncEndCtx = struct { ctx: OptionalCtx, - attribute: &ast.NodeAsyncAttribute, + attribute: &ast.Node.AsyncAttribute, }; const ErrorTypeOrSetDeclCtx = struct { @@ -141,13 +143,13 @@ pub const Parser = struct { }; const ParamDeclEndCtx = struct { - fn_proto: &ast.NodeFnProto, - param_decl: &ast.NodeParamDecl, + fn_proto: &ast.Node.FnProto, + param_decl: &ast.Node.ParamDecl, }; const ComptimeStatementCtx = struct { comptime_token: Token, - block: &ast.NodeBlock, + block: &ast.Node.Block, }; const OptionalCtx = union(enum) { @@ -190,24 +192,24 @@ pub const Parser = struct { TopLevelExternOrField: TopLevelExternOrFieldCtx, ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.NodeContainerDecl, - ContainerInitArg: &ast.NodeContainerDecl, - ContainerDecl: &ast.NodeContainerDecl, + ContainerInitArgStart: &ast.Node.ContainerDecl, + ContainerInitArg: &ast.Node.ContainerDecl, + ContainerDecl: &ast.Node.ContainerDecl, VarDecl: VarDeclCtx, - VarDeclAlign: &ast.NodeVarDecl, - VarDeclEq: &ast.NodeVarDecl, + VarDeclAlign: &ast.Node.VarDecl, + VarDeclEq: &ast.Node.VarDecl, - FnDef: &ast.NodeFnProto, - FnProto: &ast.NodeFnProto, - FnProtoAlign: &ast.NodeFnProto, - FnProtoReturnType: &ast.NodeFnProto, + FnDef: &ast.Node.FnProto, + FnProto: &ast.Node.FnProto, + FnProtoAlign: &ast.Node.FnProto, + FnProtoReturnType: &ast.Node.FnProto, - ParamDecl: &ast.NodeFnProto, - ParamDeclAliasOrComptime: &ast.NodeParamDecl, - ParamDeclName: &ast.NodeParamDecl, + ParamDecl: &ast.Node.FnProto, + ParamDeclAliasOrComptime: &ast.Node.ParamDecl, + ParamDeclName: &ast.Node.ParamDecl, ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.NodeFnProto, + ParamDeclComma: &ast.Node.FnProto, MaybeLabeledExpression: MaybeLabeledExpressionCtx, LabeledExpression: LabelCtx, @@ -215,39 +217,39 @@ pub const Parser = struct { While: LoopCtx, WhileContinueExpr: &?&ast.Node, For: LoopCtx, - Else: &?&ast.NodeElse, + Else: &?&ast.Node.Else, - Block: &ast.NodeBlock, - Statement: &ast.NodeBlock, + Block: &ast.Node.Block, + Statement: &ast.Node.Block, ComptimeStatement: ComptimeStatementCtx, Semicolon: &&ast.Node, - AsmOutputItems: &ArrayList(&ast.NodeAsmOutput), - AsmOutputReturnOrType: &ast.NodeAsmOutput, - AsmInputItems: &ArrayList(&ast.NodeAsmInput), + AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), + AsmOutputReturnOrType: &ast.Node.AsmOutput, + AsmInputItems: &ArrayList(&ast.Node.AsmInput), AsmClopperItems: &ArrayList(&ast.Node), ExprListItemOrEnd: ExprListCtx, ExprListCommaOrEnd: ExprListCtx, - FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer), - FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer), - FieldListCommaOrEnd: &ast.NodeContainerDecl, + FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer), + FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer), + FieldListCommaOrEnd: &ast.Node.ContainerDecl, IdentifierListItemOrEnd: ListSave(&ast.Node), IdentifierListCommaOrEnd: ListSave(&ast.Node), - SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase), - SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase), + SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase), + SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase), SwitchCaseFirstItem: &ArrayList(&ast.Node), SwitchCaseItem: &ArrayList(&ast.Node), SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), - SuspendBody: &ast.NodeSuspend, - AsyncAllocator: &ast.NodeAsyncAttribute, + SuspendBody: &ast.Node.Suspend, + AsyncAllocator: &ast.Node.AsyncAttribute, AsyncEnd: AsyncEndCtx, ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.NodeSuffixOp, - SliceOrArrayType: &ast.NodePrefixOp, - AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo, + SliceOrArrayAccess: &ast.Node.SuffixOp, + SliceOrArrayType: &ast.Node.PrefixOp, + AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, Payload: OptionalCtx, PointerPayload: OptionalCtx, @@ -310,8 +312,8 @@ pub const Parser = struct { errdefer arena_allocator.deinit(); const arena = &arena_allocator.allocator; - const root_node = try self.createNode(arena, ast.NodeRoot, - ast.NodeRoot { + const root_node = try self.createNode(arena, ast.Node.Root, + ast.Node.Root { .base = undefined, .decls = ArrayList(&ast.Node).init(arena), // initialized when we get the eof token @@ -334,43 +336,19 @@ pub const Parser = struct { // warn("\n"); //} - // look for line comments - while (true) { - if (self.eatToken(Token.Id.LineComment)) |line_comment| { - const node = blk: { - if (self.pending_line_comment_node) |comment_node| { - break :blk comment_node; - } else { - const comment_node = try arena.create(ast.NodeLineComment); - *comment_node = ast.NodeLineComment { - .base = ast.Node { - .id = ast.Node.Id.LineComment, - .comment = null, - }, - .lines = ArrayList(Token).init(arena), - }; - self.pending_line_comment_node = comment_node; - break :blk comment_node; - } - }; - try node.lines.append(line_comment); - continue; - } - break; - } - // This gives us 1 free append that can't fail const state = stack.pop(); switch (state) { State.TopLevel => { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try self.createNode(arena, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createNode(arena, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = undefined, @@ -378,9 +356,10 @@ pub const Parser = struct { .rbrace = undefined, } ); - const test_node = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl, - ast.NodeTestDecl { + const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl, + ast.Node.TestDecl { .base = undefined, + .comments = comments, .test_token = token, .name = undefined, .body_node = &block.base, @@ -413,8 +392,8 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_comptime => { - const block = try self.createNode(arena, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createNode(arena, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = undefined, @@ -422,8 +401,8 @@ pub const Parser = struct { .rbrace = undefined, } ); - const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime, - ast.NodeComptime { + const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime, + ast.Node.Comptime { .base = undefined, .comptime_token = token, .expr = &block.base, @@ -506,6 +485,7 @@ pub const Parser = struct { continue; }, State.TopLevelDecl => |ctx| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_use => { @@ -513,8 +493,8 @@ pub const Parser = struct { return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id)); } - const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse, - ast.NodeUse { + const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use, + ast.Node.Use { .base = undefined, .visib_token = ctx.visib_token, .expr = undefined, @@ -539,6 +519,7 @@ pub const Parser = struct { stack.append(State { .VarDecl = VarDeclCtx { + .comments = comments, .visib_token = ctx.visib_token, .lib_name = ctx.lib_name, .comptime_token = null, @@ -551,9 +532,10 @@ pub const Parser = struct { }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = comments, .visib_token = ctx.visib_token, .name_token = null, .fn_token = undefined, @@ -583,8 +565,8 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_async => { - const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, - ast.NodeAsyncAttribute { + const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { .base = undefined, .async_token = token, .allocator_type = null, @@ -616,9 +598,9 @@ pub const Parser = struct { }, State.TopLevelExternOrField => |ctx| { if (self.eatToken(Token.Id.Identifier)) |identifier| { - std.debug.assert(ctx.container_decl.kind == ast.NodeContainerDecl.Kind.Struct); - const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.NodeStructField, - ast.NodeStructField { + std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); + const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField, + ast.Node.StructField { .base = undefined, .visib_token = ctx.visib_token, .name_token = identifier, @@ -647,15 +629,15 @@ pub const Parser = struct { State.ContainerKind => |ctx| { const token = self.getNextToken(); - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl, - ast.NodeContainerDecl { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, + ast.Node.ContainerDecl { .base = undefined, .ltoken = ctx.ltoken, .layout = ctx.layout, .kind = switch (token.id) { - Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct, - Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union, - Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum, + Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, + Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, + Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, else => { return self.parseError(token, "expected {}, {} or {}, found {}", @tagName(Token.Id.Keyword_struct), @@ -664,7 +646,7 @@ pub const Parser = struct { @tagName(token.id)); }, }, - .init_arg_expr = ast.NodeContainerDecl.InitArg.None, + .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, .fields_and_decls = ArrayList(&ast.Node).init(arena), .rbrace_token = undefined, } @@ -690,11 +672,11 @@ pub const Parser = struct { const init_arg_token = self.getNextToken(); switch (init_arg_token.id) { Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum; }, else => { self.putBackToken(init_arg_token); - container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined }; + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; }, } @@ -705,9 +687,9 @@ pub const Parser = struct { switch (token.id) { Token.Id.Identifier => { switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField, - ast.NodeStructField { + ast.Node.ContainerDecl.Kind.Struct => { + const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField, + ast.Node.StructField { .base = undefined, .visib_token = null, .name_token = token, @@ -720,9 +702,9 @@ pub const Parser = struct { try stack.append(State { .ExpectToken = Token.Id.Colon }); continue; }, - ast.NodeContainerDecl.Kind.Union => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeUnionTag, - ast.NodeUnionTag { + ast.Node.ContainerDecl.Kind.Union => { + const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag, + ast.Node.UnionTag { .base = undefined, .name_token = token, .type_expr = null, @@ -734,9 +716,9 @@ pub const Parser = struct { try stack.append(State { .IfToken = Token.Id.Colon }); continue; }, - ast.NodeContainerDecl.Kind.Enum => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeEnumTag, - ast.NodeEnumTag { + ast.Node.ContainerDecl.Kind.Enum => { + const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag, + ast.Node.EnumTag { .base = undefined, .name_token = token, .value = null, @@ -752,7 +734,7 @@ pub const Parser = struct { }, Token.Id.Keyword_pub => { switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => { + ast.Node.ContainerDecl.Kind.Struct => { try stack.append(State { .TopLevelExternOrField = TopLevelExternOrFieldCtx { .visib_token = token, @@ -809,9 +791,10 @@ pub const Parser = struct { State.VarDecl => |ctx| { - const var_decl = try self.createAttachNode(arena, ctx.list, ast.NodeVarDecl, - ast.NodeVarDecl { + const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl, + ast.Node.VarDecl { .base = undefined, + .comments = ctx.comments, .visib_token = ctx.visib_token, .mut_token = ctx.mut_token, .comptime_token = ctx.comptime_token, @@ -881,8 +864,8 @@ pub const Parser = struct { const token = self.getNextToken(); switch(token.id) { Token.Id.LBrace => { - const block = try self.createNode(arena, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createNode(arena, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = token, @@ -924,7 +907,7 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.Bang => { - fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined }; + fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, }) catch unreachable; @@ -934,15 +917,15 @@ pub const Parser = struct { // TODO: this is a special case. Remove this when #760 is fixed if (token.id == Token.Id.Keyword_error) { if (self.isPeekToken(Token.Id.LBrace)) { - fn_proto.return_type = ast.NodeFnProto.ReturnType { - .Explicit = &(try self.createLiteral(arena, ast.NodeErrorType, token)).base + fn_proto.return_type = ast.Node.FnProto.ReturnType { + .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base }; continue; } } self.putBackToken(token); - fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined }; + fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; }, @@ -954,8 +937,8 @@ pub const Parser = struct { if (self.eatToken(Token.Id.RParen)) |_| { continue; } - const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl, - ast.NodeParamDecl { + const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl, + ast.Node.ParamDecl { .base = undefined, .comptime_token = null, .noalias_token = null, @@ -1026,15 +1009,15 @@ pub const Parser = struct { continue; } - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeIdentifier, ctx.label); + _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); continue; }, State.LabeledExpression => |ctx| { const token = self.getNextToken(); switch (token.id) { Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = ctx.label, .lbrace = token, @@ -1123,8 +1106,8 @@ pub const Parser = struct { } }, State.While => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeWhile, - ast.NodeWhile { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, + ast.Node.While { .base = undefined, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1153,8 +1136,8 @@ pub const Parser = struct { continue; }, State.For => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFor, - ast.NodeFor { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, + ast.Node.For { .base = undefined, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1175,8 +1158,8 @@ pub const Parser = struct { }, State.Else => |dest| { if (self.eatToken(Token.Id.Keyword_else)) |else_token| { - const node = try self.createNode(arena, ast.NodeElse, - ast.NodeElse { + const node = try self.createNode(arena, ast.Node.Else, + ast.Node.Else { .base = undefined, .else_token = else_token, .payload = null, @@ -1210,6 +1193,7 @@ pub const Parser = struct { } }, State.Statement => |block| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_comptime => { @@ -1224,6 +1208,7 @@ pub const Parser = struct { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.append(State { .VarDecl = VarDeclCtx { + .comments = comments, .visib_token = null, .comptime_token = null, .extern_export_token = null, @@ -1235,13 +1220,13 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer, - ast.NodeDefer { + const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer, + ast.Node.Defer { .base = undefined, .defer_token = token, .kind = switch (token.id) { - Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error, + Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, + Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, else => unreachable, }, .expr = undefined, @@ -1252,8 +1237,8 @@ pub const Parser = struct { continue; }, Token.Id.LBrace => { - const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock, - ast.NodeBlock { + const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = token, @@ -1274,11 +1259,13 @@ pub const Parser = struct { } }, State.ComptimeStatement => |ctx| { + const comments = try self.eatComments(arena); const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_var, Token.Id.Keyword_const => { stack.append(State { .VarDecl = VarDeclCtx { + .comments = comments, .visib_token = null, .comptime_token = ctx.comptime_token, .extern_export_token = null, @@ -1316,8 +1303,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeAsmOutput, - ast.NodeAsmOutput { + const node = try self.createNode(arena, ast.Node.AsmOutput, + ast.Node.AsmOutput { .base = undefined, .symbolic_name = undefined, .constraint = undefined, @@ -1340,11 +1327,11 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.Identifier => { - node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, token) }; + node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) }; continue; }, Token.Id.Arrow => { - node.kind = ast.NodeAsmOutput.Kind { .Return = undefined }; + node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); continue; }, @@ -1362,8 +1349,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeAsmInput, - ast.NodeAsmInput { + const node = try self.createNode(arena, ast.Node.AsmInput, + ast.Node.AsmInput { .base = undefined, .symbolic_name = undefined, .constraint = undefined, @@ -1415,8 +1402,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeFieldInitializer, - ast.NodeFieldInitializer { + const node = try self.createNode(arena, ast.Node.FieldInitializer, + ast.Node.FieldInitializer { .base = undefined, .period_token = undefined, .name_token = undefined, @@ -1485,8 +1472,8 @@ pub const Parser = struct { continue; } - const node = try self.createNode(arena, ast.NodeSwitchCase, - ast.NodeSwitchCase { + const node = try self.createNode(arena, ast.Node.SwitchCase, + ast.Node.SwitchCase { .base = undefined, .items = ArrayList(&ast.Node).init(arena), .payload = null, @@ -1512,8 +1499,8 @@ pub const Parser = struct { State.SwitchCaseFirstItem => |case_items| { const token = self.getNextToken(); if (token.id == Token.Id.Keyword_else) { - const else_node = try self.createAttachNode(arena, case_items, ast.NodeSwitchElse, - ast.NodeSwitchElse { + const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse, + ast.Node.SwitchElse { .base = undefined, .token = token, } @@ -1564,24 +1551,24 @@ pub const Parser = struct { switch (node.id) { ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node); + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); fn_proto.async_attr = ctx.attribute; continue; }, ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node); - if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); + if (suffix_op.op == ast.Node.SuffixOp.Op.Call) { suffix_op.op.Call.async_attr = ctx.attribute; continue; } return self.parseError(node.firstToken(), "expected {}, found {}.", - @tagName(ast.NodeSuffixOp.SuffixOp.Call), + @tagName(ast.Node.SuffixOp.Op.Call), @tagName(suffix_op.op)); }, else => { return self.parseError(node.firstToken(), "expected {} or {}, found {}.", - @tagName(ast.NodeSuffixOp.SuffixOp.Call), + @tagName(ast.Node.SuffixOp.Op.Call), @tagName(ast.Node.Id.FnProto), @tagName(node.id)); } @@ -1591,9 +1578,10 @@ pub const Parser = struct { State.ExternType => |ctx| { if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = ctx.comments, .visib_token = null, .name_token = null, .fn_token = fn_token, @@ -1616,7 +1604,7 @@ pub const Parser = struct { .ContainerKind = ContainerKindCtx { .opt_ctx = ctx.opt_ctx, .ltoken = ctx.extern_token, - .layout = ast.NodeContainerDecl.Layout.Extern, + .layout = ast.Node.ContainerDecl.Layout.Extern, }, }) catch unreachable; continue; @@ -1626,8 +1614,8 @@ pub const Parser = struct { switch (token.id) { Token.Id.Ellipsis2 => { const start = node.op.ArrayAccess; - node.op = ast.NodeSuffixOp.SuffixOp { - .Slice = ast.NodeSuffixOp.SliceRange { + node.op = ast.Node.SuffixOp.Op { + .Slice = ast.Node.SuffixOp.SliceRange { .start = start, .end = null, } @@ -1653,8 +1641,8 @@ pub const Parser = struct { }, State.SliceOrArrayType => |node| { if (self.eatToken(Token.Id.RBracket)) |_| { - node.op = ast.NodePrefixOp.PrefixOp { - .SliceType = ast.NodePrefixOp.AddrOfInfo { + node.op = ast.Node.PrefixOp.Op { + .SliceType = ast.Node.PrefixOp.AddrOfInfo { .align_expr = null, .bit_offset_start_token = null, .bit_offset_end_token = null, @@ -1667,7 +1655,7 @@ pub const Parser = struct { continue; } - node.op = ast.NodePrefixOp.PrefixOp { .ArrayType = undefined }; + node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; try stack.append(State { .ExpectToken = Token.Id.RBracket }); try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); @@ -1723,8 +1711,8 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePayload, - ast.NodePayload { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload, + ast.Node.Payload { .base = undefined, .lpipe = token, .error_symbol = undefined, @@ -1754,8 +1742,8 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerPayload, - ast.NodePointerPayload { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, + ast.Node.PointerPayload { .base = undefined, .lpipe = token, .ptr_token = null, @@ -1792,8 +1780,8 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerIndexPayload, - ast.NodePointerIndexPayload { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, + ast.Node.PointerIndexPayload { .base = undefined, .lpipe = token, .ptr_token = null, @@ -1826,8 +1814,8 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression, - ast.NodeControlFlowExpression { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, + ast.Node.ControlFlowExpression { .base = undefined, .ltoken = token, .kind = undefined, @@ -1839,31 +1827,31 @@ pub const Parser = struct { switch (token.id) { Token.Id.Keyword_break => { - node.kind = ast.NodeControlFlowExpression.Kind { .Break = null }; + node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); try stack.append(State { .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_continue => { - node.kind = ast.NodeControlFlowExpression.Kind { .Continue = null }; + node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); try stack.append(State { .IfToken = Token.Id.Colon }); }, Token.Id.Keyword_return => { - node.kind = ast.NodeControlFlowExpression.Kind.Return; + node.kind = ast.Node.ControlFlowExpression.Kind.Return; }, else => unreachable, } continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, - ast.NodePrefixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = switch (token.id) { - Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} }, - Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, else => unreachable, }, .rhs = undefined, @@ -1891,12 +1879,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = ellipsis3, - .op = ast.NodeInfixOp.InfixOp.Range, + .op = ast.Node.InfixOp.Op.Range, .rhs = undefined, } ); @@ -1915,8 +1903,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToAssignment(token.id)) |ass_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -1944,8 +1932,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -1957,7 +1945,7 @@ pub const Parser = struct { stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - if (node.op == ast.NodeInfixOp.InfixOp.Catch) { + if (node.op == ast.Node.InfixOp.Op.Catch) { try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); } continue; @@ -1977,12 +1965,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Keyword_or)) |or_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = or_token, - .op = ast.NodeInfixOp.InfixOp.BoolOr, + .op = ast.Node.InfixOp.Op.BoolOr, .rhs = undefined, } ); @@ -2002,12 +1990,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Keyword_and)) |and_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = and_token, - .op = ast.NodeInfixOp.InfixOp.BoolAnd, + .op = ast.Node.InfixOp.Op.BoolAnd, .rhs = undefined, } ); @@ -2028,8 +2016,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToComparison(token.id)) |comp_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2056,12 +2044,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Pipe)) |pipe| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = pipe, - .op = ast.NodeInfixOp.InfixOp.BitOr, + .op = ast.Node.InfixOp.Op.BitOr, .rhs = undefined, } ); @@ -2081,12 +2069,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Caret)) |caret| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = caret, - .op = ast.NodeInfixOp.InfixOp.BitXor, + .op = ast.Node.InfixOp.Op.BitXor, .rhs = undefined, } ); @@ -2106,12 +2094,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Ampersand)) |ampersand| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = ampersand, - .op = ast.NodeInfixOp.InfixOp.BitAnd, + .op = ast.Node.InfixOp.Op.BitAnd, .rhs = undefined, } ); @@ -2132,8 +2120,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToBitShift(token.id)) |bitshift_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2161,8 +2149,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToAddition(token.id)) |add_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2190,8 +2178,8 @@ pub const Parser = struct { const token = self.getNextToken(); if (tokenIdToMultiply(token.id)) |mult_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, @@ -2219,12 +2207,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.isPeekToken(Token.Id.Period)) { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { - .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena), + .op = ast.Node.SuffixOp.Op { + .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena), }, .rtoken = undefined, } @@ -2232,7 +2220,7 @@ pub const Parser = struct { stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State { .IfToken = Token.Id.LBrace }); try stack.append(State { - .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) { + .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) { .list = &node.op.StructInitializer, .ptr = &node.rtoken, } @@ -2240,11 +2228,11 @@ pub const Parser = struct { continue; } - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { + .op = ast.Node.SuffixOp.Op { .ArrayInitializer = ArrayList(&ast.Node).init(arena), }, .rtoken = undefined, @@ -2272,12 +2260,12 @@ pub const Parser = struct { const lhs = opt_ctx.get() ?? continue; if (self.eatToken(Token.Id.Bang)) |bang| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = bang, - .op = ast.NodeInfixOp.InfixOp.ErrorUnion, + .op = ast.Node.InfixOp.Op.ErrorUnion, .rhs = undefined, } ); @@ -2290,8 +2278,8 @@ pub const Parser = struct { State.PrefixOpExpression => |opt_ctx| { const token = self.getNextToken(); if (tokenIdToPrefixOp(token.id)) |prefix_id| { - var node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, - ast.NodePrefixOp { + var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = prefix_id, @@ -2301,8 +2289,8 @@ pub const Parser = struct { // Treat '**' token as two derefs if (token.id == Token.Id.AsteriskAsterisk) { - const child = try self.createNode(arena, ast.NodePrefixOp, - ast.NodePrefixOp { + const child = try self.createNode(arena, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = prefix_id, @@ -2314,7 +2302,7 @@ pub const Parser = struct { } stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) { + if (node.op == ast.Node.PrefixOp.Op.AddrOf) { try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); } continue; @@ -2327,8 +2315,8 @@ pub const Parser = struct { State.SuffixOpExpressionBegin => |opt_ctx| { if (self.eatToken(Token.Id.Keyword_async)) |async_token| { - const async_node = try self.createNode(arena, ast.NodeAsyncAttribute, - ast.NodeAsyncAttribute { + const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { .base = undefined, .async_token = async_token, .allocator_type = null, @@ -2358,12 +2346,12 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { - .Call = ast.NodeSuffixOp.CallInfo { + .op = ast.Node.SuffixOp.Op { + .Call = ast.Node.SuffixOp.CallInfo { .params = ArrayList(&ast.Node).init(arena), .async_attr = null, } @@ -2382,11 +2370,11 @@ pub const Parser = struct { continue; }, Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp, - ast.NodeSuffixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { .base = undefined, .lhs = lhs, - .op = ast.NodeSuffixOp.SuffixOp { + .op = ast.Node.SuffixOp.Op { .ArrayAccess = undefined, }, .rtoken = undefined @@ -2398,12 +2386,12 @@ pub const Parser = struct { continue; }, Token.Id.Period => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp, - ast.NodeInfixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, .op_token = token, - .op = ast.NodeInfixOp.InfixOp.Period, + .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, } ); @@ -2422,39 +2410,39 @@ pub const Parser = struct { const token = self.getNextToken(); switch (token.id) { Token.Id.IntegerLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeStringLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token); continue; }, Token.Id.FloatLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeFloatLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token); continue; }, Token.Id.CharLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeCharLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token); continue; }, Token.Id.Keyword_undefined => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUndefinedLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token); continue; }, Token.Id.Keyword_true, Token.Id.Keyword_false => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeBoolLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token); continue; }, Token.Id.Keyword_null => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeNullLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token); continue; }, Token.Id.Keyword_this => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeThisLiteral, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token); continue; }, Token.Id.Keyword_var => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeVarType, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token); continue; }, Token.Id.Keyword_unreachable => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUnreachable, token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token); continue; }, Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { @@ -2462,8 +2450,8 @@ pub const Parser = struct { continue; }, Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression, - ast.NodeGroupedExpression { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, + ast.Node.GroupedExpression { .base = undefined, .lparen = token, .expr = undefined, @@ -2480,8 +2468,8 @@ pub const Parser = struct { continue; }, Token.Id.Builtin => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeBuiltinCall, - ast.NodeBuiltinCall { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, + ast.Node.BuiltinCall { .base = undefined, .builtin_token = token, .params = ArrayList(&ast.Node).init(arena), @@ -2499,8 +2487,8 @@ pub const Parser = struct { continue; }, Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp, - ast.NodePrefixOp { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, .op_token = token, .op = undefined, @@ -2524,7 +2512,7 @@ pub const Parser = struct { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Packed, + .layout = ast.Node.ContainerDecl.Layout.Packed, }, }) catch unreachable; continue; @@ -2534,6 +2522,7 @@ pub const Parser = struct { .ExternType = ExternTypeCtx { .opt_ctx = opt_ctx, .extern_token = token, + .comments = null, }, }) catch unreachable; continue; @@ -2544,7 +2533,7 @@ pub const Parser = struct { .ContainerKind = ContainerKindCtx { .opt_ctx = opt_ctx, .ltoken = token, - .layout = ast.NodeContainerDecl.Layout.Auto, + .layout = ast.Node.ContainerDecl.Layout.Auto, }, }) catch unreachable; continue; @@ -2559,9 +2548,10 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = null, .visib_token = null, .name_token = null, .fn_token = token, @@ -2580,9 +2570,10 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto, - ast.NodeFnProto { + const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, + ast.Node.FnProto { .base = undefined, + .comments = null, .visib_token = null, .name_token = null, .fn_token = undefined, @@ -2607,15 +2598,15 @@ pub const Parser = struct { continue; }, Token.Id.Keyword_asm => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeAsm, - ast.NodeAsm { + const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm, + ast.Node.Asm { .base = undefined, .asm_token = token, .volatile_token = null, .template = undefined, - //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena), - .outputs = ArrayList(&ast.NodeAsmOutput).init(arena), - .inputs = ArrayList(&ast.NodeAsmInput).init(arena), + //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena), + .outputs = ArrayList(&ast.Node.AsmOutput).init(arena), + .inputs = ArrayList(&ast.Node.AsmInput).init(arena), .cloppers = ArrayList(&ast.Node).init(arena), .rparen = undefined, } @@ -2666,12 +2657,12 @@ pub const Parser = struct { State.ErrorTypeOrSetDecl => |ctx| { if (self.eatToken(Token.Id.LBrace) == null) { - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeErrorType, ctx.error_token); + _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); continue; } - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeErrorSetDecl, - ast.NodeErrorSetDecl { + const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl, + ast.Node.ErrorSetDecl { .base = undefined, .error_token = ctx.error_token, .decls = ArrayList(&ast.Node).init(arena), @@ -2702,7 +2693,7 @@ pub const Parser = struct { }, State.Identifier => |opt_ctx| { if (self.eatToken(Token.Id.Identifier)) |ident_token| { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeIdentifier, ident_token); + _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); continue; } @@ -2750,6 +2741,33 @@ pub const Parser = struct { } } + fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { + var result: ?&ast.Node.LineComment = null; + while (true) { + if (self.eatToken(Token.Id.LineComment)) |line_comment| { + const node = blk: { + if (result) |comment_node| { + break :blk comment_node; + } else { + const comment_node = try arena.create(ast.Node.LineComment); + *comment_node = ast.Node.LineComment { + .base = ast.Node { + .id = ast.Node.Id.LineComment, + }, + .lines = ArrayList(Token).init(arena), + }; + result = comment_node; + break :blk comment_node; + } + }; + try node.lines.append(line_comment); + continue; + } + break; + } + return result; + } + fn requireSemiColon(node: &const ast.Node) bool { var n = node; while (true) { @@ -2770,7 +2788,7 @@ pub const Parser = struct { ast.Node.Id.LineComment, ast.Node.Id.TestDecl => return false, ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.NodeWhile, "base", n); + const while_node = @fieldParentPtr(ast.Node.While, "base", n); if (while_node.@"else") |@"else"| { n = @"else".base; continue; @@ -2779,7 +2797,7 @@ pub const Parser = struct { return while_node.body.id != ast.Node.Id.Block; }, ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.NodeFor, "base", n); + const for_node = @fieldParentPtr(ast.Node.For, "base", n); if (for_node.@"else") |@"else"| { n = @"else".base; continue; @@ -2788,7 +2806,7 @@ pub const Parser = struct { return for_node.body.id != ast.Node.Id.Block; }, ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.NodeIf, "base", n); + const if_node = @fieldParentPtr(ast.Node.If, "base", n); if (if_node.@"else") |@"else"| { n = @"else".base; continue; @@ -2797,20 +2815,20 @@ pub const Parser = struct { return if_node.body.id != ast.Node.Id.Block; }, ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.NodeElse, "base", n); + const else_node = @fieldParentPtr(ast.Node.Else, "base", n); n = else_node.body; continue; }, ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n); + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); return defer_node.expr.id != ast.Node.Id.Block; }, ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n); + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); return comptime_node.expr.id != ast.Node.Id.Block; }, ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n); + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); if (suspend_node.body) |body| { return body.id != ast.Node.Id.Block; } @@ -2825,11 +2843,11 @@ pub const Parser = struct { fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node { switch (token.id) { Token.Id.StringLiteral => { - return &(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base; + return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try self.createNode(arena, ast.NodeMultilineStringLiteral, - ast.NodeMultilineStringLiteral { + const node = try self.createNode(arena, ast.Node.MultilineStringLiteral, + ast.Node.MultilineStringLiteral { .base = undefined, .tokens = ArrayList(Token).init(arena), } @@ -2856,8 +2874,8 @@ pub const Parser = struct { fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool { switch (token.id) { Token.Id.Keyword_suspend => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeSuspend, - ast.NodeSuspend { + const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend, + ast.Node.Suspend { .base = undefined, .suspend_token = *token, .payload = null, @@ -2870,8 +2888,8 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_if => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeIf, - ast.NodeIf { + const node = try self.createToCtxNode(arena, ctx, ast.Node.If, + ast.Node.If { .base = undefined, .if_token = *token, .condition = undefined, @@ -2912,18 +2930,18 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_switch => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeSwitch, - ast.NodeSwitch { + const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch, + ast.Node.Switch { .base = undefined, .switch_token = *token, .expr = undefined, - .cases = ArrayList(&ast.NodeSwitchCase).init(arena), + .cases = ArrayList(&ast.Node.SwitchCase).init(arena), .rbrace = undefined, } ); stack.append(State { - .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) { + .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) { .list = &node.cases, .ptr = &node.rbrace, }, @@ -2935,8 +2953,8 @@ pub const Parser = struct { return true; }, Token.Id.Keyword_comptime => { - const node = try self.createToCtxNode(arena, ctx, ast.NodeComptime, - ast.NodeComptime { + const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime, + ast.Node.Comptime { .base = undefined, .comptime_token = *token, .expr = undefined, @@ -2946,8 +2964,8 @@ pub const Parser = struct { return true; }, Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx, ast.NodeBlock, - ast.NodeBlock { + const block = try self.createToCtxNode(arena, ctx, ast.Node.Block, + ast.Node.Block { .base = undefined, .label = null, .lbrace = *token, @@ -2978,88 +2996,88 @@ pub const Parser = struct { } } - fn tokenIdToAssignment(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { // TODO: We have to cast all cases because of this: // error: expected type '?InfixOp', found '?@TagType(InfixOp)' return switch (*id) { - Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp { .AssignBitAnd = void{} }, - Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftRight = void{} }, - Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp { .AssignTimes = void{} }, - Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp { .AssignTimesWarp = void{} }, - Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp { .AssignBitXor = void{} }, - Token.Id.Equal => ast.NodeInfixOp.InfixOp { .Assign = void{} }, - Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp { .AssignMinus = void{} }, - Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignMinusWrap = void{} }, - Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp { .AssignMod = void{} }, - Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp { .AssignBitOr = void{} }, - Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp { .AssignPlus = void{} }, - Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignPlusWrap = void{} }, - Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp { .AssignDiv = void{} }, + Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} }, + Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} }, + Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} }, + Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} }, + Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} }, + Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} }, + Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} }, + Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} }, + Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} }, + Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} }, + Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} }, + Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} }, + Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} }, else => null, }; } - fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} }, + Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, else => null, }; } - fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.BangEqual => ast.NodeInfixOp.InfixOp { .BangEqual = void{} }, - Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp { .EqualEqual = void{} }, - Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp { .LessThan = void{} }, - Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .LessOrEqual = void{} }, - Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp { .GreaterThan = void{} }, - Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .GreaterOrEqual = void{} }, + Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, + Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, + Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, + Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, + Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, + Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, else => null, }; } - fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} }, + Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, else => null, }; } - fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Minus => ast.NodeInfixOp.InfixOp { .Sub = void{} }, - Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp { .SubWrap = void{} }, - Token.Id.Plus => ast.NodeInfixOp.InfixOp { .Add = void{} }, - Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp { .AddWrap = void{} }, - Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp { .ArrayCat = void{} }, + Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, + Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, + Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, + Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, + Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, else => null, }; } - fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp { + fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { - Token.Id.Slash => ast.NodeInfixOp.InfixOp { .Div = void{} }, - Token.Id.Asterisk => ast.NodeInfixOp.InfixOp { .Mult = void{} }, - Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp { .ArrayMult = void{} }, - Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp { .MultWrap = void{} }, - Token.Id.Percent => ast.NodeInfixOp.InfixOp { .Mod = void{} }, - Token.Id.PipePipe => ast.NodeInfixOp.InfixOp { .MergeErrorSets = void{} }, + Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, + Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, + Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, + Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, + Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, + Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, else => null, }; } - fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.NodePrefixOp.PrefixOp { + fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { return switch (id) { - Token.Id.Bang => ast.NodePrefixOp.PrefixOp { .BoolNot = void{} }, - Token.Id.Tilde => ast.NodePrefixOp.PrefixOp { .BitNot = void{} }, - Token.Id.Minus => ast.NodePrefixOp.PrefixOp { .Negation = void{} }, - Token.Id.MinusPercent => ast.NodePrefixOp.PrefixOp { .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.NodePrefixOp.PrefixOp { .Deref = void{} }, - Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp { - .AddrOf = ast.NodePrefixOp.AddrOfInfo { + Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, + Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, + Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, + Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op { + .AddrOf = ast.Node.PrefixOp.AddrOfInfo { .align_expr = null, .bit_offset_start_token = null, .bit_offset_end_token = null, @@ -3067,10 +3085,10 @@ pub const Parser = struct { .volatile_token = null, }, }, - Token.Id.QuestionMark => ast.NodePrefixOp.PrefixOp { .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.NodePrefixOp.PrefixOp { .UnwrapMaybe = void{} }, - Token.Id.Keyword_await => ast.NodePrefixOp.PrefixOp { .Await = void{} }, - Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{ } }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, + Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, else => null, }; } @@ -3080,11 +3098,7 @@ pub const Parser = struct { *node = *init_to; node.base = blk: { const id = ast.Node.typeToId(T); - if (self.pending_line_comment_node) |comment_node| { - self.pending_line_comment_node = null; - break :blk ast.Node {.id = id, .comment = comment_node}; - } - break :blk ast.Node {.id = id, .comment = null }; + break :blk ast.Node {.id = id}; }; return node; @@ -3183,7 +3197,7 @@ pub const Parser = struct { indent: usize, }; - pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void { + pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { var stack = self.initUtilityArrayList(RenderAstFrame); defer self.deinitUtilityArrayList(stack); @@ -3215,14 +3229,14 @@ pub const Parser = struct { ParamDecl: &ast.Node, Text: []const u8, Expression: &ast.Node, - VarDecl: &ast.NodeVarDecl, + VarDecl: &ast.Node.VarDecl, Statement: &ast.Node, - FieldInitializer: &ast.NodeFieldInitializer, + FieldInitializer: &ast.Node.FieldInitializer, PrintIndent, Indent: usize, }; - pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void { + pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { var stack = self.initUtilityArrayList(RenderState); defer self.deinitUtilityArrayList(stack); @@ -3256,7 +3270,8 @@ pub const Parser = struct { RenderState.TopLevelDecl => |decl| { switch (decl.id) { ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl); + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + try self.renderComments(stream, fn_proto, indent); if (fn_proto.body_node) |body_node| { stack.append(RenderState { .Expression = body_node}) catch unreachable; @@ -3268,7 +3283,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = decl }); }, ast.Node.Id.Use => { - const use_decl = @fieldParentPtr(ast.NodeUse, "base", decl); + const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); if (use_decl.visib_token) |visib_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } @@ -3277,18 +3292,19 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = use_decl.expr }); }, ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl); + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); try stack.append(RenderState { .VarDecl = var_decl}); }, ast.Node.Id.TestDecl => { - const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl); + const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); + try self.renderComments(stream, test_decl, indent); try stream.print("test "); try stack.append(RenderState { .Expression = test_decl.body_node }); try stack.append(RenderState { .Text = " " }); try stack.append(RenderState { .Expression = test_decl.name }); }, ast.Node.Id.StructField => { - const field = @fieldParentPtr(ast.NodeStructField, "base", decl); + const field = @fieldParentPtr(ast.Node.StructField, "base", decl); if (field.visib_token) |visib_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); } @@ -3296,7 +3312,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = field.type_expr}); }, ast.Node.Id.UnionTag => { - const tag = @fieldParentPtr(ast.NodeUnionTag, "base", decl); + const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); if (tag.type_expr) |type_expr| { @@ -3305,7 +3321,7 @@ pub const Parser = struct { } }, ast.Node.Id.EnumTag => { - const tag = @fieldParentPtr(ast.NodeEnumTag, "base", decl); + const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); if (tag.value) |value| { @@ -3324,6 +3340,7 @@ pub const Parser = struct { }, RenderState.FieldInitializer => |field_init| { + //TODO try self.renderComments(stream, field_init, indent); try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token)); try stream.print(" = "); try stack.append(RenderState { .Expression = field_init.expr }); @@ -3369,7 +3386,8 @@ pub const Parser = struct { }, RenderState.ParamDecl => |base| { - const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base); + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); + // TODO try self.renderComments(stream, param_decl, indent); if (param_decl.comptime_token) |comptime_token| { try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token)); } @@ -3390,11 +3408,11 @@ pub const Parser = struct { }, RenderState.Expression => |base| switch (base.id) { ast.Node.Id.Identifier => { - const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base); + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token)); }, ast.Node.Id.Block => { - const block = @fieldParentPtr(ast.NodeBlock, "base", base); + const block = @fieldParentPtr(ast.Node.Block, "base", base); if (block.label) |label| { try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); } @@ -3430,17 +3448,17 @@ pub const Parser = struct { } }, ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.NodeDefer, "base", base); + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token)); try stack.append(RenderState { .Expression = defer_node.expr }); }, ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", base); + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token)); try stack.append(RenderState { .Expression = comptime_node.expr }); }, ast.Node.Id.AsyncAttribute => { - const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base); + const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token)); if (async_attr.allocator_type) |allocator_type| { @@ -3450,7 +3468,7 @@ pub const Parser = struct { } }, ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base); + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token)); if (suspend_node.body) |body| { @@ -3464,10 +3482,10 @@ pub const Parser = struct { } }, ast.Node.Id.InfixOp => { - const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base); + const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) { + if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { if (prefix_op_node.op.Catch) |payload| { try stack.append(RenderState { .Text = " " }); try stack.append(RenderState { .Expression = payload }); @@ -3475,49 +3493,49 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " catch " }); } else { const text = switch (prefix_op_node.op) { - ast.NodeInfixOp.InfixOp.Add => " + ", - ast.NodeInfixOp.InfixOp.AddWrap => " +% ", - ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ", - ast.NodeInfixOp.InfixOp.ArrayMult => " ** ", - ast.NodeInfixOp.InfixOp.Assign => " = ", - ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ", - ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ", - ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ", - ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ", - ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ", - ast.NodeInfixOp.InfixOp.AssignDiv => " /= ", - ast.NodeInfixOp.InfixOp.AssignMinus => " -= ", - ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ", - ast.NodeInfixOp.InfixOp.AssignMod => " %= ", - ast.NodeInfixOp.InfixOp.AssignPlus => " += ", - ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ", - ast.NodeInfixOp.InfixOp.AssignTimes => " *= ", - ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ", - ast.NodeInfixOp.InfixOp.BangEqual => " != ", - ast.NodeInfixOp.InfixOp.BitAnd => " & ", - ast.NodeInfixOp.InfixOp.BitOr => " | ", - ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ", - ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ", - ast.NodeInfixOp.InfixOp.BitXor => " ^ ", - ast.NodeInfixOp.InfixOp.BoolAnd => " and ", - ast.NodeInfixOp.InfixOp.BoolOr => " or ", - ast.NodeInfixOp.InfixOp.Div => " / ", - ast.NodeInfixOp.InfixOp.EqualEqual => " == ", - ast.NodeInfixOp.InfixOp.ErrorUnion => "!", - ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ", - ast.NodeInfixOp.InfixOp.GreaterThan => " > ", - ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ", - ast.NodeInfixOp.InfixOp.LessThan => " < ", - ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ", - ast.NodeInfixOp.InfixOp.Mod => " % ", - ast.NodeInfixOp.InfixOp.Mult => " * ", - ast.NodeInfixOp.InfixOp.MultWrap => " *% ", - ast.NodeInfixOp.InfixOp.Period => ".", - ast.NodeInfixOp.InfixOp.Sub => " - ", - ast.NodeInfixOp.InfixOp.SubWrap => " -% ", - ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ", - ast.NodeInfixOp.InfixOp.Range => " ... ", - ast.NodeInfixOp.InfixOp.Catch => unreachable, + ast.Node.InfixOp.Op.Add => " + ", + ast.Node.InfixOp.Op.AddWrap => " +% ", + ast.Node.InfixOp.Op.ArrayCat => " ++ ", + ast.Node.InfixOp.Op.ArrayMult => " ** ", + ast.Node.InfixOp.Op.Assign => " = ", + ast.Node.InfixOp.Op.AssignBitAnd => " &= ", + ast.Node.InfixOp.Op.AssignBitOr => " |= ", + ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", + ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", + ast.Node.InfixOp.Op.AssignBitXor => " ^= ", + ast.Node.InfixOp.Op.AssignDiv => " /= ", + ast.Node.InfixOp.Op.AssignMinus => " -= ", + ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", + ast.Node.InfixOp.Op.AssignMod => " %= ", + ast.Node.InfixOp.Op.AssignPlus => " += ", + ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", + ast.Node.InfixOp.Op.AssignTimes => " *= ", + ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", + ast.Node.InfixOp.Op.BangEqual => " != ", + ast.Node.InfixOp.Op.BitAnd => " & ", + ast.Node.InfixOp.Op.BitOr => " | ", + ast.Node.InfixOp.Op.BitShiftLeft => " << ", + ast.Node.InfixOp.Op.BitShiftRight => " >> ", + ast.Node.InfixOp.Op.BitXor => " ^ ", + ast.Node.InfixOp.Op.BoolAnd => " and ", + ast.Node.InfixOp.Op.BoolOr => " or ", + ast.Node.InfixOp.Op.Div => " / ", + ast.Node.InfixOp.Op.EqualEqual => " == ", + ast.Node.InfixOp.Op.ErrorUnion => "!", + ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", + ast.Node.InfixOp.Op.GreaterThan => " > ", + ast.Node.InfixOp.Op.LessOrEqual => " <= ", + ast.Node.InfixOp.Op.LessThan => " < ", + ast.Node.InfixOp.Op.MergeErrorSets => " || ", + ast.Node.InfixOp.Op.Mod => " % ", + ast.Node.InfixOp.Op.Mult => " * ", + ast.Node.InfixOp.Op.MultWrap => " *% ", + ast.Node.InfixOp.Op.Period => ".", + ast.Node.InfixOp.Op.Sub => " - ", + ast.Node.InfixOp.Op.SubWrap => " -% ", + ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", + ast.Node.InfixOp.Op.Range => " ... ", + ast.Node.InfixOp.Op.Catch => unreachable, }; try stack.append(RenderState { .Text = text }); @@ -3525,10 +3543,10 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = prefix_op_node.lhs }); }, ast.Node.Id.PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base); + const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); try stack.append(RenderState { .Expression = prefix_op_node.rhs }); switch (prefix_op_node.op) { - ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| { + ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { try stream.write("&"); if (addr_of_info.volatile_token != null) { try stack.append(RenderState { .Text = "volatile "}); @@ -3542,7 +3560,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = align_expr}); } }, - ast.NodePrefixOp.PrefixOp.SliceType => |addr_of_info| { + ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { try stream.write("[]"); if (addr_of_info.volatile_token != null) { try stack.append(RenderState { .Text = "volatile "}); @@ -3556,29 +3574,29 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = align_expr}); } }, - ast.NodePrefixOp.PrefixOp.ArrayType => |array_index| { + ast.Node.PrefixOp.Op.ArrayType => |array_index| { try stack.append(RenderState { .Text = "]"}); try stack.append(RenderState { .Expression = array_index}); try stack.append(RenderState { .Text = "["}); }, - ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"), - ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"), - ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"), - ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"), - ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"), - ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "), - ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"), - ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"), - ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "), - ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "), - ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "), + ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), + ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), + ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Negation => try stream.write("-"), + ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), + ast.Node.PrefixOp.Op.Try => try stream.write("try "), + ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), + ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), + ast.Node.PrefixOp.Op.Await => try stream.write("await "), + ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), + ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), } }, ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", base); + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); switch (suffix_op.op) { - ast.NodeSuffixOp.SuffixOp.Call => |call_info| { + ast.Node.SuffixOp.Op.Call => |call_info| { try stack.append(RenderState { .Text = ")"}); var i = call_info.params.len; while (i != 0) { @@ -3597,13 +3615,13 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = &async_attr.base }); } }, - ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| { + ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { try stack.append(RenderState { .Text = "]"}); try stack.append(RenderState { .Expression = index_expr}); try stack.append(RenderState { .Text = "["}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, - ast.NodeSuffixOp.SuffixOp.Slice => |range| { + ast.Node.SuffixOp.Op.Slice => |range| { try stack.append(RenderState { .Text = "]"}); if (range.end) |end| { try stack.append(RenderState { .Expression = end}); @@ -3613,7 +3631,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "["}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, - ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| { + ast.Node.SuffixOp.Op.StructInitializer => |field_inits| { if (field_inits.len == 0) { try stack.append(RenderState { .Text = "{}" }); try stack.append(RenderState { .Expression = suffix_op.lhs }); @@ -3634,7 +3652,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = " {\n"}); try stack.append(RenderState { .Expression = suffix_op.lhs }); }, - ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| { + ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| { if (exprs.len == 0) { try stack.append(RenderState { .Text = "{}" }); try stack.append(RenderState { .Expression = suffix_op.lhs }); @@ -3658,7 +3676,7 @@ pub const Parser = struct { } }, ast.Node.Id.ControlFlowExpression => { - const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base); + const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); if (flow_expr.rhs) |rhs| { try stack.append(RenderState { .Expression = rhs }); @@ -3666,34 +3684,34 @@ pub const Parser = struct { } switch (flow_expr.kind) { - ast.NodeControlFlowExpression.Kind.Break => |maybe_label| { + ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { try stream.print("break"); if (maybe_label) |label| { try stream.print(" :"); try stack.append(RenderState { .Expression = label }); } }, - ast.NodeControlFlowExpression.Kind.Continue => |maybe_label| { + ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { try stream.print("continue"); if (maybe_label) |label| { try stream.print(" :"); try stack.append(RenderState { .Expression = label }); } }, - ast.NodeControlFlowExpression.Kind.Return => { + ast.Node.ControlFlowExpression.Kind.Return => { try stream.print("return"); }, } }, ast.Node.Id.Payload => { - const payload = @fieldParentPtr(ast.NodePayload, "base", base); + const payload = @fieldParentPtr(ast.Node.Payload, "base", base); try stack.append(RenderState { .Text = "|"}); try stack.append(RenderState { .Expression = payload.error_symbol }); try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerPayload => { - const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base); + const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); try stack.append(RenderState { .Text = "|"}); try stack.append(RenderState { .Expression = payload.value_symbol }); @@ -3704,7 +3722,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.PointerIndexPayload => { - const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base); + const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); try stack.append(RenderState { .Text = "|"}); if (payload.index_symbol) |index_symbol| { @@ -3721,69 +3739,69 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "|"}); }, ast.Node.Id.GroupedExpression => { - const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base); + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); try stack.append(RenderState { .Text = ")"}); try stack.append(RenderState { .Expression = grouped_expr.expr }); try stack.append(RenderState { .Text = "("}); }, ast.Node.Id.FieldInitializer => { - const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base); + const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token)); try stack.append(RenderState { .Expression = field_init.expr }); }, ast.Node.Id.IntegerLiteral => { - const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base); + const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token)); }, ast.Node.Id.FloatLiteral => { - const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base); + const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token)); }, ast.Node.Id.StringLiteral => { - const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base); + const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token)); }, ast.Node.Id.CharLiteral => { - const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base); + const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token)); }, ast.Node.Id.BoolLiteral => { - const bool_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base); + const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token)); }, ast.Node.Id.NullLiteral => { - const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base); + const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token)); }, ast.Node.Id.ThisLiteral => { - const this_literal = @fieldParentPtr(ast.NodeThisLiteral, "base", base); + const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token)); }, ast.Node.Id.Unreachable => { - const unreachable_node = @fieldParentPtr(ast.NodeUnreachable, "base", base); + const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token)); }, ast.Node.Id.ErrorType => { - const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base); + const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token)); }, ast.Node.Id.VarType => { - const var_type = @fieldParentPtr(ast.NodeVarType, "base", base); + const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token)); }, ast.Node.Id.ContainerDecl => { - const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base); + const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); switch (container_decl.layout) { - ast.NodeContainerDecl.Layout.Packed => try stream.print("packed "), - ast.NodeContainerDecl.Layout.Extern => try stream.print("extern "), - ast.NodeContainerDecl.Layout.Auto => { }, + ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), + ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), + ast.Node.ContainerDecl.Layout.Auto => { }, } switch (container_decl.kind) { - ast.NodeContainerDecl.Kind.Struct => try stream.print("struct"), - ast.NodeContainerDecl.Kind.Enum => try stream.print("enum"), - ast.NodeContainerDecl.Kind.Union => try stream.print("union"), + ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), + ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), + ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), } try stack.append(RenderState { .Text = "}"}); @@ -3823,9 +3841,9 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "{"}); switch (container_decl.init_arg_expr) { - ast.NodeContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), - ast.NodeContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}), - ast.NodeContainerDecl.InitArg.Type => |type_expr| { + ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), + ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}), + ast.Node.ContainerDecl.InitArg.Type => |type_expr| { try stack.append(RenderState { .Text = ") "}); try stack.append(RenderState { .Expression = type_expr}); try stack.append(RenderState { .Text = "("}); @@ -3833,7 +3851,7 @@ pub const Parser = struct { } }, ast.Node.Id.ErrorSetDecl => { - const err_set_decl = @fieldParentPtr(ast.NodeErrorSetDecl, "base", base); + const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); try stream.print("error "); try stack.append(RenderState { .Text = "}"}); @@ -3866,7 +3884,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "{"}); }, ast.Node.Id.MultilineStringLiteral => { - const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base); + const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); try stream.print("\n"); var i : usize = 0; @@ -3878,11 +3896,11 @@ pub const Parser = struct { try stream.writeByteNTimes(' ', indent + indent_delta); }, ast.Node.Id.UndefinedLiteral => { - const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base); + const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token)); }, ast.Node.Id.BuiltinCall => { - const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base); + const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token)); try stack.append(RenderState { .Text = ")"}); var i = builtin_call.params.len; @@ -3896,13 +3914,13 @@ pub const Parser = struct { } }, ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", base); + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); switch (fn_proto.return_type) { - ast.NodeFnProto.ReturnType.Explicit => |node| { + ast.Node.FnProto.ReturnType.Explicit => |node| { try stack.append(RenderState { .Expression = node}); }, - ast.NodeFnProto.ReturnType.InferErrorSet => |node| { + ast.Node.FnProto.ReturnType.InferErrorSet => |node| { try stack.append(RenderState { .Expression = node}); try stack.append(RenderState { .Text = "!"}); }, @@ -3960,7 +3978,7 @@ pub const Parser = struct { }, ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), ast.Node.Id.Switch => { - const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base); + const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); try stack.append(RenderState { .Text = "}"}); @@ -3994,7 +4012,7 @@ pub const Parser = struct { try stack.append(RenderState { .Expression = switch_node.expr }); }, ast.Node.Id.SwitchCase => { - const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base); + const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); try stack.append(RenderState { .Expression = switch_case.expr }); if (switch_case.payload) |payload| { @@ -4016,11 +4034,11 @@ pub const Parser = struct { } }, ast.Node.Id.SwitchElse => { - const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base); + const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token)); }, ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.NodeElse, "base", base); + const else_node = @fieldParentPtr(ast.Node.Else, "base", base); try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token)); switch (else_node.body.id) { @@ -4045,7 +4063,7 @@ pub const Parser = struct { } }, ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.NodeWhile, "base", base); + const while_node = @fieldParentPtr(ast.Node.While, "base", base); if (while_node.label) |label| { try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); } @@ -4095,7 +4113,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.NodeFor, "base", base); + const for_node = @fieldParentPtr(ast.Node.For, "base", base); if (for_node.label) |label| { try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); } @@ -4138,7 +4156,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.NodeIf, "base", base); + const if_node = @fieldParentPtr(ast.Node.If, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token)); switch (if_node.body.id) { @@ -4185,7 +4203,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.Asm => { - const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base); + const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token)); if (asm_node.volatile_token) |volatile_token| { @@ -4272,7 +4290,7 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "(" }); }, ast.Node.Id.AsmInput => { - const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base); + const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); try stack.append(RenderState { .Text = ")"}); try stack.append(RenderState { .Expression = asm_input.expr}); @@ -4283,14 +4301,14 @@ pub const Parser = struct { try stack.append(RenderState { .Text = "["}); }, ast.Node.Id.AsmOutput => { - const asm_output = @fieldParentPtr(ast.NodeAsmOutput, "base", base); + const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); try stack.append(RenderState { .Text = ")"}); switch (asm_output.kind) { - ast.NodeAsmOutput.Kind.Variable => |variable_name| { + ast.Node.AsmOutput.Kind.Variable => |variable_name| { try stack.append(RenderState { .Expression = &variable_name.base}); }, - ast.NodeAsmOutput.Kind.Return => |return_type| { + ast.Node.AsmOutput.Kind.Return => |return_type| { try stack.append(RenderState { .Expression = return_type}); try stack.append(RenderState { .Text = "-> "}); }, @@ -4312,15 +4330,10 @@ pub const Parser = struct { ast.Node.Id.ParamDecl => unreachable, }, RenderState.Statement => |base| { - if (base.comment) |comment| { - for (comment.lines.toSliceConst()) |line_token| { - try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); - try stream.writeByteNTimes(' ', indent); - } - } switch (base.id) { ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base); + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); + try self.renderComments(stream, var_decl, indent); try stack.append(RenderState { .VarDecl = var_decl}); }, else => { @@ -4337,6 +4350,14 @@ pub const Parser = struct { } } + fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { + const comment = node.comments ?? return; + for (comment.lines.toSliceConst()) |line_token| { + try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); + try stream.writeByteNTimes(' ', indent); + } + } + fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) { const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T); self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count); @@ -4411,6 +4432,14 @@ fn testCanonical(source: []const u8) !void { } } +test "zig fmt: preserve top level comments" { + try testCanonical( + \\// top level comment + \\test "hi" {} + \\ + ); +} + test "zig fmt: get stdout or fail" { try testCanonical( \\const std = @import("std"); -- cgit v1.2.3 From 21767144fc1a8627a109e81a164c55171c279d82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 18:11:50 -0400 Subject: linux: support VDSO for clock_gettime also fix a compiler crash when using cmpxchg with nullable pointer --- CMakeLists.txt | 1 + src/codegen.cpp | 10 + std/elf.zig | 611 ++++++++++++++++++++++++++++++++++++++++++++++ std/os/index.zig | 1 + std/os/linux/index.zig | 20 ++ std/os/linux/vdso.zig | 89 +++++++ std/os/linux/x86_64.zig | 8 + std/os/time.zig | 8 +- std/special/bootstrap.zig | 27 +- test/cases/atomics.zig | 20 ++ 10 files changed, 783 insertions(+), 12 deletions(-) create mode 100644 std/os/linux/vdso.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 07e722f7b8..e692974719 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -508,6 +508,7 @@ set(ZIG_STD_FILES "os/index.zig" "os/linux/errno.zig" "os/linux/index.zig" + "os/linux/vdso.zig" "os/linux/x86_64.zig" "os/path.zig" "os/time.zig" diff --git a/src/codegen.cpp b/src/codegen.cpp index b5c8fdecac..4581c3e2b3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3561,6 +3561,16 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, success_order, failure_order, instruction->is_weak); + TypeTableEntry *maybe_type = instruction->base.value.type; + assert(maybe_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = maybe_type->data.maybe.child_type; + + if (type_is_codegen_pointer(child_type)) { + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); + } + assert(instruction->tmp_ptr != nullptr); assert(type_has_bits(instruction->type)); diff --git a/std/elf.zig b/std/elf.zig index 7e20fa000f..1764829bc8 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -7,6 +7,246 @@ const mem = std.mem; const debug = std.debug; const InStream = std.stream.InStream; +pub const AT_NULL = 0; +pub const AT_IGNORE = 1; +pub const AT_EXECFD = 2; +pub const AT_PHDR = 3; +pub const AT_PHENT = 4; +pub const AT_PHNUM = 5; +pub const AT_PAGESZ = 6; +pub const AT_BASE = 7; +pub const AT_FLAGS = 8; +pub const AT_ENTRY = 9; +pub const AT_NOTELF = 10; +pub const AT_UID = 11; +pub const AT_EUID = 12; +pub const AT_GID = 13; +pub const AT_EGID = 14; +pub const AT_CLKTCK = 17; +pub const AT_PLATFORM = 15; +pub const AT_HWCAP = 16; +pub const AT_FPUCW = 18; +pub const AT_DCACHEBSIZE = 19; +pub const AT_ICACHEBSIZE = 20; +pub const AT_UCACHEBSIZE = 21; +pub const AT_IGNOREPPC = 22; +pub const AT_SECURE = 23; +pub const AT_BASE_PLATFORM = 24; +pub const AT_RANDOM = 25; +pub const AT_HWCAP2 = 26; +pub const AT_EXECFN = 31; +pub const AT_SYSINFO = 32; +pub const AT_SYSINFO_EHDR = 33; +pub const AT_L1I_CACHESHAPE = 34; +pub const AT_L1D_CACHESHAPE = 35; +pub const AT_L2_CACHESHAPE = 36; +pub const AT_L3_CACHESHAPE = 37; +pub const AT_L1I_CACHESIZE = 40; +pub const AT_L1I_CACHEGEOMETRY = 41; +pub const AT_L1D_CACHESIZE = 42; +pub const AT_L1D_CACHEGEOMETRY = 43; +pub const AT_L2_CACHESIZE = 44; +pub const AT_L2_CACHEGEOMETRY = 45; +pub const AT_L3_CACHESIZE = 46; +pub const AT_L3_CACHEGEOMETRY = 47; + +pub const DT_NULL = 0; +pub const DT_NEEDED = 1; +pub const DT_PLTRELSZ = 2; +pub const DT_PLTGOT = 3; +pub const DT_HASH = 4; +pub const DT_STRTAB = 5; +pub const DT_SYMTAB = 6; +pub const DT_RELA = 7; +pub const DT_RELASZ = 8; +pub const DT_RELAENT = 9; +pub const DT_STRSZ = 10; +pub const DT_SYMENT = 11; +pub const DT_INIT = 12; +pub const DT_FINI = 13; +pub const DT_SONAME = 14; +pub const DT_RPATH = 15; +pub const DT_SYMBOLIC = 16; +pub const DT_REL = 17; +pub const DT_RELSZ = 18; +pub const DT_RELENT = 19; +pub const DT_PLTREL = 20; +pub const DT_DEBUG = 21; +pub const DT_TEXTREL = 22; +pub const DT_JMPREL = 23; +pub const DT_BIND_NOW = 24; +pub const DT_INIT_ARRAY = 25; +pub const DT_FINI_ARRAY = 26; +pub const DT_INIT_ARRAYSZ = 27; +pub const DT_FINI_ARRAYSZ = 28; +pub const DT_RUNPATH = 29; +pub const DT_FLAGS = 30; +pub const DT_ENCODING = 32; +pub const DT_PREINIT_ARRAY = 32; +pub const DT_PREINIT_ARRAYSZ = 33; +pub const DT_SYMTAB_SHNDX = 34; +pub const DT_NUM = 35; +pub const DT_LOOS = 0x6000000d; +pub const DT_HIOS = 0x6ffff000; +pub const DT_LOPROC = 0x70000000; +pub const DT_HIPROC = 0x7fffffff; +pub const DT_PROCNUM = DT_MIPS_NUM; + +pub const DT_VALRNGLO = 0x6ffffd00; +pub const DT_GNU_PRELINKED = 0x6ffffdf5; +pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6; +pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7; +pub const DT_CHECKSUM = 0x6ffffdf8; +pub const DT_PLTPADSZ = 0x6ffffdf9; +pub const DT_MOVEENT = 0x6ffffdfa; +pub const DT_MOVESZ = 0x6ffffdfb; +pub const DT_FEATURE_1 = 0x6ffffdfc; +pub const DT_POSFLAG_1 = 0x6ffffdfd; + +pub const DT_SYMINSZ = 0x6ffffdfe; +pub const DT_SYMINENT = 0x6ffffdff; +pub const DT_VALRNGHI = 0x6ffffdff; +pub const DT_VALNUM = 12; + +pub const DT_ADDRRNGLO = 0x6ffffe00; +pub const DT_GNU_HASH = 0x6ffffef5; +pub const DT_TLSDESC_PLT = 0x6ffffef6; +pub const DT_TLSDESC_GOT = 0x6ffffef7; +pub const DT_GNU_CONFLICT = 0x6ffffef8; +pub const DT_GNU_LIBLIST = 0x6ffffef9; +pub const DT_CONFIG = 0x6ffffefa; +pub const DT_DEPAUDIT = 0x6ffffefb; +pub const DT_AUDIT = 0x6ffffefc; +pub const DT_PLTPAD = 0x6ffffefd; +pub const DT_MOVETAB = 0x6ffffefe; +pub const DT_SYMINFO = 0x6ffffeff; +pub const DT_ADDRRNGHI = 0x6ffffeff; +pub const DT_ADDRNUM = 11; + + +pub const DT_VERSYM = 0x6ffffff0; + +pub const DT_RELACOUNT = 0x6ffffff9; +pub const DT_RELCOUNT = 0x6ffffffa; + + +pub const DT_FLAGS_1 = 0x6ffffffb; +pub const DT_VERDEF = 0x6ffffffc; + +pub const DT_VERDEFNUM = 0x6ffffffd; +pub const DT_VERNEED = 0x6ffffffe; + +pub const DT_VERNEEDNUM = 0x6fffffff; +pub const DT_VERSIONTAGNUM = 16; + + + +pub const DT_AUXILIARY = 0x7ffffffd; +pub const DT_FILTER = 0x7fffffff; +pub const DT_EXTRANUM = 3; + + +pub const DT_SPARC_REGISTER = 0x70000001; +pub const DT_SPARC_NUM = 2; + +pub const DT_MIPS_RLD_VERSION = 0x70000001; +pub const DT_MIPS_TIME_STAMP = 0x70000002; +pub const DT_MIPS_ICHECKSUM = 0x70000003; +pub const DT_MIPS_IVERSION = 0x70000004; +pub const DT_MIPS_FLAGS = 0x70000005; +pub const DT_MIPS_BASE_ADDRESS = 0x70000006; +pub const DT_MIPS_MSYM = 0x70000007; +pub const DT_MIPS_CONFLICT = 0x70000008; +pub const DT_MIPS_LIBLIST = 0x70000009; +pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a; +pub const DT_MIPS_CONFLICTNO = 0x7000000b; +pub const DT_MIPS_LIBLISTNO = 0x70000010; +pub const DT_MIPS_SYMTABNO = 0x70000011; +pub const DT_MIPS_UNREFEXTNO = 0x70000012; +pub const DT_MIPS_GOTSYM = 0x70000013; +pub const DT_MIPS_HIPAGENO = 0x70000014; +pub const DT_MIPS_RLD_MAP = 0x70000016; +pub const DT_MIPS_DELTA_CLASS = 0x70000017; +pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018; + +pub const DT_MIPS_DELTA_INSTANCE = 0x70000019; +pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a; + +pub const DT_MIPS_DELTA_RELOC = 0x7000001b; +pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c; + +pub const DT_MIPS_DELTA_SYM = 0x7000001d; + +pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e; + +pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020; + +pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021; + +pub const DT_MIPS_CXX_FLAGS = 0x70000022; +pub const DT_MIPS_PIXIE_INIT = 0x70000023; +pub const DT_MIPS_SYMBOL_LIB = 0x70000024; +pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025; +pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026; +pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027; +pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028; +pub const DT_MIPS_OPTIONS = 0x70000029; +pub const DT_MIPS_INTERFACE = 0x7000002a; +pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b; +pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c; +pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d; + +pub const DT_MIPS_PERF_SUFFIX = 0x7000002e; + +pub const DT_MIPS_COMPACT_SIZE = 0x7000002f; +pub const DT_MIPS_GP_VALUE = 0x70000030; +pub const DT_MIPS_AUX_DYNAMIC = 0x70000031; + +pub const DT_MIPS_PLTGOT = 0x70000032; + +pub const DT_MIPS_RWPLT = 0x70000034; +pub const DT_MIPS_RLD_MAP_REL = 0x70000035; +pub const DT_MIPS_NUM = 0x36; + +pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0); +pub const DT_ALPHA_NUM = 1; + +pub const DT_PPC_GOT = (DT_LOPROC + 0); +pub const DT_PPC_OPT = (DT_LOPROC + 1); +pub const DT_PPC_NUM = 2; + +pub const DT_PPC64_GLINK = (DT_LOPROC + 0); +pub const DT_PPC64_OPD = (DT_LOPROC + 1); +pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2); +pub const DT_PPC64_OPT = (DT_LOPROC + 3); +pub const DT_PPC64_NUM = 4; + +pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0); +pub const DT_IA_64_NUM = 1; + +pub const DT_NIOS2_GP = 0x70000002; + +pub const PT_NULL = 0; +pub const PT_LOAD = 1; +pub const PT_DYNAMIC = 2; +pub const PT_INTERP = 3; +pub const PT_NOTE = 4; +pub const PT_SHLIB = 5; +pub const PT_PHDR = 6; +pub const PT_TLS = 7; +pub const PT_NUM = 8; +pub const PT_LOOS = 0x60000000; +pub const PT_GNU_EH_FRAME = 0x6474e550; +pub const PT_GNU_STACK = 0x6474e551; +pub const PT_GNU_RELRO = 0x6474e552; +pub const PT_LOSUNW = 0x6ffffffa; +pub const PT_SUNWBSS = 0x6ffffffa; +pub const PT_SUNWSTACK = 0x6ffffffb; +pub const PT_HISUNW = 0x6fffffff; +pub const PT_HIOS = 0x6fffffff; +pub const PT_LOPROC = 0x70000000; +pub const PT_HIPROC = 0x7fffffff; + pub const SHT_NULL = 0; pub const SHT_PROGBITS = 1; pub const SHT_SYMTAB = 2; @@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff; pub const SHT_LOUSER = 0x80000000; pub const SHT_HIUSER = 0xffffffff; +pub const STB_LOCAL = 0; +pub const STB_GLOBAL = 1; +pub const STB_WEAK = 2; +pub const STB_NUM = 3; +pub const STB_LOOS = 10; +pub const STB_GNU_UNIQUE = 10; +pub const STB_HIOS = 12; +pub const STB_LOPROC = 13; +pub const STB_HIPROC = 15; + +pub const STB_MIPS_SPLIT_COMMON = 13; + +pub const STT_NOTYPE = 0; +pub const STT_OBJECT = 1; +pub const STT_FUNC = 2; +pub const STT_SECTION = 3; +pub const STT_FILE = 4; +pub const STT_COMMON = 5; +pub const STT_TLS = 6; +pub const STT_NUM = 7; +pub const STT_LOOS = 10; +pub const STT_GNU_IFUNC = 10; +pub const STT_HIOS = 12; +pub const STT_LOPROC = 13; +pub const STT_HIPROC = 15; + +pub const STT_SPARC_REGISTER = 13; + +pub const STT_PARISC_MILLICODE = 13; + +pub const STT_HP_OPAQUE = (STT_LOOS + 0x1); +pub const STT_HP_STUB = (STT_LOOS + 0x2); + +pub const STT_ARM_TFUNC = STT_LOPROC; +pub const STT_ARM_16BIT = STT_HIPROC; + +pub const VER_FLG_BASE = 0x1; +pub const VER_FLG_WEAK = 0x2; + pub const FileType = enum { Relocatable, Executable, @@ -266,3 +545,335 @@ pub const Elf = struct { try elf.in_file.seekTo(elf_section.offset); } }; + +pub const EI_NIDENT = 16; +pub const Elf32_Half = u16; +pub const Elf64_Half = u16; +pub const Elf32_Word = u32; +pub const Elf32_Sword = i32; +pub const Elf64_Word = u32; +pub const Elf64_Sword = i32; +pub const Elf32_Xword = u64; +pub const Elf32_Sxword = i64; +pub const Elf64_Xword = u64; +pub const Elf64_Sxword = i64; +pub const Elf32_Addr = u32; +pub const Elf64_Addr = u64; +pub const Elf32_Off = u32; +pub const Elf64_Off = u64; +pub const Elf32_Section = u16; +pub const Elf64_Section = u16; +pub const Elf32_Versym = Elf32_Half; +pub const Elf64_Versym = Elf64_Half; +pub const Elf32_Ehdr = extern struct { + e_ident: [EI_NIDENT]u8, + e_type: Elf32_Half, + e_machine: Elf32_Half, + e_version: Elf32_Word, + e_entry: Elf32_Addr, + e_phoff: Elf32_Off, + e_shoff: Elf32_Off, + e_flags: Elf32_Word, + e_ehsize: Elf32_Half, + e_phentsize: Elf32_Half, + e_phnum: Elf32_Half, + e_shentsize: Elf32_Half, + e_shnum: Elf32_Half, + e_shstrndx: Elf32_Half, +}; +pub const Elf64_Ehdr = extern struct { + e_ident: [EI_NIDENT]u8, + e_type: Elf64_Half, + e_machine: Elf64_Half, + e_version: Elf64_Word, + e_entry: Elf64_Addr, + e_phoff: Elf64_Off, + e_shoff: Elf64_Off, + e_flags: Elf64_Word, + e_ehsize: Elf64_Half, + e_phentsize: Elf64_Half, + e_phnum: Elf64_Half, + e_shentsize: Elf64_Half, + e_shnum: Elf64_Half, + e_shstrndx: Elf64_Half, +}; +pub const Elf32_Shdr = extern struct { + sh_name: Elf32_Word, + sh_type: Elf32_Word, + sh_flags: Elf32_Word, + sh_addr: Elf32_Addr, + sh_offset: Elf32_Off, + sh_size: Elf32_Word, + sh_link: Elf32_Word, + sh_info: Elf32_Word, + sh_addralign: Elf32_Word, + sh_entsize: Elf32_Word, +}; +pub const Elf64_Shdr = extern struct { + sh_name: Elf64_Word, + sh_type: Elf64_Word, + sh_flags: Elf64_Xword, + sh_addr: Elf64_Addr, + sh_offset: Elf64_Off, + sh_size: Elf64_Xword, + sh_link: Elf64_Word, + sh_info: Elf64_Word, + sh_addralign: Elf64_Xword, + sh_entsize: Elf64_Xword, +}; +pub const Elf32_Chdr = extern struct { + ch_type: Elf32_Word, + ch_size: Elf32_Word, + ch_addralign: Elf32_Word, +}; +pub const Elf64_Chdr = extern struct { + ch_type: Elf64_Word, + ch_reserved: Elf64_Word, + ch_size: Elf64_Xword, + ch_addralign: Elf64_Xword, +}; +pub const Elf32_Sym = extern struct { + st_name: Elf32_Word, + st_value: Elf32_Addr, + st_size: Elf32_Word, + st_info: u8, + st_other: u8, + st_shndx: Elf32_Section, +}; +pub const Elf64_Sym = extern struct { + st_name: Elf64_Word, + st_info: u8, + st_other: u8, + st_shndx: Elf64_Section, + st_value: Elf64_Addr, + st_size: Elf64_Xword, +}; +pub const Elf32_Syminfo = extern struct { + si_boundto: Elf32_Half, + si_flags: Elf32_Half, +}; +pub const Elf64_Syminfo = extern struct { + si_boundto: Elf64_Half, + si_flags: Elf64_Half, +}; +pub const Elf32_Rel = extern struct { + r_offset: Elf32_Addr, + r_info: Elf32_Word, +}; +pub const Elf64_Rel = extern struct { + r_offset: Elf64_Addr, + r_info: Elf64_Xword, +}; +pub const Elf32_Rela = extern struct { + r_offset: Elf32_Addr, + r_info: Elf32_Word, + r_addend: Elf32_Sword, +}; +pub const Elf64_Rela = extern struct { + r_offset: Elf64_Addr, + r_info: Elf64_Xword, + r_addend: Elf64_Sxword, +}; +pub const Elf32_Phdr = extern struct { + p_type: Elf32_Word, + p_offset: Elf32_Off, + p_vaddr: Elf32_Addr, + p_paddr: Elf32_Addr, + p_filesz: Elf32_Word, + p_memsz: Elf32_Word, + p_flags: Elf32_Word, + p_align: Elf32_Word, +}; +pub const Elf64_Phdr = extern struct { + p_type: Elf64_Word, + p_flags: Elf64_Word, + p_offset: Elf64_Off, + p_vaddr: Elf64_Addr, + p_paddr: Elf64_Addr, + p_filesz: Elf64_Xword, + p_memsz: Elf64_Xword, + p_align: Elf64_Xword, +}; +pub const Elf32_Dyn = extern struct { + d_tag: Elf32_Sword, + d_un: extern union { + d_val: Elf32_Word, + d_ptr: Elf32_Addr, + }, +}; +pub const Elf64_Dyn = extern struct { + d_tag: Elf64_Sxword, + d_un: extern union { + d_val: Elf64_Xword, + d_ptr: Elf64_Addr, + }, +}; +pub const Elf32_Verdef = extern struct { + vd_version: Elf32_Half, + vd_flags: Elf32_Half, + vd_ndx: Elf32_Half, + vd_cnt: Elf32_Half, + vd_hash: Elf32_Word, + vd_aux: Elf32_Word, + vd_next: Elf32_Word, +}; +pub const Elf64_Verdef = extern struct { + vd_version: Elf64_Half, + vd_flags: Elf64_Half, + vd_ndx: Elf64_Half, + vd_cnt: Elf64_Half, + vd_hash: Elf64_Word, + vd_aux: Elf64_Word, + vd_next: Elf64_Word, +}; +pub const Elf32_Verdaux = extern struct { + vda_name: Elf32_Word, + vda_next: Elf32_Word, +}; +pub const Elf64_Verdaux = extern struct { + vda_name: Elf64_Word, + vda_next: Elf64_Word, +}; +pub const Elf32_Verneed = extern struct { + vn_version: Elf32_Half, + vn_cnt: Elf32_Half, + vn_file: Elf32_Word, + vn_aux: Elf32_Word, + vn_next: Elf32_Word, +}; +pub const Elf64_Verneed = extern struct { + vn_version: Elf64_Half, + vn_cnt: Elf64_Half, + vn_file: Elf64_Word, + vn_aux: Elf64_Word, + vn_next: Elf64_Word, +}; +pub const Elf32_Vernaux = extern struct { + vna_hash: Elf32_Word, + vna_flags: Elf32_Half, + vna_other: Elf32_Half, + vna_name: Elf32_Word, + vna_next: Elf32_Word, +}; +pub const Elf64_Vernaux = extern struct { + vna_hash: Elf64_Word, + vna_flags: Elf64_Half, + vna_other: Elf64_Half, + vna_name: Elf64_Word, + vna_next: Elf64_Word, +}; +pub const Elf32_auxv_t = extern struct { + a_type: u32, + a_un: extern union { + a_val: u32, + }, +}; +pub const Elf64_auxv_t = extern struct { + a_type: u64, + a_un: extern union { + a_val: u64, + }, +}; +pub const Elf32_Nhdr = extern struct { + n_namesz: Elf32_Word, + n_descsz: Elf32_Word, + n_type: Elf32_Word, +}; +pub const Elf64_Nhdr = extern struct { + n_namesz: Elf64_Word, + n_descsz: Elf64_Word, + n_type: Elf64_Word, +}; +pub const Elf32_Move = extern struct { + m_value: Elf32_Xword, + m_info: Elf32_Word, + m_poffset: Elf32_Word, + m_repeat: Elf32_Half, + m_stride: Elf32_Half, +}; +pub const Elf64_Move = extern struct { + m_value: Elf64_Xword, + m_info: Elf64_Xword, + m_poffset: Elf64_Xword, + m_repeat: Elf64_Half, + m_stride: Elf64_Half, +}; +pub const Elf32_gptab = extern union { + gt_header: extern struct { + gt_current_g_value: Elf32_Word, + gt_unused: Elf32_Word, + }, + gt_entry: extern struct { + gt_g_value: Elf32_Word, + gt_bytes: Elf32_Word, + }, +}; +pub const Elf32_RegInfo = extern struct { + ri_gprmask: Elf32_Word, + ri_cprmask: [4]Elf32_Word, + ri_gp_value: Elf32_Sword, +}; +pub const Elf_Options = extern struct { + kind: u8, + size: u8, + @"section": Elf32_Section, + info: Elf32_Word, +}; +pub const Elf_Options_Hw = extern struct { + hwp_flags1: Elf32_Word, + hwp_flags2: Elf32_Word, +}; +pub const Elf32_Lib = extern struct { + l_name: Elf32_Word, + l_time_stamp: Elf32_Word, + l_checksum: Elf32_Word, + l_version: Elf32_Word, + l_flags: Elf32_Word, +}; +pub const Elf64_Lib = extern struct { + l_name: Elf64_Word, + l_time_stamp: Elf64_Word, + l_checksum: Elf64_Word, + l_version: Elf64_Word, + l_flags: Elf64_Word, +}; +pub const Elf32_Conflict = Elf32_Addr; +pub const Elf_MIPS_ABIFlags_v0 = extern struct { + version: Elf32_Half, + isa_level: u8, + isa_rev: u8, + gpr_size: u8, + cpr1_size: u8, + cpr2_size: u8, + fp_abi: u8, + isa_ext: Elf32_Word, + ases: Elf32_Word, + flags1: Elf32_Word, + flags2: Elf32_Word, +}; + +pub const Ehdr = switch(@sizeOf(usize)) { + 4 => Elf32_Ehdr, + 8 => Elf64_Ehdr, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Phdr = switch(@sizeOf(usize)) { + 4 => Elf32_Phdr, + 8 => Elf64_Phdr, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Sym = switch(@sizeOf(usize)) { + 4 => Elf32_Sym, + 8 => Elf64_Sym, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Verdef = switch(@sizeOf(usize)) { + 4 => Elf32_Verdef, + 8 => Elf64_Verdef, + else => @compileError("expected pointer size of 32 or 64"), +}; +pub const Verdaux = switch(@sizeOf(usize)) { + 4 => Elf32_Verdaux, + 8 => Elf64_Verdaux, + else => @compileError("expected pointer size of 32 or 64"), +}; diff --git a/std/os/index.zig b/std/os/index.zig index 1916e23db0..0639490725 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -478,6 +478,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { }; } +pub var linux_aux_raw = []usize{0} ** 38; pub var posix_environ_raw: []&u8 = undefined; /// Caller must free result when done. diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index d7924f7159..dcd9532d1d 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -1,6 +1,7 @@ const std = @import("../../index.zig"); const assert = std.debug.assert; const builtin = @import("builtin"); +const vdso = @import("vdso.zig"); pub use switch (builtin.arch) { builtin.Arch.x86_64 => @import("x86_64.zig"), builtin.Arch.i386 => @import("i386.zig"), @@ -806,8 +807,27 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize { } pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { + if (VDSO_CGT_SYM.len != 0) { + const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); + if (@ptrToInt(f) != 0) { + const rc = f(clk_id, tp); + switch (rc) { + 0, @bitCast(usize, isize(-EINVAL)) => return rc, + else => {}, + } + } + } return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } +var vdso_clock_gettime = init_vdso_clock_gettime; +extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { + const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); + var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); + _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, + builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); + if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); + return f(clk, ts); +} pub fn clock_getres(clk_id: i32, tp: ×pec) usize { return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig new file mode 100644 index 0000000000..f4fb513af9 --- /dev/null +++ b/std/os/linux/vdso.zig @@ -0,0 +1,89 @@ +const std = @import("../../index.zig"); +const elf = std.elf; +const linux = std.os.linux; +const cstr = std.cstr; +const mem = std.mem; + +pub fn lookup(vername: []const u8, name: []const u8) usize { + const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR]; + if (vdso_addr == 0) return 0; + + const eh = @intToPtr(&elf.Ehdr, vdso_addr); + var ph_addr: usize = vdso_addr + eh.e_phoff; + const ph = @intToPtr(&elf.Phdr, ph_addr); + + var maybe_dynv: ?&usize = null; + var base: usize = @maxValue(usize); + { + var i: usize = 0; + while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) { + const this_ph = @intToPtr(&elf.Phdr, ph_addr); + switch (this_ph.p_type) { + elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, + elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset), + else => {}, + } + } + } + const dynv = maybe_dynv ?? return 0; + if (base == @maxValue(usize)) return 0; + + var maybe_strings: ?&u8 = null; + var maybe_syms: ?&elf.Sym = null; + var maybe_hashtab: ?&linux.Elf_Symndx = null; + var maybe_versym: ?&u16 = null; + var maybe_verdef: ?&elf.Verdef = null; + + { + var i: usize = 0; + while (dynv[i] != 0) : (i += 2) { + const p = base + dynv[i + 1]; + switch (dynv[i]) { + elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p), + else => {}, + } + } + } + + const strings = maybe_strings ?? return 0; + const syms = maybe_syms ?? return 0; + const hashtab = maybe_hashtab ?? return 0; + if (maybe_verdef == null) maybe_versym = null; + + + const OK_TYPES = (1<>4) & OK_BINDS)) continue; + if (0==syms[i].st_shndx) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue; + if (maybe_versym) |versym| { + if (!checkver(??maybe_verdef, versym[i], vername, strings)) + continue; + } + return base + syms[i].st_value; + } + + return 0; +} + +fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool { + var def = def_arg; + const vsym = @bitCast(u32, vsym_arg) & 0x7fff; + while (true) { + if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + break; + if (def.vd_next == 0) + return false; + def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next); + } + const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux); + return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); +} diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index bfc27b0f38..544b2365ce 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16; pub const F_GETOWNER_UIDS = 17; + +pub const VDSO_USEFUL = true; +pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6"; +pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; +pub const VDSO_GETCPU_VER = "LINUX_2.6"; + pub fn syscall0(number: usize) usize { return asm volatile ("syscall" : [ret] "={rax}" (-> usize) @@ -509,3 +516,4 @@ pub const dirent = extern struct { d_name: u8, // field address is the address of first byte of name }; +pub const Elf_Symndx = u32; diff --git a/std/os/time.zig b/std/os/time.zig index 8a906d7954..e9fbf9798c 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -160,13 +160,13 @@ pub const Timer = struct { Os.windows => { var freq: i64 = undefined; var err = windows.QueryPerformanceFrequency(&freq); - if (err == 0) return error.TimerUnsupported; + if (err == windows.FALSE) return error.TimerUnsupported; self.frequency = u64(freq); self.resolution = @divFloor(ns_per_s, self.frequency); var start_time: i64 = undefined; err = windows.QueryPerformanceCounter(&start_time); - debug.assert(err != 0); + debug.assert(err != windows.FALSE); self.start_time = u64(start_time); }, Os.linux => { @@ -236,7 +236,7 @@ pub const Timer = struct { fn clockWindows() u64 { var result: i64 = undefined; var err = windows.QueryPerformanceCounter(&result); - debug.assert(err != 0); + debug.assert(err != windows.FALSE); return u64(result); } @@ -285,4 +285,4 @@ test "os.time.Timer" { timer.reset(); debug.assert(timer.read() < time_1); -} \ No newline at end of file +} diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index d2c22c13e1..1dc7e24869 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn { fn posixCallMainAndExit() noreturn { const argc = *argc_ptr; const argv = @ptrCast(&&u8, &argc_ptr[1]); - const envp = @ptrCast(&?&u8, &argv[argc + 1]); + const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]); + var envp_count: usize = 0; + while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} + const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count]; + if (builtin.os == builtin.Os.linux) { + const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1]; + var i: usize = 0; + while (auxv[i] != 0) : (i += 2) { + if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1]; + } + std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size); + } + std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) u8 { +fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; - - var env_count: usize = 0; - while (envp[env_count] != null) : (env_count += 1) {} - std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count]; - + std.os.posix_environ_raw = envp; return callMain(); } extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 { - return callMainWithArgs(usize(c_argc), c_argv, c_envp); + var env_count: usize = 0; + while (c_envp[env_count] != null) : (env_count += 1) {} + const envp = @ptrCast(&&u8, c_envp)[0..env_count]; + return callMainWithArgs(usize(c_argc), c_argv, envp); } fn callMain() u8 { diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index 4cadabb728..d406285d29 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -49,3 +49,23 @@ fn testAtomicLoad(ptr: &u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); assert(x == 42); } + +test "cmpxchg with ptr" { + var data1: i32 = 1234; + var data2: i32 = 5678; + var data3: i32 = 9101; + var x: &i32 = &data1; + if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == &data1); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == &data1); + } + assert(x == &data3); + + assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(x == &data2); +} -- cgit v1.2.3 From 1c41f1ca6252faaa7750936c67562f1866a48075 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 20:54:52 -0400 Subject: better error reporting for missing libc on windows closes #931 --- src/analyze.cpp | 9 ++++++--- src/os.cpp | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index d142b86326..5dd7b0d18c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4433,7 +4433,8 @@ void find_libc_lib_path(CodeGen *g) { if (g->msvc_lib_dir == nullptr) { Buf* vc_lib_dir = buf_alloc(); if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { - zig_panic("Unable to determine vcruntime path."); + fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); + exit(1); } g->msvc_lib_dir = vc_lib_dir; } @@ -4441,7 +4442,8 @@ void find_libc_lib_path(CodeGen *g) { if (g->libc_lib_dir == nullptr) { Buf* ucrt_lib_path = buf_alloc(); if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine ucrt path."); + fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); + exit(1); } g->libc_lib_dir = ucrt_lib_path; } @@ -4449,7 +4451,8 @@ void find_libc_lib_path(CodeGen *g) { if (g->kernel32_lib_dir == nullptr) { Buf* kern_lib_path = buf_alloc(); if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - zig_panic("Unable to determine kernel32 path."); + fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); + exit(1); } g->kernel32_lib_dir = kern_lib_path; } diff --git a/src/os.cpp b/src/os.cpp index 97462bd658..d335d8d218 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1334,6 +1334,9 @@ com_done:; int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) + if (buf_len(sdk->path10) == 0 || buf_len(sdk->version10) == 0) { + return ErrorFileNotFound; + } buf_resize(output_buf, 0); buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); switch (platform_type) { -- cgit v1.2.3 From 25dff91fa099489858428a7071b8c76acf1f943d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 21:08:52 -0400 Subject: fix windows build broken by previous commit fixes build failure from 1c41f1ca6252faaa7750936c67562f1866a48075 --- src/os.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index d335d8d218..c93e2887b7 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1334,7 +1334,7 @@ com_done:; int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) - if (buf_len(sdk->path10) == 0 || buf_len(sdk->version10) == 0) { + if (buf_len(&sdk->path10) == 0 || buf_len(&sdk->version10) == 0) { return ErrorFileNotFound; } buf_resize(output_buf, 0); -- cgit v1.2.3 From 75328e32045d1275c03f1f37058ee0a3b775c632 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 21:47:25 -0400 Subject: exit(1) instead of abort() for file not found --- src/analyze.cpp | 6 ++++-- src/codegen.cpp | 6 ++++-- src/os.cpp | 3 --- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 5dd7b0d18c..a598d7676e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4306,7 +4306,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { if (g->win_sdk == nullptr) { if (os_find_windows_sdk(&g->win_sdk)) { - zig_panic("Unable to determine Windows SDK path."); + fprintf(stderr, "unable to determine windows sdk path\n"); + exit(1); } } assert(g->win_sdk != nullptr); @@ -4408,7 +4409,8 @@ void find_libc_include_path(CodeGen *g) { ZigWindowsSDK *sdk = get_windows_sdk(g); g->libc_include_dir = buf_alloc(); if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { - zig_panic("Unable to determine libc include path."); + fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); + exit(1); } } else if (g->zig_target.os == OsLinux) { g->libc_include_dir = get_linux_libc_include_path(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 4581c3e2b3..2d8c385f44 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6638,12 +6638,14 @@ static void gen_root_source(CodeGen *g) { Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(rel_full_path, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + exit(1); } Buf *source_code = buf_alloc(); if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { - zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + exit(1); } g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code); diff --git a/src/os.cpp b/src/os.cpp index c93e2887b7..97462bd658 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1334,9 +1334,6 @@ com_done:; int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) - if (buf_len(&sdk->path10) == 0 || buf_len(&sdk->version10) == 0) { - return ErrorFileNotFound; - } buf_resize(output_buf, 0); buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); switch (platform_type) { -- cgit v1.2.3 From 8503eff8c12697c35ab5c73d6651c4b996339706 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 22 Apr 2018 23:46:55 -0400 Subject: add compile error for invalid deref on switch target closes #945 --- src/ir.cpp | 5 ++++- test/compile_errors.zig | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 865a6823be..cd00fc6230 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14782,7 +14782,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, return out_val->type; } - assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer); + if (target_value_ptr->value.type->id != TypeTableEntryIdPointer) { + ir_add_error(ira, target_value_ptr, buf_sprintf("invalid deref on switch target")); + return ira->codegen->builtin_types.entry_invalid; + } TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; ConstExprValue *pointee_val = nullptr; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2c4e35c562..f8febc27b8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,21 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("invalid deref on switch target", + \\comptime { + \\ var tile = Tile.Empty; + \\ switch (*tile) { + \\ Tile.Empty => {}, + \\ Tile.Filled => {}, + \\ } + \\} + \\const Tile = enum { + \\ Empty, + \\ Filled, + \\}; + , + ".tmp_source.zig:3:13: error: invalid deref on switch target"); + cases.add("invalid field access in comptime", \\comptime { var x = doesnt_exist.whatever; } , -- cgit v1.2.3 From 89a4c373d3756baeac9d2780b1249ada37961d16 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Apr 2018 12:06:18 -0400 Subject: fix bigint twos complement implementation closes #948 --- src/bigint.cpp | 5 +++++ test/cases/eval.zig | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'src') diff --git a/src/bigint.cpp b/src/bigint.cpp index 85e5dad4ad..2a688debd5 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) size_t digits_to_copy = bit_count / 64; size_t leftover_bits = bit_count % 64; dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1); + if (dest->digit_count == 1 && leftover_bits == 0) { + dest->data.digit = op_digits[0]; + if (dest->data.digit == 0) dest->digit_count = 0; + return; + } dest->data.digits = allocate_nonzero(dest->digit_count); for (size_t i = 0; i < digits_to_copy; i += 1) { uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0; diff --git a/test/cases/eval.zig b/test/cases/eval.zig index d6f7afe864..e13d4340e7 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -513,3 +513,19 @@ test "array concat of slices gives slice" { assert(std.mem.eql(u8, c, "aoeuasdf")); } } + +test "comptime shlWithOverflow" { + const ct_shifted: u64 = comptime amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + const rt_shifted: u64 = amt: { + var amt = u64(0); + _ = @shlWithOverflow(u64, ~u64(0), 16, &amt); + break :amt amt; + }; + + assert(ct_shifted == rt_shifted); +} -- cgit v1.2.3 From 15bf0c1541479870dff1f8d64a3746c337f901ef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Apr 2018 18:06:33 -0400 Subject: fix interaction between defer and labeled break closes #830 --- src/ir.cpp | 3 +++ test/cases/defer.zig | 11 +++++++++++ 2 files changed, 14 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index cd00fc6230..86c77758b2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3147,6 +3147,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { return noreturn_return_value; } + + ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); + return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else { incoming_blocks.append(irb->current_basic_block); incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node))); diff --git a/test/cases/defer.zig b/test/cases/defer.zig index a989af18c2..5470b4bbd0 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void { assert(i == 5); } } + +test "defer and labeled break" { + var i = usize(0); + + blk: { + defer i += 1; + break :blk; + } + + assert(i == 1); +} -- cgit v1.2.3 From 7eab62325b539be4aacb17fd64f5b4445c8409e7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 01:49:22 +0300 Subject: One step towards @typeInfo --- src/all_types.hpp | 8 +++ src/codegen.cpp | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ir.cpp | 47 +++++++++++++++++ src/ir_print.cpp | 9 ++++ 4 files changed, 217 insertions(+) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..332fc5204e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1293,6 +1293,7 @@ enum BuiltinFnId { BuiltinFnIdMemberType, BuiltinFnIdMemberName, BuiltinFnIdField, + BuiltinFnIdTypeInfo, BuiltinFnIdTypeof, BuiltinFnIdAddWithOverflow, BuiltinFnIdSubWithOverflow, @@ -2037,6 +2038,7 @@ enum IrInstructionId { IrInstructionIdTagType, IrInstructionIdFieldParentPtr, IrInstructionIdOffsetOf, + IrInstructionIdTypeInfo, IrInstructionIdTypeId, IrInstructionIdSetEvalBranchQuota, IrInstructionIdPtrTypeOf, @@ -2858,6 +2860,12 @@ struct IrInstructionOffsetOf { IrInstruction *field_name; }; +struct IrInstructionTypeInfo { + IrInstruction base; + + IrInstruction *type_value; +}; + struct IrInstructionTypeId { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2d8c385f44..ea2d9f463e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4502,6 +4502,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdDeclRef: case IrInstructionIdSwitchVar: case IrInstructionIdOffsetOf: + case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdSetEvalBranchQuota: case IrInstructionIdPtrTypeOf: @@ -6125,6 +6126,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2); create_builtin_fn(g, BuiltinFnIdField, "field", 2); + create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); @@ -6344,6 +6346,157 @@ static void define_builtin_compile_vars(CodeGen *g) { } buf_appendf(contents, "};\n\n"); } + { + buf_appendf(contents, + "pub const IntInfo = struct {\n" + " is_signed: bool,\n" + " bits: u8,\n" + "};\n" + "\n" + "pub const FloatInfo = struct {\n" + " bits: u8,\n" + "};\n" + "\n" + "pub const PointerInfo = struct {\n" + " is_const: bool,\n" + " is_volatile: bool,\n" + " alignment: u32,\n" + " child: &TypeInfo,\n" + "};\n" + "\n" + "pub const ArrayInfo = struct {\n" + " len: u64,\n" + " child: &TypeInfo,\n" + "};\n" + "\n" + "pub const ContainerLayout = enum {\n" + " Auto,\n" + " Extern,\n" + " Packed,\n" + "};\n" + "\n" + "pub const StructFieldInfo = struct {\n" + " name: []const u8,\n" + " offset: usize,\n" + " type_info: TypeInfo,\n" + "};\n" + "\n" + "pub const StructInfo = struct {\n" + " layout: ContainerLayout,\n" + " fields: []StructFieldInfo,\n" + "};\n" + "\n" + "pub const NullableInfo = struct {\n" + " child: &TypeInfo,\n" + "};\n" + "\n" + "pub const ErrorUnionInfo = struct {\n" + " error_set: ErrorSetInfo,\n" + " payload: &TypeInfo,\n" + "};\n" + "\n" + "pub const ErrorInfo = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + "};\n" + "\n" + "pub const ErrorSetInfo = struct {\n" + " errors: []ErrorInfo,\n" + "};\n" + "\n" + "pub const EnumFieldInfo = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + "};\n" + "\n" + "pub const EnumInfo = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: IntInfo,\n" + " fields: []EnumFieldInfo,\n" + "};\n" + "\n" + "pub const UnionFieldInfo = struct {\n" + " name: []const u8,\n" + " enum_field: EnumFieldInfo,\n" + " type_info: TypeInfo,\n" + "};\n" + "\n" + "pub const UnionInfo = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: ?EnumInfo,\n" + " fields: []UnionFieldInfo,\n" + "};\n" + "\n" + "pub const CallingConvention = enum {\n" + " Unspecified,\n" + " C,\n" + " Cold,\n" + " Naked,\n" + " Stdcall,\n" + " Async,\n" + "};\n" + "\n" + "pub const FnArgInfo = struct {\n" + " is_comptime: bool,\n" + " name: []const u8,\n" + " type_info: TypeInfo,\n" + "};\n" + "\n" + "pub const FnInfo = struct {\n" + " calling_convention: CallingConvention,\n" + " is_generic: bool,\n" + " is_varargs: bool,\n" + " return_type: &TypeInfo,\n" + " args: []FnArgInfo,\n" + "};\n" + "\n" + "pub const BoundFnInfo = struct {\n" + " bound_type: &TypeInfo,\n" + " fn_info: FnInfo,\n" + "};\n" + "\n" + "pub const PromiseInfo = struct {\n" + " child: ?&TypeInfo,\n" + "};\n" + "\n" + "pub const TypeInfo = union(TypeId) {\n" + " Type: void,\n" + " Void: void,\n" + " Bool: void,\n" + " NoReturn: void,\n" + " Int: IntInfo,\n" + " Float: FloatInfo,\n" + " Pointer: PointerInfo,\n" + " Array: ArrayInfo,\n" + " Struct: StructInfo,\n" + " FloatLiteral: void,\n" + " IntLiteral: void,\n" + " UndefinedLiteral: void,\n" + " NullLiteral: void,\n" + " Nullable: NullableInfo,\n" + " ErrorUnion: ErrorUnionInfo,\n" + " ErrorSet: ErrorSetInfo,\n" + " Enum: EnumInfo,\n" + " Union: UnionInfo,\n" + " Fn: FnInfo,\n" + " Namespace: void,\n" + " Block: void,\n" + " BoundFn: BoundFnInfo,\n" + " ArgTuple: void,\n" + " Opaque: void,\n" + " Promise: PromiseInfo,\n" + "};\n\n"); + assert(ContainerLayoutAuto == 0); + assert(ContainerLayoutExtern == 1); + assert(ContainerLayoutPacked == 2); + + assert(CallingConventionUnspecified == 0); + assert(CallingConventionC == 1); + assert(CallingConventionCold == 2); + assert(CallingConventionNaked == 3); + assert(CallingConventionStdcall == 4); + assert(CallingConventionAsync == 5); + } { buf_appendf(contents, "pub const FloatMode = enum {\n" diff --git a/src/ir.cpp b/src/ir.cpp index cd00fc6230..69897f3b91 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -615,6 +615,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) { return IrInstructionIdOffsetOf; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { + return IrInstructionIdTypeInfo; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { return IrInstructionIdTypeId; } @@ -2440,6 +2444,16 @@ static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } +static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value) { + IrInstructionTypeInfo *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + + ir_ref_instruction(type_value, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { @@ -4080,6 +4094,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_load_ptr(irb, scope, node, ptr_instruction); } + case BuiltinFnIdTypeInfo: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_info, lval); + } case BuiltinFnIdBreakpoint: return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval); case BuiltinFnIdReturnAddress: @@ -15669,6 +15693,26 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } +static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, + IrInstructionTypeInfo *instruction) +{ + IrInstruction *type_value = instruction->type_value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_is_invalid(type_entry)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); + assert(var_value->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *result_type = var_value->data.x_type; + + // TODO: Check if we need to explicitely make a &const TypeInfo here, I think we don't. + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_struct.fields = create_const_vals(1); + // TODO: Fill the struct + zig_panic("Building TypeInfo..."); + return result_type; +} + static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { @@ -18555,6 +18599,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction); case IrInstructionIdOffsetOf: return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction); + case IrInstructionIdTypeInfo: + return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); case IrInstructionIdTypeId: return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); case IrInstructionIdSetEvalBranchQuota: @@ -18821,6 +18867,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdTagName: case IrInstructionIdFieldParentPtr: case IrInstructionIdOffsetOf: + case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdAlignCast: case IrInstructionIdOpaqueType: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a77ae244d4..9678120f1d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -966,6 +966,12 @@ static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction) fprintf(irp->f, ")"); } +static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) { + fprintf(irp->f, "@typeInfo("); + ir_print_other_instruction(irp, instruction->type_value); + fprintf(irp->f, ")"); +} + static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) { fprintf(irp->f, "@typeId("); ir_print_other_instruction(irp, instruction->type_value); @@ -1536,6 +1542,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdOffsetOf: ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction); break; + case IrInstructionIdTypeInfo: + ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); + break; case IrInstructionIdTypeId: ir_print_type_id(irp, (IrInstructionTypeId *)instruction); break; -- cgit v1.2.3 From fb88f5a0d21f68632a8c712e14a9640f9c4f6cb3 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 11:20:33 +0300 Subject: @typeInfo with void payloads now works! --- src/ir.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 69897f3b91..52136ec9c8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15705,11 +15705,32 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, assert(var_value->type->id == TypeTableEntryIdMetaType); TypeTableEntry *result_type = var_value->data.x_type; - // TODO: Check if we need to explicitely make a &const TypeInfo here, I think we don't. ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_struct.fields = create_const_vals(1); - // TODO: Fill the struct - zig_panic("Building TypeInfo..."); + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); + out_val->data.x_union.parent.id = ConstParentIdNone; + + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdArgTuple: + case TypeTableEntryIdOpaque: + // TODO: Check out this is the way to handle voids; + out_val->data.x_union.payload = nullptr; + break; + default: + zig_panic("@typeInfo unsupported for %s", buf_ptr(&type_entry->name)); + } + return result_type; } -- cgit v1.2.3 From ec2a3ed500d4ba269c87b3ad0efe7bcc89eb2057 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 15:03:46 +0300 Subject: Attempt at adding comptime union field access --- src/codegen.cpp | 1 + src/ir.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index ea2d9f463e..60a459f822 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6347,6 +6347,7 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { + // TODO: Add method info where methods are supported. buf_appendf(contents, "pub const IntInfo = struct {\n" " is_signed: bool,\n" diff --git a/src/ir.cpp b/src/ir.cpp index 52136ec9c8..2212efb101 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13473,6 +13473,41 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } else if (bare_type->id == TypeTableEntryIdUnion) { TypeUnionField *field = find_union_type_field(bare_type, field_name); if (field) { + if (instr_is_comptime(container_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ConstExprValue *union_val = const_ptr_pointee(ira->codegen, ptr_val); + if (type_is_invalid(union_val->type)) + return ira->codegen->invalid_instruction; + + TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); + if (actual_field == nullptr) + zig_unreachable(); + + if (field != actual_field) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), + buf_ptr(actual_field->name))); + return ira->codegen->invalid_instruction; + } + + ConstExprValue *payload_val = union_val->data.x_union.payload; + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, payload_val->type, is_const, is_volatile, + get_abi_alignment(ira->codegen, payload_val->type), 0, 0); + + IrInstruction *result = ir_get_const(ira, source_instr); + ConstExprValue *const_val = &result->value; + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut; + const_val->data.x_ptr.data.ref.pointee = payload_val; + const_val->type = ptr_type; + return result; + } + } + IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, get_abi_alignment(ira->codegen, field->type_entry), 0, 0); @@ -15706,8 +15741,12 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + // TODO: Do I need those? probably set in ir_build_const_from + //out_val->special = ConstValSpecialStatic; + //out_val->type = result_type; + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - out_val->data.x_union.parent.id = ConstParentIdNone; + //out_val->data.x_union.parent.id = ConstParentIdNone; switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -15724,9 +15763,34 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: - // TODO: Check out this is the way to handle voids; out_val->data.x_union.payload = nullptr; break; + case TypeTableEntryIdInt: + { + // Error from 'ir_resolve_const': "unable to evaluate constant expression" + ConstExprValue *payload = create_const_vals(1); + out_val->data.x_union.payload = payload; + + payload->special = ConstValSpecialStatic; + payload->type = get_builtin_value(ira->codegen, "IntInfo")->type; + + ConstExprValue *fields = create_const_vals(2); + payload->data.x_struct.fields = fields; + + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = out_val; + + // TODO: See what happens if we don't set the field type (set it to nullptr) + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_bool; + fields[0].data.x_bool = type_entry->data.integral.is_signed; + + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_u8; + bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); + + break; + } default: zig_panic("@typeInfo unsupported for %s", buf_ptr(&type_entry->name)); } -- cgit v1.2.3 From e9309d3b1310863d9571486b071b7a34bea1fb60 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 15:17:34 +0300 Subject: Fixed IntInfo generation. --- src/ir.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 2212efb101..39e48ac33b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15772,7 +15772,10 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, out_val->data.x_union.payload = payload; payload->special = ConstValSpecialStatic; - payload->type = get_builtin_value(ira->codegen, "IntInfo")->type; + + ConstExprValue *int_info_type = get_builtin_value(ira->codegen, "IntInfo"); + assert(int_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = int_info_type->data.x_type; ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; -- cgit v1.2.3 From 0e5fb035e3d12879b4bd7a23578fdb8932deb4d1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 16:23:22 +0300 Subject: Added (broken) pointer info, float info --- src/ir.cpp | 155 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 125 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 39e48ac33b..3ba9bc4772 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15728,27 +15728,13 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } -static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, - IrInstructionTypeInfo *instruction) +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, TypeTableEntry *type_entry) { - IrInstruction *type_value = instruction->type_value->other; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - if (type_is_invalid(type_entry)) - return ira->codegen->builtin_types.entry_invalid; + assert(type_entry != nullptr); + assert(!type_is_invalid(type_entry)); - ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); - assert(var_value->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *result_type = var_value->data.x_type; - - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - // TODO: Do I need those? probably set in ir_build_const_from - //out_val->special = ConstValSpecialStatic; - //out_val->type = result_type; - - bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - //out_val->data.x_union.parent.id = ConstParentIdNone; - - switch (type_entry->id) { + switch (type_entry->id) + { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: @@ -15763,14 +15749,11 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: - out_val->data.x_union.payload = nullptr; - break; + // TODO: Construct a valid void payload. + return nullptr; case TypeTableEntryIdInt: { - // Error from 'ir_resolve_const': "unable to evaluate constant expression" ConstExprValue *payload = create_const_vals(1); - out_val->data.x_union.payload = payload; - payload->special = ConstValSpecialStatic; ConstExprValue *int_info_type = get_builtin_value(ira->codegen, "IntInfo"); @@ -15780,23 +15763,135 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } - // TODO: See what happens if we don't set the field type (set it to nullptr) + // is_signed: bool fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; - + // bits: u8 fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - break; + return payload; + } + case TypeTableEntryIdFloat: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *float_info_type = get_builtin_value(ira->codegen, "FloatInfo"); + assert(float_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = float_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(1); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // bits: u8 + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_u8; + bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); + + return payload; + } + case TypeTableEntryIdPointer: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PointerInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(4); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // is_const: bool + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_bool; + fields[0].data.x_bool = type_entry->data.pointer.is_const; + // is_volatile: bool + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_bool; + fields[1].data.x_bool = type_entry->data.pointer.is_volatile; + // alignment: u32 + fields[2].special = ConstValSpecialStatic; + fields[2].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.pointer.alignment); + // child: &TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + fields[3].special = ConstValSpecialStatic; + fields[3].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[3].data.x_ptr.special = ConstPtrSpecialRef; + fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; + fields[3].data.x_ptr.data.ref.pointee = ir_make_type_info_value(ira, &fields[3], type_entry->data.pointer.child_type); + return payload; } default: - zig_panic("@typeInfo unsupported for %s", buf_ptr(&type_entry->name)); + zig_unreachable(); } +} + +static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, + IrInstructionTypeInfo *instruction) +{ + IrInstruction *type_value = instruction->type_value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_is_invalid(type_entry)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); + assert(var_value->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *result_type = var_value->data.x_type; + + // TODO: We need to return a const pointer to the typeinfo, not the typeinfo by value. + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->type = result_type; + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); + out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, type_entry); return result_type; } -- cgit v1.2.3 From 189e8e97bdd8dea8efa9ea30401b164ea619f2e4 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 16:50:36 +0300 Subject: PointerInfo child is a pointer to a TypeInfo union, still not working correctly --- src/ir.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 3ba9bc4772..86c718da81 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15867,7 +15867,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[3].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); fields[3].data.x_ptr.special = ConstPtrSpecialRef; fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; - fields[3].data.x_ptr.data.ref.pointee = ir_make_type_info_value(ira, &fields[3], type_entry->data.pointer.child_type); + ConstExprValue *union_val = create_const_vals(1); + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.pointer.child_type); + fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } default: -- cgit v1.2.3 From 2d8553c85333734a1690dfae30d881103dde0727 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 17:01:20 +0300 Subject: Fixed PointerInfo generation --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 86c718da81..e1208f03ad 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15859,7 +15859,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // alignment: u32 fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.pointer.alignment); + bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); -- cgit v1.2.3 From 09d7033d1db6ac0887aa21f87a2f96dad039f4e3 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 17:08:45 +0300 Subject: PointerInfo child is known at comptime --- src/ir.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index e1208f03ad..be61b0c280 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15868,6 +15868,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[3].data.x_ptr.special = ConstPtrSpecialRef; fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.pointer.child_type); -- cgit v1.2.3 From 182a9fad2d55973ab50f4a2170edb88d6708681d Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 24 Apr 2018 17:38:30 +0300 Subject: Added ArrayInfo, NullableInfo, PromiseInfo generation --- src/codegen.cpp | 2 +- src/ir.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 60a459f822..2e7cc937b4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6366,7 +6366,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "};\n" "\n" "pub const ArrayInfo = struct {\n" - " len: u64,\n" + " len: usize,\n" " child: &TypeInfo,\n" "};\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index be61b0c280..a00d98efd1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15875,6 +15875,151 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } + case TypeTableEntryIdArray: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "ArrayInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(2); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // len: usize + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); + // child: &TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + fields[1].special = ConstValSpecialStatic; + fields[1].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[1].data.x_ptr.special = ConstPtrSpecialRef; + fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; + ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.array.child_type); + fields[1].data.x_ptr.data.ref.pointee = union_val; + return payload; + } + case TypeTableEntryIdMaybe: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "NullableInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(1); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // child: &TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + fields[0].special = ConstValSpecialStatic; + fields[0].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[0].data.x_ptr.special = ConstPtrSpecialRef; + fields[0].data.x_ptr.mut = ConstPtrMutComptimeVar; + ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.maybe.child_type); + fields[0].data.x_ptr.data.ref.pointee = union_val; + return payload; + } + case TypeTableEntryIdPromise: + { + ConstExprValue *payload = create_const_vals(1); + payload->special = ConstValSpecialStatic; + + ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PromiseInfo"); + assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); + payload->type = pointer_info_type->data.x_type; + + ConstExprValue *fields = create_const_vals(1); + payload->data.x_struct.fields = fields; + + if (parent->type->id == TypeTableEntryIdStruct) + { + payload->data.x_struct.parent.id = ConstParentIdStruct; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else if (parent->type->id == TypeTableEntryIdUnion) + { + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = parent; + } + else + { + payload->data.x_struct.parent.id = ConstParentIdNone; + } + // child: ?&TypeInfo + ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + + fields[0].special = ConstValSpecialStatic; + fields[0].type = get_maybe_type(ira->codegen, type_info_ptr_type); + + if (type_entry->data.promise.result_type == nullptr) + { + fields[0].data.x_maybe = nullptr; + } + else + { + ConstExprValue *maybe_value = create_const_vals(1); + maybe_value->special = ConstValSpecialStatic; + maybe_value->type = type_info_ptr_type; + + maybe_value->data.x_ptr.special = ConstPtrSpecialRef; + maybe_value->data.x_ptr.mut = ConstPtrMutComptimeVar; + + ConstExprValue *union_val = create_const_vals(1); + union_val->special = ConstValSpecialStatic; + union_val->type = type_info_type->data.x_type; + bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.promise.result_type); + + maybe_value->data.x_ptr.data.ref.pointee = union_val; + fields[0].data.x_maybe = maybe_value; + } + return payload; + } default: zig_unreachable(); } -- cgit v1.2.3 From 778b931bf33c9b19502857af918fc159c0cd8e83 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 02:50:18 +0300 Subject: Fixed comptime union void field access --- src/ir.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index a00d98efd1..aa456f5030 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13495,8 +13495,18 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } ConstExprValue *payload_val = union_val->data.x_union.payload; - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, payload_val->type, is_const, is_volatile, - get_abi_alignment(ira->codegen, payload_val->type), 0, 0); + + TypeTableEntry *field_type = field->type_entry; + if (field_type->id == TypeTableEntryIdVoid) + { + assert(payload_val == nullptr); + payload_val = create_const_vals(1); + payload_val->special = ConstValSpecialStatic; + payload_val->type = field_type; + } + + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, + get_abi_alignment(ira->codegen, field_type), 0, 0); IrInstruction *result = ir_get_const(ira, source_instr); ConstExprValue *const_val = &result->value; @@ -15749,7 +15759,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: - // TODO: Construct a valid void payload. return nullptr; case TypeTableEntryIdInt: { -- cgit v1.2.3 From d68aea4f35fe935f3bf80c86dac4da7c4b88dc5b Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 11:35:46 +0300 Subject: Added checks for field name/index mapping in TypeInfo generation. Abstracted the parent setting out. --- src/ir.cpp | 139 +++++++++++++++++++++++-------------------------------------- 1 file changed, 53 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index aa456f5030..dee093c57e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13411,7 +13411,6 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type) { @@ -15738,6 +15737,37 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } +static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index) { + Buf *field_name_buf; + + assert(type != nullptr && !type_is_invalid(type)); + // Check for our field by creating a buffer in place then using the comma operator to free it so that we don't + // leak memory in debug mode. + assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index && + (buf_deinit(field_name_buf), true)); +} + +static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent) { + assert(struct_val->type->id == TypeTableEntryIdStruct); + assert(parent->type != nullptr && !type_is_invalid(parent->type)); + + switch (parent->type->id) + { + case TypeTableEntryIdArray: + zig_panic("TODO - Only expected struct or union parent."); + case TypeTableEntryIdStruct: + struct_val->data.x_struct.parent.id = ConstParentIdStruct; + struct_val->data.x_struct.parent.data.p_union.union_val = parent; + break; + case TypeTableEntryIdUnion: + struct_val->data.x_struct.parent.id = ConstParentIdUnion; + struct_val->data.x_struct.parent.data.p_union.union_val = parent; + break; + default: + struct_val->data.x_struct.parent.id = ConstParentIdNone; + } +} + static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, TypeTableEntry *type_entry) { assert(type_entry != nullptr); @@ -15772,26 +15802,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); // is_signed: bool + ensure_field_index(payload->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; // bits: u8 + ensure_field_index(payload->type, "bits", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); @@ -15810,21 +15829,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // bits: u8 + ensure_field_index(payload->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); @@ -15843,33 +15851,25 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(4); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // is_const: bool + ensure_field_index(payload->type, "is_const", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.pointer.is_const; // is_volatile: bool + ensure_field_index(payload->type, "is_volatile", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; fields[1].data.x_bool = type_entry->data.pointer.is_volatile; // alignment: u32 + ensure_field_index(payload->type, "alignment", 2); fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo + ensure_field_index(payload->type, "child", 3); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); fields[3].special = ConstValSpecialStatic; @@ -15896,25 +15896,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // len: usize + ensure_field_index(payload->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo + ensure_field_index(payload->type, "child", 1); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); fields[1].special = ConstValSpecialStatic; @@ -15941,21 +15931,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // child: &TypeInfo + ensure_field_index(payload->type, "child", 0); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); fields[0].special = ConstValSpecialStatic; @@ -15982,21 +15961,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - if (parent->type->id == TypeTableEntryIdStruct) - { - payload->data.x_struct.parent.id = ConstParentIdStruct; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else if (parent->type->id == TypeTableEntryIdUnion) - { - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = parent; - } - else - { - payload->data.x_struct.parent.id = ConstParentIdNone; - } + ir_type_info_struct_set_parent(payload, parent); + // child: ?&TypeInfo + ensure_field_index(payload->type, "child", 0); ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_type->type->id == TypeTableEntryIdMetaType); @@ -16046,7 +16014,6 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, assert(var_value->type->id == TypeTableEntryIdMetaType); TypeTableEntry *result_type = var_value->data.x_type; - // TODO: We need to return a const pointer to the typeinfo, not the typeinfo by value. ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); -- cgit v1.2.3 From 2606993cb48e69015a9152b860e48fca29d330e1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 11:59:35 +0300 Subject: Fixed ir_type_info_struct_set_parent for struct parents. --- src/ir.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index dee093c57e..a68747fce4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15747,7 +15747,7 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent) { +static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) { assert(struct_val->type->id == TypeTableEntryIdStruct); assert(parent->type != nullptr && !type_is_invalid(parent->type)); @@ -15756,10 +15756,13 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr case TypeTableEntryIdArray: zig_panic("TODO - Only expected struct or union parent."); case TypeTableEntryIdStruct: + assert(parent_field_index >= 0); struct_val->data.x_struct.parent.id = ConstParentIdStruct; - struct_val->data.x_struct.parent.data.p_union.union_val = parent; + struct_val->data.x_struct.parent.data.p_struct.struct_val = parent; + struct_val->data.x_struct.parent.data.p_struct.field_index = parent_field_index; break; case TypeTableEntryIdUnion: + assert(parent_field_index == -1); struct_val->data.x_struct.parent.id = ConstParentIdUnion; struct_val->data.x_struct.parent.data.p_union.union_val = parent; break; @@ -15768,7 +15771,8 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr } } -static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, TypeTableEntry *type_entry) +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, + ssize_t parent_field_index, TypeTableEntry *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -15802,7 +15806,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // is_signed: bool ensure_field_index(payload->type, "is_signed", 0); @@ -15829,7 +15833,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // bits: u8 ensure_field_index(payload->type, "bits", 0); @@ -15851,7 +15855,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(4); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // is_const: bool ensure_field_index(payload->type, "is_const", 0); @@ -15880,7 +15884,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.pointer.child_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.pointer.child_type); fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15896,7 +15900,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // len: usize ensure_field_index(payload->type, "len", 0); @@ -15915,7 +15919,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.array.child_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.array.child_type); fields[1].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15931,7 +15935,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // child: &TypeInfo ensure_field_index(payload->type, "child", 0); @@ -15945,7 +15949,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.maybe.child_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.maybe.child_type); fields[0].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15961,7 +15965,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent); + ir_type_info_struct_set_parent(payload, parent, parent_field_index); // child: ?&TypeInfo ensure_field_index(payload->type, "child", 0); @@ -15990,7 +15994,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, type_entry->data.promise.result_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.promise.result_type); maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; @@ -16017,7 +16021,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, type_entry); + out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, -1, type_entry); return result_type; } -- cgit v1.2.3 From bc160821d33a9284cf8bb85cca6cbf161af71d3b Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 25 Apr 2018 17:50:11 +0300 Subject: Changed TypeInfo layout. --- src/codegen.cpp | 236 ++++++++++++++++++++++++++++---------------------------- src/ir.cpp | 89 +++++++++++++-------- 2 files changed, 174 insertions(+), 151 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 2e7cc937b4..7d83a38bbf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6349,143 +6349,143 @@ static void define_builtin_compile_vars(CodeGen *g) { { // TODO: Add method info where methods are supported. buf_appendf(contents, - "pub const IntInfo = struct {\n" - " is_signed: bool,\n" - " bits: u8,\n" - "};\n" - "\n" - "pub const FloatInfo = struct {\n" - " bits: u8,\n" - "};\n" + "pub const TypeInfo = union(TypeId) {\n" + " Type: void,\n" + " Void: void,\n" + " Bool: void,\n" + " NoReturn: void,\n" + " Int: Int,\n" + " Float: Float,\n" + " Pointer: Pointer,\n" + " Array: Array,\n" + " Struct: Struct,\n" + " FloatLiteral: void,\n" + " IntLiteral: void,\n" + " UndefinedLiteral: void,\n" + " NullLiteral: void,\n" + " Nullable: Nullable,\n" + " ErrorUnion: ErrorUnion,\n" + " ErrorSet: ErrorSet,\n" + " Enum: Enum,\n" + " Union: Union,\n" + " Fn: Fn,\n" + " Namespace: void,\n" + " Block: void,\n" + " BoundFn: BoundFn,\n" + " ArgTuple: void,\n" + " Opaque: void,\n" + " Promise: Promise,\n" + "\n\n" + " pub const Int = struct {\n" + " is_signed: bool,\n" + " bits: u8,\n" + " };\n" "\n" - "pub const PointerInfo = struct {\n" - " is_const: bool,\n" - " is_volatile: bool,\n" - " alignment: u32,\n" - " child: &TypeInfo,\n" - "};\n" + " pub const Float = struct {\n" + " bits: u8,\n" + " };\n" "\n" - "pub const ArrayInfo = struct {\n" - " len: usize,\n" - " child: &TypeInfo,\n" - "};\n" + " pub const Pointer = struct {\n" + " is_const: bool,\n" + " is_volatile: bool,\n" + " alignment: u32,\n" + " child: &TypeInfo,\n" + " };\n" "\n" - "pub const ContainerLayout = enum {\n" - " Auto,\n" - " Extern,\n" - " Packed,\n" - "};\n" + " pub const Array = struct {\n" + " len: usize,\n" + " child: &TypeInfo,\n" + " };\n" "\n" - "pub const StructFieldInfo = struct {\n" - " name: []const u8,\n" - " offset: usize,\n" - " type_info: TypeInfo,\n" - "};\n" + " pub const ContainerLayout = enum {\n" + " Auto,\n" + " Extern,\n" + " Packed,\n" + " };\n" "\n" - "pub const StructInfo = struct {\n" - " layout: ContainerLayout,\n" - " fields: []StructFieldInfo,\n" - "};\n" + " pub const StructField = struct {\n" + " name: []const u8,\n" + " offset: usize,\n" + " type_info: TypeInfo,\n" + " };\n" "\n" - "pub const NullableInfo = struct {\n" - " child: &TypeInfo,\n" - "};\n" + " pub const Struct = struct {\n" + " layout: ContainerLayout,\n" + " fields: []StructField,\n" + " };\n" "\n" - "pub const ErrorUnionInfo = struct {\n" - " error_set: ErrorSetInfo,\n" - " payload: &TypeInfo,\n" - "};\n" + " pub const Nullable = struct {\n" + " child: &TypeInfo,\n" + " };\n" "\n" - "pub const ErrorInfo = struct {\n" - " name: []const u8,\n" - " value: usize,\n" - "};\n" + " pub const ErrorUnion = struct {\n" + " error_set: ErrorSet,\n" + " payload: &TypeInfo,\n" + " };\n" "\n" - "pub const ErrorSetInfo = struct {\n" - " errors: []ErrorInfo,\n" - "};\n" + " pub const Error = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + " };\n" "\n" - "pub const EnumFieldInfo = struct {\n" - " name: []const u8,\n" - " value: usize,\n" - "};\n" + " pub const ErrorSet = struct {\n" + " errors: []Error,\n" + " };\n" "\n" - "pub const EnumInfo = struct {\n" - " layout: ContainerLayout,\n" - " tag_type: IntInfo,\n" - " fields: []EnumFieldInfo,\n" - "};\n" + " pub const EnumField = struct {\n" + " name: []const u8,\n" + " value: usize,\n" + " };\n" "\n" - "pub const UnionFieldInfo = struct {\n" - " name: []const u8,\n" - " enum_field: EnumFieldInfo,\n" - " type_info: TypeInfo,\n" - "};\n" + " pub const Enum = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: Int,\n" + " fields: []EnumField,\n" + " };\n" "\n" - "pub const UnionInfo = struct {\n" - " layout: ContainerLayout,\n" - " tag_type: ?EnumInfo,\n" - " fields: []UnionFieldInfo,\n" - "};\n" + " pub const UnionField = struct {\n" + " name: []const u8,\n" + " enum_field: EnumField,\n" + " type_info: TypeInfo,\n" + " };\n" "\n" - "pub const CallingConvention = enum {\n" - " Unspecified,\n" - " C,\n" - " Cold,\n" - " Naked,\n" - " Stdcall,\n" - " Async,\n" - "};\n" + " pub const Union = struct {\n" + " layout: ContainerLayout,\n" + " tag_type: ?Enum,\n" + " fields: []UnionField,\n" + " };\n" "\n" - "pub const FnArgInfo = struct {\n" - " is_comptime: bool,\n" - " name: []const u8,\n" - " type_info: TypeInfo,\n" - "};\n" + " pub const CallingConvention = enum {\n" + " Unspecified,\n" + " C,\n" + " Cold,\n" + " Naked,\n" + " Stdcall,\n" + " Async,\n" + " };\n" "\n" - "pub const FnInfo = struct {\n" - " calling_convention: CallingConvention,\n" - " is_generic: bool,\n" - " is_varargs: bool,\n" - " return_type: &TypeInfo,\n" - " args: []FnArgInfo,\n" - "};\n" + " pub const FnArg = struct {\n" + " is_comptime: bool,\n" + " name: []const u8,\n" + " type_info: TypeInfo,\n" + " };\n" "\n" - "pub const BoundFnInfo = struct {\n" - " bound_type: &TypeInfo,\n" - " fn_info: FnInfo,\n" - "};\n" + " pub const Fn = struct {\n" + " calling_convention: CallingConvention,\n" + " is_generic: bool,\n" + " is_varargs: bool,\n" + " return_type: &TypeInfo,\n" + " args: []FnArg,\n" + " };\n" "\n" - "pub const PromiseInfo = struct {\n" - " child: ?&TypeInfo,\n" - "};\n" + " pub const BoundFn = struct {\n" + " bound_type: &TypeInfo,\n" + " fn_info: Fn,\n" + " };\n" "\n" - "pub const TypeInfo = union(TypeId) {\n" - " Type: void,\n" - " Void: void,\n" - " Bool: void,\n" - " NoReturn: void,\n" - " Int: IntInfo,\n" - " Float: FloatInfo,\n" - " Pointer: PointerInfo,\n" - " Array: ArrayInfo,\n" - " Struct: StructInfo,\n" - " FloatLiteral: void,\n" - " IntLiteral: void,\n" - " UndefinedLiteral: void,\n" - " NullLiteral: void,\n" - " Nullable: NullableInfo,\n" - " ErrorUnion: ErrorUnionInfo,\n" - " ErrorSet: ErrorSetInfo,\n" - " Enum: EnumInfo,\n" - " Union: UnionInfo,\n" - " Fn: FnInfo,\n" - " Namespace: void,\n" - " Block: void,\n" - " BoundFn: BoundFnInfo,\n" - " ArgTuple: void,\n" - " Opaque: void,\n" - " Promise: PromiseInfo,\n" + " pub const Promise = struct {\n" + " child: ?&TypeInfo,\n" + " };\n" "};\n\n"); assert(ContainerLayoutAuto == 0); assert(ContainerLayoutExtern == 1); diff --git a/src/ir.cpp b/src/ir.cpp index a68747fce4..1f2ef0e68f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15737,7 +15737,8 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_num_lit_int; } -static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index) { +static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index) +{ Buf *field_name_buf; assert(type != nullptr && !type_is_invalid(type)); @@ -15747,7 +15748,8 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) { +static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) +{ assert(struct_val->type->id == TypeTableEntryIdStruct); assert(parent->type != nullptr && !type_is_invalid(parent->type)); @@ -15771,6 +15773,37 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr } } +static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) +{ + ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_var->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = type_info_var->data.x_type; + assert(type_info_type->id == TypeTableEntryIdUnion); + + if (type_name == nullptr) + return type_info_type; + + // @TODO + + ScopeDecls *type_info_scope = get_container_scope(type_info_type); + assert(type_info_scope != nullptr); + + Buf field_name = BUF_INIT; + buf_init_from_str(&field_name, type_name); + auto entry = type_info_scope->decl_table.maybe_get(&field_name); + buf_deinit(&field_name); + assert(entry != nullptr); + + TldVar *tld = (TldVar *)entry->value; + assert(tld->base.id == TldIdVar); + + VariableTableEntry *var = tld->var; + + assert(var->value->type->id == TypeTableEntryIdMetaType); + return var->value->data.x_type; +} + static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, ssize_t parent_field_index, TypeTableEntry *type_entry) { @@ -15798,10 +15831,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *int_info_type = get_builtin_value(ira->codegen, "IntInfo"); - assert(int_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = int_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Int"); ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; @@ -15825,10 +15855,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *float_info_type = get_builtin_value(ira->codegen, "FloatInfo"); - assert(float_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = float_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Float"); ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; @@ -15847,10 +15874,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PointerInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Pointer"); ConstExprValue *fields = create_const_vals(4); payload->data.x_struct.fields = fields; @@ -15884,7 +15908,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.pointer.child_type); + + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.pointer.child_type); + fields[3].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15892,10 +15919,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "ArrayInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Array"); ConstExprValue *fields = create_const_vals(2); payload->data.x_struct.fields = fields; @@ -15919,7 +15943,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.array.child_type); + + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.array.child_type); + fields[1].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15927,10 +15954,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "NullableInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Nullable"); ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; @@ -15949,7 +15973,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.maybe.child_type); + + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.maybe.child_type); + fields[0].data.x_ptr.data.ref.pointee = union_val; return payload; } @@ -15957,10 +15984,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p { ConstExprValue *payload = create_const_vals(1); payload->special = ConstValSpecialStatic; - - ConstExprValue *pointer_info_type = get_builtin_value(ira->codegen, "PromiseInfo"); - assert(pointer_info_type->type->id == TypeTableEntryIdMetaType); - payload->type = pointer_info_type->data.x_type; + payload->type = ir_type_info_get_type(ira, "Promise"); ConstExprValue *fields = create_const_vals(1); payload->data.x_struct.fields = fields; @@ -15994,7 +16018,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p union_val->special = ConstValSpecialStatic; union_val->type = type_info_type->data.x_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.promise.result_type); + union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, + type_entry->data.promise.result_type); maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; @@ -16014,9 +16039,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeInfo"); - assert(var_value->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *result_type = var_value->data.x_type; + TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; -- cgit v1.2.3 From dd88d7deda66a4e4e1527831e7b24a3cf358d1b7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 13:27:16 +0300 Subject: Cleanup --- src/ir.cpp | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 1f2ef0e68f..cbd8d51b32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15775,17 +15775,20 @@ static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExpr static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) { - ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_var->type->id == TypeTableEntryIdMetaType); + static ConstExprValue *type_info_var = nullptr; + static TypeTableEntry *type_info_type = nullptr; + if (type_info_var == nullptr) + { + type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); + assert(type_info_var->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *type_info_type = type_info_var->data.x_type; - assert(type_info_type->id == TypeTableEntryIdUnion); + type_info_type = type_info_var->data.x_type; + assert(type_info_type->id == TypeTableEntryIdUnion); + } if (type_name == nullptr) return type_info_type; - // @TODO - ScopeDecls *type_info_scope = get_container_scope(type_info_type); assert(type_info_scope != nullptr); @@ -15898,15 +15901,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo ensure_field_index(payload->type, "child", 3); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + fields[3].special = ConstValSpecialStatic; - fields[3].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[3].type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[3].data.x_ptr.special = ConstPtrSpecialRef; fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, @@ -15933,15 +15937,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo ensure_field_index(payload->type, "child", 1); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + fields[1].special = ConstValSpecialStatic; - fields[1].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[1].type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[1].data.x_ptr.special = ConstPtrSpecialRef; fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, @@ -15963,15 +15968,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // child: &TypeInfo ensure_field_index(payload->type, "child", 0); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); + + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + fields[0].special = ConstValSpecialStatic; - fields[0].type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + fields[0].type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[0].data.x_ptr.special = ConstPtrSpecialRef; fields[0].data.x_ptr.mut = ConstPtrMutComptimeVar; ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, @@ -15993,10 +15999,9 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // child: ?&TypeInfo ensure_field_index(payload->type, "child", 0); - ConstExprValue *type_info_type = get_builtin_value(ira->codegen, "TypeInfo"); - assert(type_info_type->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type->data.x_type, false); + TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); + TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type, false); fields[0].special = ConstValSpecialStatic; fields[0].type = get_maybe_type(ira->codegen, type_info_ptr_type); @@ -16016,7 +16021,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p ConstExprValue *union_val = create_const_vals(1); union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type->data.x_type; + union_val->type = type_info_type; bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, type_entry->data.promise.result_type); -- cgit v1.2.3 From bb56360bfaa317809bd3c742a655b357bd5b6226 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 14:03:19 +0300 Subject: Added TypeInfo cache --- src/all_types.hpp | 1 + src/codegen.cpp | 10 ++++- src/ir.cpp | 109 ++++++++++++++++++++++++++++++------------------------ 3 files changed, 70 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 332fc5204e..c1c6c9a1a5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1507,6 +1507,7 @@ struct CodeGen { HashMap exported_symbol_names; HashMap external_prototypes; HashMap string_literals_table; + HashMap type_info_cache; ZigList import_queue; diff --git a/src/codegen.cpp b/src/codegen.cpp index 7d83a38bbf..71df7fcd65 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -88,6 +88,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->exported_symbol_names.init(8); g->external_prototypes.init(8); g->string_literals_table.init(16); + g->type_info_cache.init(32); g->is_test_build = false; g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); @@ -6347,7 +6348,8 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { - // TODO: Add method info where methods are supported. + // @TODO Add method info where methods are supported. + // @TODO Add Namespace info. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" " Type: void,\n" @@ -6403,6 +6405,11 @@ static void define_builtin_compile_vars(CodeGen *g) { " Packed,\n" " };\n" "\n" + " pub const Method = struct {\n" + " name: []const u8,\n" + " fn_info: Fn,\n" + " };\n" + "\n" " pub const StructField = struct {\n" " name: []const u8,\n" " offset: usize,\n" @@ -6412,6 +6419,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const Struct = struct {\n" " layout: ContainerLayout,\n" " fields: []StructField,\n" + " methods: []Method,\n" " };\n" "\n" " pub const Nullable = struct {\n" diff --git a/src/ir.cpp b/src/ir.cpp index cbd8d51b32..9229e4e89d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15810,6 +15810,15 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, ssize_t parent_field_index, TypeTableEntry *type_entry) { + // Lookup an available value in our cache. + auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); + if (entry != nullptr) + return entry->value; + + ConstExprValue *result = nullptr; + + // @TODO + // We should probably cache the values generated with a type_entry key. assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -15832,75 +15841,73 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p return nullptr; case TypeTableEntryIdInt: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Int"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Int"); ConstExprValue *fields = create_const_vals(2); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // is_signed: bool - ensure_field_index(payload->type, "is_signed", 0); + ensure_field_index(result->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.integral.is_signed; // bits: u8 - ensure_field_index(payload->type, "bits", 1); + ensure_field_index(result->type, "bits", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - - return payload; + break; } case TypeTableEntryIdFloat: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Float"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Float"); ConstExprValue *fields = create_const_vals(1); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // bits: u8 - ensure_field_index(payload->type, "bits", 0); + ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); - - return payload; + break; } case TypeTableEntryIdPointer: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Pointer"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Pointer"); ConstExprValue *fields = create_const_vals(4); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // is_const: bool - ensure_field_index(payload->type, "is_const", 0); + ensure_field_index(result->type, "is_const", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_bool; fields[0].data.x_bool = type_entry->data.pointer.is_const; // is_volatile: bool - ensure_field_index(payload->type, "is_volatile", 1); + ensure_field_index(result->type, "is_volatile", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; fields[1].data.x_bool = type_entry->data.pointer.is_volatile; // alignment: u32 - ensure_field_index(payload->type, "alignment", 2); + ensure_field_index(result->type, "alignment", 2); fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); // child: &TypeInfo - ensure_field_index(payload->type, "child", 3); + ensure_field_index(result->type, "child", 3); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); @@ -15917,26 +15924,26 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.pointer.child_type); fields[3].data.x_ptr.data.ref.pointee = union_val; - return payload; + break; } case TypeTableEntryIdArray: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Array"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Array"); ConstExprValue *fields = create_const_vals(2); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // len: usize - ensure_field_index(payload->type, "len", 0); + ensure_field_index(result->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo - ensure_field_index(payload->type, "child", 1); + ensure_field_index(result->type, "child", 1); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); @@ -15953,21 +15960,21 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.array.child_type); fields[1].data.x_ptr.data.ref.pointee = union_val; - return payload; + break; } case TypeTableEntryIdMaybe: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Nullable"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Nullable"); ConstExprValue *fields = create_const_vals(1); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // child: &TypeInfo - ensure_field_index(payload->type, "child", 0); + ensure_field_index(result->type, "child", 0); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); @@ -15984,21 +15991,21 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.maybe.child_type); fields[0].data.x_ptr.data.ref.pointee = union_val; - return payload; + break; } case TypeTableEntryIdPromise: { - ConstExprValue *payload = create_const_vals(1); - payload->special = ConstValSpecialStatic; - payload->type = ir_type_info_get_type(ira, "Promise"); + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Promise"); ConstExprValue *fields = create_const_vals(1); - payload->data.x_struct.fields = fields; + result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(payload, parent, parent_field_index); + ir_type_info_struct_set_parent(result, parent, parent_field_index); // child: ?&TypeInfo - ensure_field_index(payload->type, "child", 0); + ensure_field_index(result->type, "child", 0); TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type, false); @@ -16029,11 +16036,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; } - return payload; + break; } default: zig_unreachable(); } + + assert(result != nullptr); + ira->codegen->type_info_cache.put(type_entry, result); + return result; } static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, -- cgit v1.2.3 From 7a91e4736a4151bd41b65b6e3b395cc51c443162 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 14:29:27 +0300 Subject: Reset parent on cached TypeInfo values if we need to. --- src/codegen.cpp | 3 ++- src/ir.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 71df7fcd65..a8c32044fc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6348,7 +6348,6 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { - // @TODO Add method info where methods are supported. // @TODO Add Namespace info. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" @@ -6449,6 +6448,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: Int,\n" " fields: []EnumField,\n" + " methods: []Method,\n" " };\n" "\n" " pub const UnionField = struct {\n" @@ -6461,6 +6461,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: ?Enum,\n" " fields: []UnionField,\n" + " methods: []Method,\n" " };\n" "\n" " pub const CallingConvention = enum {\n" diff --git a/src/ir.cpp b/src/ir.cpp index 9229e4e89d..5cffb882f2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15810,17 +15810,61 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, ssize_t parent_field_index, TypeTableEntry *type_entry) { + assert(type_entry != nullptr); + assert(!type_is_invalid(type_entry)); + // Lookup an available value in our cache. auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); if (entry != nullptr) - return entry->value; + { + // Override the parent if we need to. + ConstExprValue *result = entry->value; - ConstExprValue *result = nullptr; + assert(result->type->id == TypeTableEntryIdStruct); - // @TODO - // We should probably cache the values generated with a type_entry key. - assert(type_entry != nullptr); - assert(!type_is_invalid(type_entry)); + ConstParent *curr_parent = &result->data.x_struct.parent; + if (curr_parent->id == ConstParentIdStruct) + { + if (curr_parent->data.p_struct.struct_val == parent && + parent_field_index != -1 && + curr_parent->data.p_struct.field_index == (size_t)parent_field_index) + { + return result; + } + ConstExprValue *new_result = create_const_vals(1); + copy_const_val(new_result, result, true); + ir_type_info_struct_set_parent(new_result, parent, parent_field_index); + return new_result; + } + else if (curr_parent->id == ConstParentIdUnion) + { + if (curr_parent->data.p_union.union_val == parent) + { + return result; + } + ConstExprValue *new_result = create_const_vals(1); + copy_const_val(new_result, result, true); + ir_type_info_struct_set_parent(new_result, parent, parent_field_index); + return new_result; + } + else if (curr_parent->id == ConstParentIdNone) + { + if (parent->type->id != TypeTableEntryIdStruct && + parent->type->id != TypeTableEntryIdArray && + parent->type->id != TypeTableEntryIdUnion) + { + return result; + } + ConstExprValue *new_result = create_const_vals(1); + copy_const_val(new_result, result, true); + ir_type_info_struct_set_parent(new_result, parent, parent_field_index); + return new_result; + } + + return result; + } + + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -16042,6 +16086,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p zig_unreachable(); } + // Cache the returned value. assert(result != nullptr); ira->codegen->type_info_cache.put(type_entry, result); return result; -- cgit v1.2.3 From f5977f68ebe344ec9c96507322218b87c24804a1 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 16:41:59 +0300 Subject: Added Enum TypeInfo except for methods --- src/codegen.cpp | 8 ++-- src/ir.cpp | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 111 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index a8c32044fc..9c4e5b1555 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6412,7 +6412,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const StructField = struct {\n" " name: []const u8,\n" " offset: usize,\n" - " type_info: TypeInfo,\n" + " type_info: &TypeInfo,\n" " };\n" "\n" " pub const Struct = struct {\n" @@ -6446,7 +6446,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const Enum = struct {\n" " layout: ContainerLayout,\n" - " tag_type: Int,\n" + " tag_type: &Int,\n" " fields: []EnumField,\n" " methods: []Method,\n" " };\n" @@ -6454,7 +6454,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const UnionField = struct {\n" " name: []const u8,\n" " enum_field: EnumField,\n" - " type_info: TypeInfo,\n" + " type_info: &TypeInfo,\n" " };\n" "\n" " pub const Union = struct {\n" @@ -6476,7 +6476,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const FnArg = struct {\n" " is_comptime: bool,\n" " name: []const u8,\n" - " type_info: TypeInfo,\n" + " type_info: &TypeInfo,\n" " };\n" "\n" " pub const Fn = struct {\n" diff --git a/src/ir.cpp b/src/ir.cpp index 5cffb882f2..9ca95d6222 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15864,7 +15864,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p return result; } - ConstExprValue *result = nullptr; + // If the value is not present in the cache, we will build it, add it and return it. + // We add values to the cache eagerly, as soon as we have filled out the root object's fields. + // That way, if we need to fetch the value in a recursive call down the line, even if we need to + // copy the value and reajust the parent, the value we get back still points to child values that + // will be filled later. switch (type_entry->id) { @@ -15885,7 +15889,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p return nullptr; case TypeTableEntryIdInt: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int"); @@ -15893,6 +15897,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // is_signed: bool ensure_field_index(result->type, "is_signed", 0); @@ -15904,11 +15909,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - break; + + return result; } case TypeTableEntryIdFloat: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float"); @@ -15916,17 +15922,19 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // bits: u8 ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); - break; + + return result; } case TypeTableEntryIdPointer: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Pointer"); @@ -15934,6 +15942,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // is_const: bool ensure_field_index(result->type, "is_const", 0); @@ -15968,11 +15977,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.pointer.child_type); fields[3].data.x_ptr.data.ref.pointee = union_val; - break; + + return result; } case TypeTableEntryIdArray: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array"); @@ -15980,6 +15990,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // len: usize ensure_field_index(result->type, "len", 0); @@ -15988,7 +15999,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); // child: &TypeInfo ensure_field_index(result->type, "child", 1); - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); fields[1].special = ConstValSpecialStatic; @@ -16004,11 +16014,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.array.child_type); fields[1].data.x_ptr.data.ref.pointee = union_val; - break; + + return result; } case TypeTableEntryIdMaybe: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Nullable"); @@ -16016,6 +16027,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // child: &TypeInfo ensure_field_index(result->type, "child", 0); @@ -16035,11 +16047,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p type_entry->data.maybe.child_type); fields[0].data.x_ptr.data.ref.pointee = union_val; - break; + + return result; } case TypeTableEntryIdPromise: { - result = create_const_vals(1); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Promise"); @@ -16047,6 +16060,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p result->data.x_struct.fields = fields; ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); // child: ?&TypeInfo ensure_field_index(result->type, "child", 0); @@ -16080,16 +16094,91 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p maybe_value->data.x_ptr.data.ref.pointee = union_val; fields[0].data.x_maybe = maybe_value; } - break; + + return result; + } + case TypeTableEntryIdEnum: + { + ConstExprValue *result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Enum"); + + ConstExprValue *fields = create_const_vals(4); + result->data.x_struct.fields = fields; + + ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout); + // tag_type: &TypeInfo.Int + ensure_field_index(result->type, "tag_type", 1); + + TypeTableEntry *type_info_int_type = ir_type_info_get_type(ira, "Int"); + + fields[1].special = ConstValSpecialStatic; + fields[1].type = get_pointer_to_type(ira->codegen, type_info_int_type, false); + fields[1].data.x_ptr.special = ConstPtrSpecialRef; + fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; + + ConstExprValue *tag_type_info_struct = ir_make_type_info_value(ira, &fields[1], -1, + type_entry->data.enumeration.tag_int_type); + assert(tag_type_info_struct->type == type_info_int_type); + fields[1].data.x_ptr.data.ref.pointee = tag_type_info_struct; + // fields: []TypeInfo.EnumField + ensure_field_index(result->type, "fields", 2); + + TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) + // ensure_field_index(type_info_enum_field_type, "name", 0); + // ensure_field_index(type_info_enum_field_type, "value", 1); + + uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; + + ConstExprValue *enum_field_array = create_const_vals(1); + enum_field_array->special = ConstValSpecialStatic; + enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); + enum_field_array->data.x_array.special = ConstArraySpecialNone; + enum_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; + enum_field_array->data.x_array.s_none.elements = create_const_vals(enum_field_count); + + init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); + + for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) + { + TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; + ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; + + enum_field_val->special = ConstValSpecialStatic; + enum_field_val->type = type_info_enum_field_type; + + ConstExprValue *inner_fields = create_const_vals(2); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_usize; + + ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); + + bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); + + enum_field_val->data.x_struct.fields = inner_fields; + enum_field_val->data.x_struct.parent.id = ConstParentIdArray; + enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; + enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + } + + // @TODO + // methods: []TypeInfo.Method + return result; } default: zig_unreachable(); } - // Cache the returned value. - assert(result != nullptr); - ira->codegen->type_info_cache.put(type_entry, result); - return result; + zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, -- cgit v1.2.3 From 4aa5d87ada56f9887d49c7a2a80246e0bb9d5fb9 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 17:14:38 +0300 Subject: Added ErrorSet TypeInfo generation. --- src/codegen.cpp | 2 +- src/ir.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 9c4e5b1555..8c56af674c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6426,7 +6426,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const ErrorUnion = struct {\n" - " error_set: ErrorSet,\n" + " error_set: &ErrorSet,\n" " payload: &TypeInfo,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index 9ca95d6222..92fd7a7018 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16172,6 +16172,63 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // @TODO // methods: []TypeInfo.Method + return result; + } + case TypeTableEntryIdErrorSet: + { + ConstExprValue *result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "ErrorSet"); + + ConstExprValue *fields = create_const_vals(1); + result->data.x_struct.fields = fields; + + ir_type_info_struct_set_parent(result, parent, parent_field_index); + ira->codegen->type_info_cache.put(type_entry, result); + + // errors: []TypeInfo.Error + ensure_field_index(result->type, "errors", 0); + + TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error"); + // @TODO Same as above in Enum TypeInfo generation. + // ensure_field_index(type_info_error_type, "name", 0); + // ensure_field_index(type_info_error_type, "value", 1); + + uint32_t error_count = type_entry->data.error_set.err_count; + ConstExprValue *error_array = create_const_vals(1); + error_array->special = ConstValSpecialStatic; + error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); + error_array->data.x_array.special = ConstArraySpecialNone; + error_array->data.x_array.s_none.parent.id = ConstParentIdNone; + error_array->data.x_array.s_none.elements = create_const_vals(error_count); + + init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); + for (uint32_t error_index = 0; error_index < error_count; error_index++) + { + ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; + ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index]; + + error_val->special = ConstValSpecialStatic; + error_val->type = type_info_error_type; + + ConstExprValue *inner_fields = create_const_vals(2); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_usize; + + ConstExprValue *name = nullptr; + if (error->cached_error_name_val != nullptr) + name = error->cached_error_name_val; + if (name == nullptr) + name = create_const_str_lit(ira->codegen, &error->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true); + bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); + + error_val->data.x_struct.fields = inner_fields; + error_val->data.x_struct.parent.id = ConstParentIdArray; + error_val->data.x_struct.parent.data.p_array.array_val = error_array; + error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + } + return result; } default: -- cgit v1.2.3 From fbbbee6b72991b65c31a8eac8f1edfc3d1a88b59 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 18:18:47 +0300 Subject: Switched to shallow TypeInfo. --- src/codegen.cpp | 24 ++--- src/ir.cpp | 271 ++++++++++++-------------------------------------------- 2 files changed, 69 insertions(+), 226 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 8c56af674c..c8bb502718 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6390,12 +6390,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " is_const: bool,\n" " is_volatile: bool,\n" " alignment: u32,\n" - " child: &TypeInfo,\n" + " child: type,\n" " };\n" "\n" " pub const Array = struct {\n" " len: usize,\n" - " child: &TypeInfo,\n" + " child: type,\n" " };\n" "\n" " pub const ContainerLayout = enum {\n" @@ -6412,7 +6412,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const StructField = struct {\n" " name: []const u8,\n" " offset: usize,\n" - " type_info: &TypeInfo,\n" + " field_type: type,\n" " };\n" "\n" " pub const Struct = struct {\n" @@ -6422,12 +6422,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const Nullable = struct {\n" - " child: &TypeInfo,\n" + " child: type,\n" " };\n" "\n" " pub const ErrorUnion = struct {\n" " error_set: &ErrorSet,\n" - " payload: &TypeInfo,\n" + " payload: type,\n" " };\n" "\n" " pub const Error = struct {\n" @@ -6446,7 +6446,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const Enum = struct {\n" " layout: ContainerLayout,\n" - " tag_type: &Int,\n" + " tag_type: type,\n" " fields: []EnumField,\n" " methods: []Method,\n" " };\n" @@ -6454,12 +6454,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const UnionField = struct {\n" " name: []const u8,\n" " enum_field: EnumField,\n" - " type_info: &TypeInfo,\n" + " field_type: type,\n" " };\n" "\n" " pub const Union = struct {\n" " layout: ContainerLayout,\n" - " tag_type: ?Enum,\n" + " tag_type: type,\n" " fields: []UnionField,\n" " methods: []Method,\n" " };\n" @@ -6476,24 +6476,24 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const FnArg = struct {\n" " is_comptime: bool,\n" " name: []const u8,\n" - " type_info: &TypeInfo,\n" + " arg_type: type,\n" " };\n" "\n" " pub const Fn = struct {\n" " calling_convention: CallingConvention,\n" " is_generic: bool,\n" " is_varargs: bool,\n" - " return_type: &TypeInfo,\n" + " return_type: type,\n" " args: []FnArg,\n" " };\n" "\n" " pub const BoundFn = struct {\n" - " bound_type: &TypeInfo,\n" + " bound_type: type,\n" " fn_info: Fn,\n" " };\n" "\n" " pub const Promise = struct {\n" - " child: ?&TypeInfo,\n" + " child: type,\n" " };\n" "};\n\n"); assert(ContainerLayoutAuto == 0); diff --git a/src/ir.cpp b/src/ir.cpp index 92fd7a7018..fdf1a0c8ab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15748,31 +15748,6 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static void ir_type_info_struct_set_parent(ConstExprValue *struct_val, ConstExprValue *parent, ssize_t parent_field_index) -{ - assert(struct_val->type->id == TypeTableEntryIdStruct); - assert(parent->type != nullptr && !type_is_invalid(parent->type)); - - switch (parent->type->id) - { - case TypeTableEntryIdArray: - zig_panic("TODO - Only expected struct or union parent."); - case TypeTableEntryIdStruct: - assert(parent_field_index >= 0); - struct_val->data.x_struct.parent.id = ConstParentIdStruct; - struct_val->data.x_struct.parent.data.p_struct.struct_val = parent; - struct_val->data.x_struct.parent.data.p_struct.field_index = parent_field_index; - break; - case TypeTableEntryIdUnion: - assert(parent_field_index == -1); - struct_val->data.x_struct.parent.id = ConstParentIdUnion; - struct_val->data.x_struct.parent.data.p_union.union_val = parent; - break; - default: - struct_val->data.x_struct.parent.id = ConstParentIdNone; - } -} - static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) { static ConstExprValue *type_info_var = nullptr; @@ -15807,69 +15782,12 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na return var->value->data.x_type; } -static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *parent, - ssize_t parent_field_index, TypeTableEntry *type_entry) +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); - // Lookup an available value in our cache. - auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); - if (entry != nullptr) - { - // Override the parent if we need to. - ConstExprValue *result = entry->value; - - assert(result->type->id == TypeTableEntryIdStruct); - - ConstParent *curr_parent = &result->data.x_struct.parent; - if (curr_parent->id == ConstParentIdStruct) - { - if (curr_parent->data.p_struct.struct_val == parent && - parent_field_index != -1 && - curr_parent->data.p_struct.field_index == (size_t)parent_field_index) - { - return result; - } - ConstExprValue *new_result = create_const_vals(1); - copy_const_val(new_result, result, true); - ir_type_info_struct_set_parent(new_result, parent, parent_field_index); - return new_result; - } - else if (curr_parent->id == ConstParentIdUnion) - { - if (curr_parent->data.p_union.union_val == parent) - { - return result; - } - ConstExprValue *new_result = create_const_vals(1); - copy_const_val(new_result, result, true); - ir_type_info_struct_set_parent(new_result, parent, parent_field_index); - return new_result; - } - else if (curr_parent->id == ConstParentIdNone) - { - if (parent->type->id != TypeTableEntryIdStruct && - parent->type->id != TypeTableEntryIdArray && - parent->type->id != TypeTableEntryIdUnion) - { - return result; - } - ConstExprValue *new_result = create_const_vals(1); - copy_const_val(new_result, result, true); - ir_type_info_struct_set_parent(new_result, parent, parent_field_index); - return new_result; - } - - return result; - } - - // If the value is not present in the cache, we will build it, add it and return it. - // We add values to the cache eagerly, as soon as we have filled out the root object's fields. - // That way, if we need to fetch the value in a recursive call down the line, even if we need to - // copy the value and reajust the parent, the value we get back still points to child values that - // will be filled later. - + ConstExprValue *result = nullptr; switch (type_entry->id) { case TypeTableEntryIdInvalid: @@ -15887,18 +15805,24 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: return nullptr; + default: + { + // Lookup an available value in our cache. + auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); + if (entry != nullptr) + return entry->value; + + // Fallthrough if we don't find one. + } case TypeTableEntryIdInt: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Int"); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // is_signed: bool ensure_field_index(result->type, "is_signed", 0); fields[0].special = ConstValSpecialStatic; @@ -15910,40 +15834,34 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[1].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); - return result; + break; } case TypeTableEntryIdFloat: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Float"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // bits: u8 ensure_field_index(result->type, "bits", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_u8; bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); - return result; + break; } case TypeTableEntryIdPointer: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Pointer"); ConstExprValue *fields = create_const_vals(4); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // is_const: bool ensure_field_index(result->type, "is_const", 0); fields[0].special = ConstValSpecialStatic; @@ -15959,175 +15877,94 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_u32; bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); - // child: &TypeInfo + // child: type ensure_field_index(result->type, "child", 3); - - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - fields[3].special = ConstValSpecialStatic; - fields[3].type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[3].data.x_ptr.special = ConstPtrSpecialRef; - fields[3].data.x_ptr.mut = ConstPtrMutComptimeVar; - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.pointer.child_type->id)); + fields[3].type = ira->codegen->builtin_types.entry_type; + fields[3].data.x_type = type_entry->data.pointer.child_type; - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.pointer.child_type); - - fields[3].data.x_ptr.data.ref.pointee = union_val; - - return result; + break; } case TypeTableEntryIdArray: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Array"); ConstExprValue *fields = create_const_vals(2); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // len: usize ensure_field_index(result->type, "len", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); - // child: &TypeInfo + // child: type ensure_field_index(result->type, "child", 1); - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - fields[1].special = ConstValSpecialStatic; - fields[1].type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[1].data.x_ptr.special = ConstPtrSpecialRef; - fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.array.child_type->id)); + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.array.child_type; - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.array.child_type); - - fields[1].data.x_ptr.data.ref.pointee = union_val; - - return result; + break; } case TypeTableEntryIdMaybe: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Nullable"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - - // child: &TypeInfo + // child: type ensure_field_index(result->type, "child", 0); - - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - fields[0].special = ConstValSpecialStatic; - fields[0].type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[0].data.x_ptr.special = ConstPtrSpecialRef; - fields[0].data.x_ptr.mut = ConstPtrMutComptimeVar; - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.maybe.child_type->id)); - - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.maybe.child_type); - - fields[0].data.x_ptr.data.ref.pointee = union_val; + fields[0].type = ira->codegen->builtin_types.entry_type; + fields[0].data.x_type = type_entry->data.maybe.child_type; - return result; + break; } case TypeTableEntryIdPromise: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Promise"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - - // child: ?&TypeInfo + // @TODO ?type instead of using @typeOf(undefined) when we have no type. + // child: type ensure_field_index(result->type, "child", 0); - - TypeTableEntry *type_info_type = ir_type_info_get_type(ira, nullptr); - TypeTableEntry *type_info_ptr_type = get_pointer_to_type(ira->codegen, type_info_type, false); - fields[0].special = ConstValSpecialStatic; - fields[0].type = get_maybe_type(ira->codegen, type_info_ptr_type); + fields[0].type = ira->codegen->builtin_types.entry_type; if (type_entry->data.promise.result_type == nullptr) - { - fields[0].data.x_maybe = nullptr; - } + fields[0].data.x_type = ira->codegen->builtin_types.entry_undef; else - { - ConstExprValue *maybe_value = create_const_vals(1); - maybe_value->special = ConstValSpecialStatic; - maybe_value->type = type_info_ptr_type; - - maybe_value->data.x_ptr.special = ConstPtrSpecialRef; - maybe_value->data.x_ptr.mut = ConstPtrMutComptimeVar; - - ConstExprValue *union_val = create_const_vals(1); - union_val->special = ConstValSpecialStatic; - union_val->type = type_info_type; - bigint_init_unsigned(&union_val->data.x_union.tag, type_id_index(type_entry->data.promise.result_type->id)); - union_val->data.x_union.payload = ir_make_type_info_value(ira, union_val, -1, - type_entry->data.promise.result_type); - - maybe_value->data.x_ptr.data.ref.pointee = union_val; - fields[0].data.x_maybe = maybe_value; - } + fields[0].data.x_type = type_entry->data.promise.result_type; - return result; + break; } case TypeTableEntryIdEnum: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Enum"); ConstExprValue *fields = create_const_vals(4); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // layout: ContainerLayout ensure_field_index(result->type, "layout", 0); fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout); - // tag_type: &TypeInfo.Int + // tag_type: type ensure_field_index(result->type, "tag_type", 1); - - TypeTableEntry *type_info_int_type = ir_type_info_get_type(ira, "Int"); - fields[1].special = ConstValSpecialStatic; - fields[1].type = get_pointer_to_type(ira->codegen, type_info_int_type, false); - fields[1].data.x_ptr.special = ConstPtrSpecialRef; - fields[1].data.x_ptr.mut = ConstPtrMutComptimeVar; - - ConstExprValue *tag_type_info_struct = ir_make_type_info_value(ira, &fields[1], -1, - type_entry->data.enumeration.tag_int_type); - assert(tag_type_info_struct->type == type_info_int_type); - fields[1].data.x_ptr.data.ref.pointee = tag_type_info_struct; + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.enumeration.tag_int_type; // fields: []TypeInfo.EnumField ensure_field_index(result->type, "fields", 2); @@ -16172,20 +16009,17 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p // @TODO // methods: []TypeInfo.Method - return result; + break; } case TypeTableEntryIdErrorSet: { - ConstExprValue *result = create_const_vals(1); + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "ErrorSet"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - ir_type_info_struct_set_parent(result, parent, parent_field_index); - ira->codegen->type_info_cache.put(type_entry, result); - // errors: []TypeInfo.Error ensure_field_index(result->type, "errors", 0); @@ -16229,13 +16063,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, ConstExprValue *p error_val->data.x_struct.parent.data.p_array.elem_index = error_index; } - return result; + break; } - default: - zig_unreachable(); } - zig_unreachable(); + assert(result != nullptr); + ira->codegen->type_info_cache.put(type_entry, result); + return result; } static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, @@ -16251,7 +16085,16 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); - out_val->data.x_union.payload = ir_make_type_info_value(ira, out_val, -1, type_entry); + + ConstExprValue *payload = ir_make_type_info_value(ira, type_entry); + out_val->data.x_union.payload = payload; + + if (payload != nullptr) + { + assert(payload->type->id == TypeTableEntryIdStruct); + payload->data.x_struct.parent.id = ConstParentIdUnion; + payload->data.x_struct.parent.data.p_union.union_val = out_val; + } return result_type; } -- cgit v1.2.3 From 884e32d5c3aac470cdcd9b20e15554d6193f5501 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 26 Apr 2018 19:56:34 +0300 Subject: Added ErrorUnion, Union TypeInfo generation --- src/codegen.cpp | 2 +- src/ir.cpp | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 127 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index c8bb502718..77794c2ab9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6426,7 +6426,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const ErrorUnion = struct {\n" - " error_set: &ErrorSet,\n" + " error_set: type,\n" " payload: type,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index fdf1a0c8ab..f91d1c3edf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15787,6 +15787,27 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); + const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field) { + TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) + // ensure_field_index(type_info_enum_field_type, "name", 0); + // ensure_field_index(type_info_enum_field_type, "value", 1); + + enum_field_val->special = ConstValSpecialStatic; + enum_field_val->type = type_info_enum_field_type; + + ConstExprValue *inner_fields = create_const_vals(2); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_usize; + + ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); + + bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); + + enum_field_val->data.x_struct.fields = inner_fields; + }; + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -15988,20 +16009,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; - - enum_field_val->special = ConstValSpecialStatic; - enum_field_val->type = type_info_enum_field_type; - - ConstExprValue *inner_fields = create_const_vals(2); - inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = ira->codegen->builtin_types.entry_usize; - - ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); - init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); - - bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); - - enum_field_val->data.x_struct.fields = inner_fields; + make_enum_field_val(enum_field_val, enum_field); enum_field_val->data.x_struct.parent.id = ConstParentIdArray; enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; @@ -16063,6 +16071,110 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t error_val->data.x_struct.parent.data.p_array.elem_index = error_index; } + break; + } + case TypeTableEntryIdErrorUnion: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "ErrorUnion"); + + ConstExprValue *fields = create_const_vals(2); + result->data.x_struct.fields = fields; + + // error_set: type + ensure_field_index(result->type, "error_set", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_type; + fields[0].data.x_type = type_entry->data.error_union.err_set_type; + + // payload: type + ensure_field_index(result->type, "payload", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.error_union.payload_type; + + break; + } + case TypeTableEntryIdUnion: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Union"); + + ConstExprValue *fields = create_const_vals(4); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout); + // tag_type: type + ensure_field_index(result->type, "tag_type", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_type; + // @TODO ?type instead of using @typeOf(undefined) when we have no type. + if (type_entry->data.unionation.tag_type == nullptr) + fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; + else + fields[1].data.x_type = type_entry->data.unionation.tag_type; + + fields[1].data.x_type = type_entry->data.unionation.tag_type; + // fields: []TypeInfo.UnionField + ensure_field_index(result->type, "fields", 2); + + TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField"); + // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) + // ensure_field_index(type_info_union_field_type, "name", 0); + // ensure_field_index(type_info_union_field_type, "enum_field", 1); + // ensure_field_index(type_info_union_field_type, "field_type", 2); + + uint32_t union_field_count = type_entry->data.unionation.src_field_count; + + ConstExprValue *union_field_array = create_const_vals(1); + union_field_array->special = ConstValSpecialStatic; + union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); + union_field_array->data.x_array.special = ConstArraySpecialNone; + union_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; + union_field_array->data.x_array.s_none.elements = create_const_vals(union_field_count); + + init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); + + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) + { + TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; + ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index]; + + union_field_val->special = ConstValSpecialStatic; + union_field_val->type = type_info_union_field_type; + + ConstExprValue *inner_fields = create_const_vals(3); + inner_fields[1].special = ConstValSpecialStatic; + make_enum_field_val(&inner_fields[1], union_field->enum_field); + inner_fields[1].data.x_struct.parent.id = ConstParentIdStruct; + inner_fields[1].data.x_struct.parent.data.p_struct.struct_val = union_field_val; + inner_fields[1].data.x_struct.parent.data.p_struct.field_index = 1; + + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = ira->codegen->builtin_types.entry_type; + inner_fields[2].data.x_type = union_field->type_entry; + + ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); + + + union_field_val->data.x_struct.fields = inner_fields; + union_field_val->data.x_struct.parent.id = ConstParentIdArray; + union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; + union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + + // @TODO Check if TypeUnionField::enum_field == nullptr when tag_type == nullptr + // If it is, make enum_field: ?EnumField, set it when available, done. + } + + // @TODO + // methods: []TypeInfo.Method break; } } -- cgit v1.2.3 From 9041d0d37e59e3f07d568f93bd680cc0065d1cd2 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 02:05:24 +0300 Subject: Fixed enum tag type detection in TypeInfo generation. --- src/codegen.cpp | 8 +++++++- src/ir.cpp | 56 +++++++++++++++++++++++--------------------------------- 2 files changed, 30 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 77794c2ab9..b6236197cc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6349,6 +6349,12 @@ static void define_builtin_compile_vars(CodeGen *g) { } { // @TODO Add Namespace info. + // @TODO Methods -> definitions + // @TODO Includes type definitions (name + type bound) + functions + const variable definitions (+ type of variable) + // @TODO Type definitions are defined as variable definitions of type 'type' + // @TODO This should give us everything available. + // @TODO An alternative is exposing the value of every variable definition, check out if it's possible and wether we want that. + // @TODO I don't think so, @field gives it to us for free. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" " Type: void,\n" @@ -6453,7 +6459,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const UnionField = struct {\n" " name: []const u8,\n" - " enum_field: EnumField,\n" + " enum_field: ?EnumField,\n" " field_type: type,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index f91d1c3edf..a6012b0a91 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15787,8 +15787,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); - const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field) { - TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, + TypeTableEntry *type_info_enum_field_type) { // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) // ensure_field_index(type_info_enum_field_type, "name", 0); // ensure_field_index(type_info_enum_field_type, "value", 1); @@ -15990,10 +15990,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ensure_field_index(result->type, "fields", 2); TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); - // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) - // ensure_field_index(type_info_enum_field_type, "name", 0); - // ensure_field_index(type_info_enum_field_type, "value", 1); - uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; ConstExprValue *enum_field_array = create_const_vals(1); @@ -16009,14 +16005,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; - make_enum_field_val(enum_field_val, enum_field); + make_enum_field_val(enum_field_val, enum_field, type_info_enum_field_type); enum_field_val->data.x_struct.parent.id = ConstParentIdArray; enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; } - // @TODO - // methods: []TypeInfo.Method + // @TODO Definitions break; } case TypeTableEntryIdErrorSet: @@ -16032,10 +16027,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ensure_field_index(result->type, "errors", 0); TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error"); - // @TODO Same as above in Enum TypeInfo generation. - // ensure_field_index(type_info_error_type, "name", 0); - // ensure_field_index(type_info_error_type, "value", 1); - uint32_t error_count = type_entry->data.error_set.err_count; ConstExprValue *error_array = create_const_vals(1); error_array->special = ConstValSpecialStatic; @@ -16115,21 +16106,18 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_type; // @TODO ?type instead of using @typeOf(undefined) when we have no type. - if (type_entry->data.unionation.tag_type == nullptr) - fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; - else + AstNode *union_decl_node = type_entry->data.unionation.decl_node; + if (union_decl_node->data.container_decl.auto_enum || + union_decl_node->data.container_decl.init_arg_expr != nullptr) + { fields[1].data.x_type = type_entry->data.unionation.tag_type; - - fields[1].data.x_type = type_entry->data.unionation.tag_type; + } + else + fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField"); - // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) - // ensure_field_index(type_info_union_field_type, "name", 0); - // ensure_field_index(type_info_union_field_type, "enum_field", 1); - // ensure_field_index(type_info_union_field_type, "field_type", 2); - uint32_t union_field_count = type_entry->data.unionation.src_field_count; ConstExprValue *union_field_array = create_const_vals(1); @@ -16141,6 +16129,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); + TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; @@ -16151,10 +16141,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; - make_enum_field_val(&inner_fields[1], union_field->enum_field); - inner_fields[1].data.x_struct.parent.id = ConstParentIdStruct; - inner_fields[1].data.x_struct.parent.data.p_struct.struct_val = union_field_val; - inner_fields[1].data.x_struct.parent.data.p_struct.field_index = 1; + inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); + + if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) + inner_fields[1].data.x_maybe = nullptr; + else + { + inner_fields[1].data.x_maybe = create_const_vals(1); + make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type); + } inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = ira->codegen->builtin_types.entry_type; @@ -16163,18 +16158,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name); init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); - union_field_val->data.x_struct.fields = inner_fields; union_field_val->data.x_struct.parent.id = ConstParentIdArray; union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; - - // @TODO Check if TypeUnionField::enum_field == nullptr when tag_type == nullptr - // If it is, make enum_field: ?EnumField, set it when available, done. } - // @TODO - // methods: []TypeInfo.Method + // @TODO Definitions break; } } -- cgit v1.2.3 From a2dadbc206f80dcf39b42f3be97a8d74795d0381 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 02:52:09 +0300 Subject: Added struct TypeInfo generation. --- src/codegen.cpp | 2 +- src/ir.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index b6236197cc..873cf18072 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6417,7 +6417,7 @@ static void define_builtin_compile_vars(CodeGen *g) { "\n" " pub const StructField = struct {\n" " name: []const u8,\n" - " offset: usize,\n" + " offset: ?usize,\n" " field_type: type,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index a6012b0a91..f167d76ca1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15787,6 +15787,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); + ensure_complete_type(ira->codegen, type_entry); + const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, TypeTableEntry *type_info_enum_field_type) { // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) @@ -16164,6 +16166,72 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; } + // @TODO Definitions + break; + } + case TypeTableEntryIdStruct: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Struct"); + + ConstExprValue *fields = create_const_vals(3); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout); + // fields: []TypeInfo.StructField + ensure_field_index(result->type, "fields", 1); + + TypeTableEntry *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField"); + uint32_t struct_field_count = type_entry->data.structure.src_field_count; + + ConstExprValue *struct_field_array = create_const_vals(1); + struct_field_array->special = ConstValSpecialStatic; + struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); + struct_field_array->data.x_array.special = ConstArraySpecialNone; + struct_field_array->data.x_array.s_none.parent.id = ConstParentIdNone; + struct_field_array->data.x_array.s_none.elements = create_const_vals(struct_field_count); + + init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); + + for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) + { + TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index]; + ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index]; + + struct_field_val->special = ConstValSpecialStatic; + struct_field_val->type = type_info_struct_field_type; + + ConstExprValue *inner_fields = create_const_vals(3); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); + + if (!type_has_bits(struct_field->type_entry)) + inner_fields[1].data.x_maybe = nullptr; + else + { + size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); + inner_fields[1].data.x_maybe = create_const_vals(1); + inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset); + } + + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = ira->codegen->builtin_types.entry_type; + inner_fields[2].data.x_type = struct_field->type_entry; + + ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); + + struct_field_val->data.x_struct.fields = inner_fields; + struct_field_val->data.x_struct.parent.id = ConstParentIdArray; + struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; + struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + } // @TODO Definitions break; } -- cgit v1.2.3 From 8f703f919f13e23b2c09f577a446aabb7799e27c Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 04:29:50 +0300 Subject: Added Fn TypeInfo generation. --- src/codegen.cpp | 7 +++-- src/ir.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 873cf18072..ee85d7d3c4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6480,16 +6480,17 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const FnArg = struct {\n" - " is_comptime: bool,\n" - " name: []const u8,\n" + " is_generic: bool,\n" + " is_noalias: bool,\n" " arg_type: type,\n" " };\n" "\n" " pub const Fn = struct {\n" " calling_convention: CallingConvention,\n" " is_generic: bool,\n" - " is_varargs: bool,\n" + " is_var_args: bool,\n" " return_type: type,\n" + " async_allocator_type: type,\n" " args: []FnArg,\n" " };\n" "\n" diff --git a/src/ir.cpp b/src/ir.cpp index f167d76ca1..a7076a3243 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16233,6 +16233,98 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; } // @TODO Definitions + break; + } + case TypeTableEntryIdFn: + { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Fn"); + + ConstExprValue *fields = create_const_vals(5); + result->data.x_struct.fields = fields; + + // @TODO Fix type = undefined with ?type + + // calling_convention: TypeInfo.CallingConvention + ensure_field_index(result->type, "calling_convention", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ir_type_info_get_type(ira, "CallingConvention"); + bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); + // is_generic: bool + ensure_field_index(result->type, "is_generic", 1); + bool is_generic = type_entry->data.fn.is_generic; + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_bool; + fields[1].data.x_bool = is_generic; + // is_varargs: bool + ensure_field_index(result->type, "is_var_args", 2); + bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; + fields[2].special = ConstValSpecialStatic; + fields[2].type = ira->codegen->builtin_types.entry_bool; + fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; + // return_type: type + ensure_field_index(result->type, "return_type", 3); + fields[3].special = ConstValSpecialStatic; + fields[3].type = ira->codegen->builtin_types.entry_type; + if (type_entry->data.fn.fn_type_id.return_type == nullptr) + fields[3].data.x_type = ira->codegen->builtin_types.entry_undef; + else + fields[3].data.x_type = type_entry->data.fn.fn_type_id.return_type; + // async_allocator_type: type + ensure_field_index(result->type, "async_allocator_type", 4); + fields[4].special = ConstValSpecialStatic; + fields[4].type = ira->codegen->builtin_types.entry_type; + if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) + fields[4].data.x_type = ira->codegen->builtin_types.entry_undef; + else + fields[4].data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; + // args: []TypeInfo.FnArg + TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg"); + size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - + (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); + + ConstExprValue *fn_arg_array = create_const_vals(1); + fn_arg_array->special = ConstValSpecialStatic; + fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); + fn_arg_array->data.x_array.special = ConstArraySpecialNone; + fn_arg_array->data.x_array.s_none.parent.id = ConstParentIdNone; + fn_arg_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); + + init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); + + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) + { + FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; + ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.s_none.elements[fn_arg_index]; + + fn_arg_val->special = ConstValSpecialStatic; + fn_arg_val->type = type_info_fn_arg_type; + + bool arg_is_generic = fn_param_info->type == nullptr; + if (arg_is_generic) assert(is_generic); + + ConstExprValue *inner_fields = create_const_vals(3); + inner_fields[0].special = ConstValSpecialStatic; + inner_fields[0].type = ira->codegen->builtin_types.entry_bool; + inner_fields[0].data.x_bool = arg_is_generic; + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_bool; + inner_fields[1].data.x_bool = fn_param_info->is_noalias; + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = ira->codegen->builtin_types.entry_type; + + if (arg_is_generic) + inner_fields[2].data.x_type = ira->codegen->builtin_types.entry_undef; + else + inner_fields[2].data.x_type = fn_param_info->type; + + fn_arg_val->data.x_struct.fields = inner_fields; + fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; + fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + } + break; } } -- cgit v1.2.3 From ea2596280fc2c78f71b08921db3a4c9826eb93e0 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Fri, 27 Apr 2018 05:10:20 +0300 Subject: Added BoundFn TypeInfo generation. --- src/codegen.cpp | 7 +------ src/ir.cpp | 11 ++++++++++- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index ee85d7d3c4..fc872e1908 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6378,7 +6378,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Fn: Fn,\n" " Namespace: void,\n" " Block: void,\n" - " BoundFn: BoundFn,\n" + " BoundFn: Fn,\n" " ArgTuple: void,\n" " Opaque: void,\n" " Promise: Promise,\n" @@ -6494,11 +6494,6 @@ static void define_builtin_compile_vars(CodeGen *g) { " args: []FnArg,\n" " };\n" "\n" - " pub const BoundFn = struct {\n" - " bound_type: type,\n" - " fn_info: Fn,\n" - " };\n" - "\n" " pub const Promise = struct {\n" " child: type,\n" " };\n" diff --git a/src/ir.cpp b/src/ir.cpp index a7076a3243..c2da83886a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16241,7 +16241,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Fn"); - ConstExprValue *fields = create_const_vals(5); + ConstExprValue *fields = create_const_vals(6); result->data.x_struct.fields = fields; // @TODO Fix type = undefined with ?type @@ -16325,6 +16325,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; } + break; + } + case TypeTableEntryIdBoundFn: + { + // @TODO figure out memory corruption error. + TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type; + assert(fn_type->id == TypeTableEntryIdFn); + result = ir_make_type_info_value(ira, fn_type); + break; } } -- cgit v1.2.3 From 3178528335cb5efbf237cecb9ea9eb3bfa31b21f Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 14:05:08 +0200 Subject: Removed zero sized error set optimization fixes #762 fixes #818 --- src/ir.cpp | 14 ++++---------- test/cases/error.zig | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 86c77758b2..ec7f41d748 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6166,16 +6166,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A buf_init_from_buf(&err_set_type->name, type_name); err_set_type->is_copyable = true; err_set_type->data.error_set.err_count = err_count; - - if (err_count == 0) { - err_set_type->zero_bits = true; - err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type; - } else { - err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; - err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; - irb->codegen->error_di_types.append(&err_set_type->di_type); - err_set_type->data.error_set.errors = allocate(err_count); - } + err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; + irb->codegen->error_di_types.append(&err_set_type->di_type); + err_set_type->data.error_set.errors = allocate(err_count); ErrorTableEntry **errors = allocate(irb->codegen->errors_by_index.length + err_count); diff --git a/test/cases/error.zig b/test/cases/error.zig index e64bf02c91..c64c835fc4 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -175,3 +175,30 @@ fn baz_1() !i32 { fn quux_1() !i32 { return error.C; } + + +test "error: fn returning empty error set can be passed as fn returning any error" { + entry(); + comptime entry(); +} + +fn entry() void { + foo2(bar2); +} + +fn foo2(f: fn()error!void) void { + const x = f(); +} + +fn bar2() (error{}!void) { } + + +test "error: Zero sized error set returned with value payload crash" { + _ = foo3(0); + _ = comptime foo3(0); +} + +const Error = error{}; +fn foo3(b: usize) Error!usize { + return b; +} -- cgit v1.2.3 From 61b01805968939669a29f7189f4ce7fab46ab2da Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 28 Apr 2018 17:01:19 +0300 Subject: Added definition TypeInfo generation, except for function definitions. --- src/codegen.cpp | 30 ++++++------ src/ir.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 154 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index fc872e1908..be1d59c26a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6348,13 +6348,6 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "};\n\n"); } { - // @TODO Add Namespace info. - // @TODO Methods -> definitions - // @TODO Includes type definitions (name + type bound) + functions + const variable definitions (+ type of variable) - // @TODO Type definitions are defined as variable definitions of type 'type' - // @TODO This should give us everything available. - // @TODO An alternative is exposing the value of every variable definition, check out if it's possible and wether we want that. - // @TODO I don't think so, @field gives it to us for free. buf_appendf(contents, "pub const TypeInfo = union(TypeId) {\n" " Type: void,\n" @@ -6410,11 +6403,6 @@ static void define_builtin_compile_vars(CodeGen *g) { " Packed,\n" " };\n" "\n" - " pub const Method = struct {\n" - " name: []const u8,\n" - " fn_info: Fn,\n" - " };\n" - "\n" " pub const StructField = struct {\n" " name: []const u8,\n" " offset: ?usize,\n" @@ -6424,7 +6412,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const Struct = struct {\n" " layout: ContainerLayout,\n" " fields: []StructField,\n" - " methods: []Method,\n" + " defs: []Definition,\n" " };\n" "\n" " pub const Nullable = struct {\n" @@ -6454,7 +6442,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: type,\n" " fields: []EnumField,\n" - " methods: []Method,\n" + " defs: []Definition,\n" " };\n" "\n" " pub const UnionField = struct {\n" @@ -6467,7 +6455,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " layout: ContainerLayout,\n" " tag_type: type,\n" " fields: []UnionField,\n" - " methods: []Method,\n" + " defs: []Definition,\n" " };\n" "\n" " pub const CallingConvention = enum {\n" @@ -6497,6 +6485,18 @@ static void define_builtin_compile_vars(CodeGen *g) { " pub const Promise = struct {\n" " child: type,\n" " };\n" + "\n" + " pub const Definition = struct {\n" + " name: []const u8,\n" + " is_pub: bool,\n" + " data: Data,\n" + "\n" + " const Data = union(enum) {\n" + " Type: type,\n" + " Var: type,\n" + " Fn: void,\n" + " };\n" + " };\n" "};\n\n"); assert(ContainerLayoutAuto == 0); assert(ContainerLayoutExtern == 1); diff --git a/src/ir.cpp b/src/ir.cpp index c2da83886a..d8da5b3172 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15748,7 +15748,7 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz (buf_deinit(field_name_buf), true)); } -static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name) +static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root = nullptr) { static ConstExprValue *type_info_var = nullptr; static TypeTableEntry *type_info_type = nullptr; @@ -15761,10 +15761,14 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na assert(type_info_type->id == TypeTableEntryIdUnion); } - if (type_name == nullptr) + if (type_name == nullptr && root == nullptr) return type_info_type; + else if (type_name == nullptr) + return root; - ScopeDecls *type_info_scope = get_container_scope(type_info_type); + TypeTableEntry *root_type = (root == nullptr) ? type_info_type : root; + + ScopeDecls *type_info_scope = get_container_scope(root_type); assert(type_info_scope != nullptr); Buf field_name = BUF_INIT; @@ -15782,6 +15786,128 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na return var->value->data.x_type; } +static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) +{ + TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition"); + ensure_complete_type(ira->codegen, type_info_definition_type); + ensure_field_index(type_info_definition_type, "name", 0); + ensure_field_index(type_info_definition_type, "is_pub", 1); + ensure_field_index(type_info_definition_type, "data", 2); + + TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); + ensure_complete_type(ira->codegen, type_info_definition_data_type); + + // Loop through our definitions once to figure out how many definitions we will generate info for. + auto decl_it = decls_scope->decl_table.entry_iterator(); + decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; + int definition_count = 0; + + while ((curr_entry = decl_it.next()) != nullptr) + { + // Skip comptime blocks. + if (curr_entry->value->id != TldIdCompTime) + { + definition_count += 1; + } + } + + ConstExprValue *definition_array = create_const_vals(1); + definition_array->special = ConstValSpecialStatic; + definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); + definition_array->data.x_array.special = ConstArraySpecialNone; + definition_array->data.x_array.s_none.parent.id = ConstParentIdNone; + definition_array->data.x_array.s_none.elements = create_const_vals(definition_count); + init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); + + // Loop through the definitions and generate info. + decl_it = decls_scope->decl_table.entry_iterator(); + curr_entry = nullptr; + int definition_index = 0; + while ((curr_entry = decl_it.next()) != nullptr) + { + // Skip comptime blocks + if (curr_entry->value->id == TldIdCompTime) + continue; + + ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index]; + + definition_val->special = ConstValSpecialStatic; + definition_val->type = type_info_definition_type; + + ConstExprValue *inner_fields = create_const_vals(3); + ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key); + init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true); + inner_fields[1].special = ConstValSpecialStatic; + inner_fields[1].type = ira->codegen->builtin_types.entry_bool; + inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; + inner_fields[2].special = ConstValSpecialStatic; + inner_fields[2].type = type_info_definition_data_type; + inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; + inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; + inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + + switch (curr_entry->value->id) + { + case TldIdVar: + { + VariableTableEntry *var = ((TldVar *)curr_entry->value)->var; + ensure_complete_type(ira->codegen, var->value->type); + if (var->value->type->id == TypeTableEntryIdMetaType) + { + // We have a variable of type 'type', so it's actually a type definition. + // 0: Data.Type: type + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); + inner_fields[2].data.x_union.payload = var->value; + } + else + { + // We have a variable of another type, so we store the type of the variable. + // 1: Data.Var: type + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); + + ConstExprValue *payload = create_const_vals(1); + payload->type = ira->codegen->builtin_types.entry_type; + payload->data.x_type = var->value->type; + + inner_fields[2].data.x_union.payload = payload; + } + + break; + } + case TldIdFn: + { + // 2: Data.Fn: Data.FnDef + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2); + // @TODO Data.FnDef + inner_fields[2].data.x_union.payload = nullptr; + break; + } + case TldIdContainer: + { + TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry; + ensure_complete_type(ira->codegen, type_entry); + // This is a type. + bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); + + ConstExprValue *payload = create_const_vals(1); + payload->type = ira->codegen->builtin_types.entry_type; + payload->data.x_type = type_entry; + + inner_fields[2].data.x_union.payload = payload; + + break; + } + default: + zig_unreachable(); + } + + definition_val->data.x_struct.fields = inner_fields; + definition_index++; + } + + assert(definition_index == definition_count); +} + static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { assert(type_entry != nullptr); @@ -15791,10 +15917,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, TypeTableEntry *type_info_enum_field_type) { - // @TODO Those cause a find_struct_type_field assertion to fail (type_entry->data.structure.complete) - // ensure_field_index(type_info_enum_field_type, "name", 0); - // ensure_field_index(type_info_enum_field_type, "value", 1); - enum_field_val->special = ConstValSpecialStatic; enum_field_val->type = type_info_enum_field_type; @@ -16012,8 +16134,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; } + // defs: []TypeInfo.Definition + ensure_field_index(result->type, "defs", 3); + ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope); - // @TODO Definitions break; } case TypeTableEntryIdErrorSet: @@ -16165,8 +16289,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; } + // defs: []TypeInfo.Definition + ensure_field_index(result->type, "defs", 3); + ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope); - // @TODO Definitions break; } case TypeTableEntryIdStruct: @@ -16232,7 +16358,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; } - // @TODO Definitions + // defs: []TypeInfo.Definition + ensure_field_index(result->type, "defs", 2); + ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope); + break; } case TypeTableEntryIdFn: @@ -16329,7 +16458,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdBoundFn: { - // @TODO figure out memory corruption error. TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type; assert(fn_type->id == TypeTableEntryIdFn); result = ir_make_type_info_value(ira, fn_type); -- cgit v1.2.3 From 2fc34eaa581cc31827e978fbd973bf36d2c647e2 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 16:27:31 +0200 Subject: Functions with infered error set can now return literals fixes #852 --- src/analyze.cpp | 1 - src/ir.cpp | 36 +++++++++++++++++++----------------- test/cases/error.zig | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index a598d7676e..11715220c7 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6131,4 +6131,3 @@ bool type_can_fail(TypeTableEntry *type_entry) { bool fn_type_can_fail(FnTypeId *fn_type_id) { return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; } - diff --git a/src/ir.cpp b/src/ir.cpp index ec7f41d748..d8156b214e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8111,7 +8111,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t * *errors = reallocate(*errors, old_errors_count, *errors_count); } -static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { +static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) { assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; if (type_is_invalid(prev_inst->value.type)) { @@ -8158,16 +8158,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdNullLit) { - prev_inst = cur_inst; - continue; - } - - if (cur_type->id == TypeTableEntryIdNullLit) { - any_are_null = true; - continue; - } - if (prev_type->id == TypeTableEntryIdErrorSet) { assert(err_set_type != nullptr); if (cur_type->id == TypeTableEntryIdErrorSet) { @@ -8427,6 +8417,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } + if (prev_type->id == TypeTableEntryIdNullLit) { + prev_inst = cur_inst; + continue; + } + + if (cur_type->id == TypeTableEntryIdNullLit) { + any_are_null = true; + continue; + } + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } @@ -8610,6 +8610,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (err_set_type != nullptr) { if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { return err_set_type; + } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); + } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) { + return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type); } else { if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) @@ -8621,8 +8625,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of null literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { - return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); } else { return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); } @@ -10645,7 +10647,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp } IrInstruction *instructions[] = {op1, op2}; - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2); + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; type_ensure_zero_bits_known(ira->codegen, resolved_type); @@ -11035,7 +11037,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; IrInstruction *instructions[] = {op1, op2}; - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2); + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; IrBinOp op_id = bin_op_instruction->op_id; @@ -13004,7 +13006,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP return first_value->value.type; } - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr, new_incoming_values.items, new_incoming_values.length); if (type_is_invalid(resolved_type)) return resolved_type; @@ -18696,7 +18698,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl } else if (ira->src_implicit_return_type_list.length == 0) { return codegen->builtin_types.entry_unreachable; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items, + return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); } } diff --git a/test/cases/error.zig b/test/cases/error.zig index c64c835fc4..2a1433df5b 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -202,3 +202,42 @@ const Error = error{}; fn foo3(b: usize) Error!usize { return b; } + + +test "error: Infer error set from literals" { + _ = nullLiteral("n") catch |err| handleErrors(err); + _ = floatLiteral("n") catch |err| handleErrors(err); + _ = intLiteral("n") catch |err| handleErrors(err); + _ = comptime nullLiteral("n") catch |err| handleErrors(err); + _ = comptime floatLiteral("n") catch |err| handleErrors(err); + _ = comptime intLiteral("n") catch |err| handleErrors(err); +} + +fn handleErrors(err: var) noreturn { + switch (err) { + error.T => {} + } + + unreachable; +} + +fn nullLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') + return null; + + return error.T; +} + +fn floatLiteral(str: []const u8) !?f64 { + if (str[0] == 'n') + return 1.0; + + return error.T; +} + +fn intLiteral(str: []const u8) !?i64 { + if (str[0] == 'n') + return 1; + + return error.T; +} -- cgit v1.2.3 From fba0347ec43fb5c06b5ac9bec541b740d95194fe Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 17:17:48 +0200 Subject: .ReturnType and @ArgType now emits errors on unresolved types related: #846 --- src/ir.cpp | 19 +++++++++++++++++++ test/compile_errors.zig | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index d8156b214e..641b8fc30c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13859,6 +13859,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } } else if (child_type->id == TypeTableEntryIdFn) { if (buf_eql_str(field_name, "ReturnType")) { + if (child_type->data.fn.fn_type_id.return_type == nullptr) { + // Return type can only ever be null, if the function is generic + assert(child_type->data.fn.is_generic); + + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, @@ -17860,6 +17869,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = fn_type_id->param_info[arg_index].type; + if (out_val->data.x_type == nullptr) { + // Args are only unresolved if our function is generic. + assert(fn_type->data.fn.is_generic); + + ir_add_error(ira, arg_index_inst, + buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_usize " because '%s' is generic", + arg_index, buf_ptr(&fn_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + return ira->codegen->builtin_types.entry_type; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8febc27b8..52e063eb39 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3209,4 +3209,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset"); + + cases.add("getting return type of generic function", + \\fn generic(a: var) void {} + \\comptime { + \\ _ = @typeOf(generic).ReturnType; + \\} + , + ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic"); + + cases.add("getting @ArgType of generic function", + \\fn generic(a: var) void {} + \\comptime { + \\ _ = @ArgType(@typeOf(generic), 0); + \\} + , + ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic"); + } -- cgit v1.2.3 From 9ba400673d798ea6f0842e4c207039c9faffb27e Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 28 Apr 2018 18:38:38 +0300 Subject: Generating TypeInfo's now forces definitions to be resolved. --- src/ir.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index d8da5b3172..6a3b4953c4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15804,9 +15804,26 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop while ((curr_entry = decl_it.next()) != nullptr) { - // Skip comptime blocks. + // If the definition is unresolved, force it to be resolved again. + if (curr_entry->value->resolution == TldResolutionUnresolved) + { + resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); + if (curr_entry->value->resolution != TldResolutionOk) + { + return; + } + } + + // Skip comptime blocks and test functions. if (curr_entry->value->id != TldIdCompTime) { + if (curr_entry->value->id == TldIdFn) + { + FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + if (fn_entry->is_test) + continue; + } + definition_count += 1; } } @@ -15825,9 +15842,15 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop int definition_index = 0; while ((curr_entry = decl_it.next()) != nullptr) { - // Skip comptime blocks + // Skip comptime blocks and test functions. if (curr_entry->value->id == TldIdCompTime) continue; + else if (curr_entry->value->id == TldIdFn) + { + FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + if (fn_entry->is_test) + continue; + } ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index]; @@ -15878,7 +15901,10 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop { // 2: Data.Fn: Data.FnDef bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2); - // @TODO Data.FnDef + + FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + assert(!fn_entry->is_test); + inner_fields[2].data.x_union.payload = nullptr; break; } -- cgit v1.2.3 From 341f8c1e8680aa3cfbeba6833f85df00355a95ef Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 17:57:47 +0200 Subject: Fixed wrong formatting for arg_index when reporting @ArgType error --- src/ir.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 641b8fc30c..4bf8240472 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17874,8 +17874,8 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc assert(fn_type->data.fn.is_generic); ir_add_error(ira, arg_index_inst, - buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_usize " because '%s' is generic", - arg_index, buf_ptr(&fn_type->name))); + buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", + arg_index, buf_ptr(&fn_type->name))); return ira->codegen->builtin_types.entry_invalid; } -- cgit v1.2.3 From af73462da46611ea9293f283ce8a6920ad73b10f Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sat, 28 Apr 2018 19:57:59 +0300 Subject: Started work on function definition TypeInfo generation. --- src/codegen.cpp | 18 +++++++++++++++++- src/ir.cpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index be1d59c26a..414a34d5cb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6494,7 +6494,19 @@ static void define_builtin_compile_vars(CodeGen *g) { " const Data = union(enum) {\n" " Type: type,\n" " Var: type,\n" - " Fn: void,\n" + " Fn: FnDef,\n" + "\n" + " const FnDef = struct {\n" + " fn_type: type,\n" + " inline_type: Inline,\n" + " calling_convention: CallingConvention,\n" + "\n" + " const Inline = enum {\n" + " Auto,\n" + " Always,\n" + " Never,\n" + " };\n" + " };\n" " };\n" " };\n" "};\n\n"); @@ -6508,6 +6520,10 @@ static void define_builtin_compile_vars(CodeGen *g) { assert(CallingConventionNaked == 3); assert(CallingConventionStdcall == 4); assert(CallingConventionAsync == 5); + + assert(FnInlineAuto == 0); + assert(FnInlineAlways == 1); + assert(FnInlineNever == 2); } { buf_appendf(contents, diff --git a/src/ir.cpp b/src/ir.cpp index 6a3b4953c4..8fadc7f7a3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15797,6 +15797,12 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); ensure_complete_type(ira->codegen, type_info_definition_data_type); + TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); + ensure_complete_type(ira->codegen, type_info_fn_def_type); + + TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); + ensure_complete_type(ira->codegen, type_info_fn_def_inline_type); + // Loop through our definitions once to figure out how many definitions we will generate info for. auto decl_it = decls_scope->decl_table.entry_iterator(); decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; @@ -15905,7 +15911,35 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); - inner_fields[2].data.x_union.payload = nullptr; + AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); + + ConstExprValue *fn_def_val = create_const_vals(1); + fn_def_val->special = ConstValSpecialStatic; + fn_def_val->type = type_info_fn_def_type; + fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; + fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + + // @TODO Add fields + ConstExprValue *fn_def_fields = create_const_vals(3); + fn_def_val->data.x_struct.fields = fn_def_fields; + + // fn_type: type + ensure_field_index(fn_def_val->type, "fn_type", 0); + fn_def_fields[0].special = ConstValSpecialStatic; + fn_def_fields[0].type = ira->codegen->builtin_types.entry_type; + fn_def_fields[0].data.x_type = fn_entry->type_entry; + // inline_type: Data.FnDef.Inline + ensure_field_index(fn_def_val->type, "inline_type", 1); + fn_def_fields[1].special = ConstValSpecialStatic; + fn_def_fields[1].type = type_info_fn_def_inline_type; + bigint_init_unsigned(&fn_def_fields[1].data.x_enum_tag, fn_entry->fn_inline); + // calling_convention: TypeInfo.CallingConvention + ensure_field_index(fn_def_val->type, "calling_convention", 2); + fn_def_fields[2].special = ConstValSpecialStatic; + fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention"); + bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); + + inner_fields[2].data.x_union.payload = fn_def_val; break; } case TldIdContainer: -- cgit v1.2.3 From 837166319dd1a5df14e5d4bebd62080bb6ebdaa1 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 19:02:46 +0200 Subject: Trying to fix osx build failing by setting param_info.type to nullptr --- src/analyze.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 11715220c7..29a2fc2560 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1261,6 +1261,10 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou fn_type_id->param_info = allocate_nonzero(param_count_alloc); fn_type_id->next_param_index = 0; fn_type_id->is_var_args = fn_proto->is_var_args; + + // We set param_info to 0, as param_info[i]->type is checked for null + // when checking if a parameters type has been resolved. + memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[i]) * fn_type_id->param_count); } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { -- cgit v1.2.3 From d6f033b42dcb49cfe45cb61821f2f451e4004686 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 19:09:25 +0200 Subject: Fixed build error --- src/analyze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 29a2fc2560..1003cf8edf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1264,7 +1264,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou // We set param_info to 0, as param_info[i]->type is checked for null // when checking if a parameters type has been resolved. - memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[i]) * fn_type_id->param_count); + memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[0]) * fn_type_id->param_count); } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { -- cgit v1.2.3 From 73bf897b5cc25ee3f1ec9d0ba1483d779de4b7c3 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 28 Apr 2018 19:21:23 +0200 Subject: Using allocate instead of allocate_nonzero so we don't have to memset --- src/analyze.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 1003cf8edf..1ecfe32f4c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1258,13 +1258,9 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou } fn_type_id->param_count = fn_proto->params.length; - fn_type_id->param_info = allocate_nonzero(param_count_alloc); + fn_type_id->param_info = allocate(param_count_alloc); fn_type_id->next_param_index = 0; fn_type_id->is_var_args = fn_proto->is_var_args; - - // We set param_info to 0, as param_info[i]->type is checked for null - // when checking if a parameters type has been resolved. - memset(fn_type_id->param_info, 0, sizeof(fn_type_id->param_info[0]) * fn_type_id->param_count); } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { -- cgit v1.2.3 From 96ecb402590df7a02526009f1630f27e14a0e77c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 17:53:06 -0400 Subject: add fuzz tests for std.atomic.Stack --- src/ir.cpp | 5 +++ std/atomic/stack.zig | 87 +++++++++++++++++++++++++++++++++++++++++++++++++--- std/heap.zig | 64 +++++++++++++++++++++++++++++++++----- 3 files changed, 143 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 4bf8240472..469900bf07 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18184,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr } else { if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) return ira->codegen->builtin_types.entry_invalid; + if (ordering == AtomicOrderUnordered) { + ir_add_error(ira, instruction->ordering, + buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); + return ira->codegen->builtin_types.entry_invalid; + } } if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 4ceecb7b1d..a1e686155c 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -1,3 +1,6 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; + /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Stack(comptime T: type) type { return struct { @@ -20,26 +23,100 @@ pub fn Stack(comptime T: type) type { /// being the first item in the stack, returns the other item that was there. pub fn pushFirst(self: &Self, node: &Node) ?&Node { node.next = null; - return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel); + return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); } pub fn push(self: &Self, node: &Node) void { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); while (true) { node.next = root; - root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break; + root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; } } pub fn pop(self: &Self) ?&Node { var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); while (true) { - root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root; + root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; } } pub fn isEmpty(self: &Self) bool { - return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null; + return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null; } }; } + +const std = @import("std"); +const Context = struct { + allocator: &std.mem.Allocator, + stack: &Stack(i32), + put_sum: isize, + get_sum: isize, + puts_done: u8, // TODO make this a bool +}; +const puts_per_thread = 1000; +const put_thread_count = 3; + +test "std.atomic.stack" { + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + var stack = Stack(i32).init(); + var context = Context { + .allocator = a, + .stack = &stack, + .put_sum = 0, + .get_sum = 0, + .puts_done = 0, + }; + + var putters: [put_thread_count]&std.os.Thread = undefined; + for (putters) |*t| { + *t = try std.os.spawnThreadAllocator(a, &context, startPuts); + } + var getters: [put_thread_count]&std.os.Thread = undefined; + for (getters) |*t| { + *t = try std.os.spawnThreadAllocator(a, &context, startGets); + } + + for (putters) |t| t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| t.wait(); + + std.debug.assert(context.put_sum == context.get_sum); +} + +fn startPuts(ctx: &Context) u8 { + var put_count: usize = puts_per_thread; + var r = std.rand.DefaultPrng.init(0xdeadbeef); + while (put_count != 0) : (put_count -= 1) { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + const x = @bitCast(i32, r.random.scalar(u32)); + const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; + node.data = x; + ctx.stack.push(node); + _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); + } + return 0; +} + +fn startGets(ctx: &Context) u8 { + while (true) { + while (ctx.stack.pop()) |node| { + std.os.time.sleep(0, 1); // let the os scheduler be our fuzz + _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); + } + + if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { + break; + } + } + return 0; +} diff --git a/std/heap.zig b/std/heap.zig index b3a1e6bf27..d632b44cd1 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -47,13 +47,6 @@ pub const DirectAllocator = struct { const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void; - //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122}; - //pub const want_safety = switch (builtin.mode) { - // builtin.Mode.Debug => true, - // builtin.Mode.ReleaseSafe => true, - // else => false, - //}; - pub fn init() DirectAllocator { return DirectAllocator { .allocator = Allocator { @@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct { fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); - const addr = @ptrToInt(&self.buffer[self.end_index]); + const addr = @ptrToInt(self.buffer.ptr) + self.end_index; const rem = @rem(addr, alignment); const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); const adjusted_index = self.end_index + march_forward_bytes; @@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct { fn free(allocator: &Allocator, bytes: []u8) void { } }; +/// lock free +pub const ThreadSafeFixedBufferAllocator = struct { + allocator: Allocator, + end_index: usize, + buffer: []u8, + + pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { + return ThreadSafeFixedBufferAllocator { + .allocator = Allocator { + .allocFn = alloc, + .reallocFn = realloc, + .freeFn = free, + }, + .buffer = buffer, + .end_index = 0, + }; + } + + fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); + var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); + while (true) { + const addr = @ptrToInt(self.buffer.ptr) + end_index; + const rem = @rem(addr, alignment); + const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); + const adjusted_index = end_index + march_forward_bytes; + const new_end_index = adjusted_index + n; + if (new_end_index > self.buffer.len) { + return error.OutOfMemory; + } + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, + builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index]; + } + } + + fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + if (new_size <= old_mem.len) { + return old_mem[0..new_size]; + } else { + const result = try alloc(allocator, new_size, alignment); + mem.copy(u8, result, old_mem); + return result; + } + } + + fn free(allocator: &Allocator, bytes: []u8) void { } +}; + test "c_allocator" { @@ -363,6 +404,13 @@ test "FixedBufferAllocator" { try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } +test "ThreadSafeFixedBufferAllocator" { + var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); + + try testAllocator(&fixed_buffer_allocator.allocator); + try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); +} + fn testAllocator(allocator: &mem.Allocator) !void { var slice = try allocator.alloc(&i32, 100); -- cgit v1.2.3 From 998e25a01e8b3ada235aee4a9f785a7454de4b3f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Apr 2018 23:47:39 -0400 Subject: pthread support working --- src/all_types.hpp | 1 + src/analyze.cpp | 8 ++++++++ src/codegen.cpp | 2 ++ std/os/index.zig | 14 ++++++++------ std/os/test.zig | 4 ++-- 5 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..f08b870b37 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1486,6 +1486,7 @@ struct CodeGen { ZigList link_libs_list; LinkLib *libc_link_lib; + LinkLib *pthread_link_lib; // add -framework [name] args to linker ZigList darwin_frameworks; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..8a9d236790 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6049,10 +6049,15 @@ LinkLib *create_link_lib(Buf *name) { LinkLib *add_link_lib(CodeGen *g, Buf *name) { bool is_libc = buf_eql_str(name, "c"); + bool is_pthread = buf_eql_str(name, "pthread"); if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; + if (is_pthread && g->pthread_link_lib != nullptr) { + return g->pthread_link_lib; + } + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { @@ -6066,6 +6071,9 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc) g->libc_link_lib = link_lib; + if (is_pthread) + g->pthread_link_lib = link_lib; + return link_lib; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 2d8c385f44..9f064d5f19 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -145,6 +145,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); + g->pthread_link_lib = create_link_lib(buf_create_from_str("pthread")); } return g; @@ -6373,6 +6374,7 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); + buf_appendf(contents, "pub const link_pthread = %s;\n", bool_to_str(g->pthread_link_lib != nullptr)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); diff --git a/std/os/index.zig b/std/os/index.zig index 3669dca198..fa1cc418a5 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2352,12 +2352,11 @@ pub const Thread = struct { stack: []u8, pthread_handle: pthread_t, - pub const use_pthreads = is_posix and builtin.link_libc; - const pthread_t = if (use_pthreads) c.pthread_t else void; - const pid_t = if (!use_pthreads) i32 else void; + const pthread_t = if (builtin.link_pthread) c.pthread_t else void; + const pid_t = if (!builtin.link_pthread) i32 else void; pub fn wait(self: &const Thread) void { - if (use_pthreads) { + if (builtin.link_pthread) { const err = c.pthread_join(self.pthread_handle, null); switch (err) { 0 => {}, @@ -2407,6 +2406,9 @@ pub const SpawnThreadError = error { /// be copied. SystemResources, + /// pthreads requires at least 16384 bytes of stack space + StackTooSmall, + Unexpected, }; @@ -2473,7 +2475,7 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread if (builtin.os == builtin.Os.windows) { // use windows API directly @compileError("TODO support spawnThread for Windows"); - } else if (Thread.use_pthreads) { + } else if (builtin.link_pthread) { // use pthreads var attr: c.pthread_attr_t = undefined; if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; @@ -2481,7 +2483,7 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread const stack_size = stack_end - @ptrToInt(stack.ptr); if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) { - return SpawnThreadError.SystemResources; + return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes } const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); diff --git a/std/os/test.zig b/std/os/test.zig index 9a155c027a..87486bde4f 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -57,8 +57,8 @@ test "spawn threads" { const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1); const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2); - var stack1: [1024]u8 = undefined; - var stack2: [1024]u8 = undefined; + var stack1: [20 * 1024]u8 = undefined; + var stack2: [20 * 1024]u8 = undefined; const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2); const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2); -- cgit v1.2.3 From bf8e419d2b7853f5cb5aba4dcba45ae28a3840aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Apr 2018 00:40:04 -0400 Subject: linux uses pthreads when linking against libc --- src/all_types.hpp | 1 - src/analyze.cpp | 8 -------- src/codegen.cpp | 2 -- std/c/index.zig | 10 +++++----- std/os/index.zig | 9 +++++---- 5 files changed, 10 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index f08b870b37..d1b2ad61d2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1486,7 +1486,6 @@ struct CodeGen { ZigList link_libs_list; LinkLib *libc_link_lib; - LinkLib *pthread_link_lib; // add -framework [name] args to linker ZigList darwin_frameworks; diff --git a/src/analyze.cpp b/src/analyze.cpp index 8a9d236790..1ecfe32f4c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6049,15 +6049,10 @@ LinkLib *create_link_lib(Buf *name) { LinkLib *add_link_lib(CodeGen *g, Buf *name) { bool is_libc = buf_eql_str(name, "c"); - bool is_pthread = buf_eql_str(name, "pthread"); if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; - if (is_pthread && g->pthread_link_lib != nullptr) { - return g->pthread_link_lib; - } - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { @@ -6071,9 +6066,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc) g->libc_link_lib = link_lib; - if (is_pthread) - g->pthread_link_lib = link_lib; - return link_lib; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 9f064d5f19..2d8c385f44 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -145,7 +145,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); - g->pthread_link_lib = create_link_lib(buf_create_from_str("pthread")); } return g; @@ -6374,7 +6373,6 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); - buf_appendf(contents, "pub const link_pthread = %s;\n", bool_to_str(g->pthread_link_lib != nullptr)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); diff --git a/std/c/index.zig b/std/c/index.zig index 5ea7145cd3..34269d2aa2 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -54,12 +54,12 @@ pub extern "c" fn realloc(&c_void, usize) ?&c_void; pub extern "c" fn free(&c_void) void; pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; -pub extern "c" fn pthread_create(noalias newthread: &pthread_t, +pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; -pub extern "c" fn pthread_attr_init(attr: &pthread_attr_t) c_int; -pub extern "c" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; -pub extern "c" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; -pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; +pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; +pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; pub const pthread_t = &@OpaqueType(); diff --git a/std/os/index.zig b/std/os/index.zig index 8681a018b9..85e46a1bf9 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2352,11 +2352,12 @@ pub const Thread = struct { stack: []u8, pthread_handle: pthread_t, - const pthread_t = if (builtin.link_pthread) c.pthread_t else void; - const pid_t = if (!builtin.link_pthread) i32 else void; + pub const use_pthreads = is_posix and builtin.link_libc; + const pthread_t = if (use_pthreads) c.pthread_t else void; + const pid_t = if (!use_pthreads) i32 else void; pub fn wait(self: &const Thread) void { - if (builtin.link_pthread) { + if (use_pthreads) { const err = c.pthread_join(self.pthread_handle, null); switch (err) { 0 => {}, @@ -2475,7 +2476,7 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start if (builtin.os == builtin.Os.windows) { // use windows API directly @compileError("TODO support spawnThread for Windows"); - } else if (builtin.link_pthread) { + } else if (Thread.use_pthreads) { // use pthreads var attr: c.pthread_attr_t = undefined; if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; -- cgit v1.2.3 From 66aa760f83529cf932d35090adfa6fb84264ff7a Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sun, 29 Apr 2018 14:03:55 +0300 Subject: More FnDef TypeInfo generation. --- src/codegen.cpp | 4 ++++ src/ir.cpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 414a34d5cb..507b9afe8b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6500,6 +6500,10 @@ static void define_builtin_compile_vars(CodeGen *g) { " fn_type: type,\n" " inline_type: Inline,\n" " calling_convention: CallingConvention,\n" + " is_var_args: bool,\n" + " is_extern: bool,\n" + " is_export: bool,\n" + " lib_name: ?[]const u8,\n" "\n" " const Inline = enum {\n" " Auto,\n" diff --git a/src/ir.cpp b/src/ir.cpp index 8fadc7f7a3..e3add4d612 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15920,7 +15920,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; // @TODO Add fields - ConstExprValue *fn_def_fields = create_const_vals(3); + ConstExprValue *fn_def_fields = create_const_vals(7); fn_def_val->data.x_struct.fields = fn_def_fields; // fn_type: type @@ -15938,6 +15938,40 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_fields[2].special = ConstValSpecialStatic; fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention"); bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); + // is_var_args: bool + ensure_field_index(fn_def_val->type, "is_var_args", 3); + fn_def_fields[3].special = ConstValSpecialStatic; + fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool; + fn_def_fields[3].data.x_bool = fn_node->is_var_args; + // is_extern: bool + ensure_field_index(fn_def_val->type, "is_extern", 4); + fn_def_fields[4].special = ConstValSpecialStatic; + fn_def_fields[4].type = ira->codegen->builtin_types.entry_bool; + fn_def_fields[4].data.x_bool = fn_node->is_extern; + // is_export: bool + ensure_field_index(fn_def_val->type, "is_export", 5); + fn_def_fields[5].special = ConstValSpecialStatic; + fn_def_fields[5].type = ira->codegen->builtin_types.entry_bool; + fn_def_fields[5].data.x_bool = fn_node->is_export; + // lib_name: ?[]const u8 + ensure_field_index(fn_def_val->type, "lib_name", 6); + fn_def_fields[6].special = ConstValSpecialStatic; + fn_def_fields[6].type = get_maybe_type(ira->codegen, + get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen, + ira->codegen->builtin_types.entry_u8, true))); + + + if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) + { + fn_def_fields[6].data.x_maybe = create_const_vals(1); + // @TODO Figure out if lib_name is always non-null for extern fns. + ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); + init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); + } + else + { + fn_def_fields[6].data.x_maybe = nullptr; + } inner_fields[2].data.x_union.payload = fn_def_val; break; -- cgit v1.2.3 From 013f548202ae1ffb584c211a0ea2cea53b745583 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Sun, 29 Apr 2018 15:40:26 +0300 Subject: Finished FnDef TypeInfo generation (warning: may be buggy). --- src/codegen.cpp | 8 +++++--- src/ir.cpp | 47 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 507b9afe8b..db69708e9a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6491,12 +6491,12 @@ static void define_builtin_compile_vars(CodeGen *g) { " is_pub: bool,\n" " data: Data,\n" "\n" - " const Data = union(enum) {\n" + " pub const Data = union(enum) {\n" " Type: type,\n" " Var: type,\n" " Fn: FnDef,\n" "\n" - " const FnDef = struct {\n" + " pub const FnDef = struct {\n" " fn_type: type,\n" " inline_type: Inline,\n" " calling_convention: CallingConvention,\n" @@ -6504,8 +6504,10 @@ static void define_builtin_compile_vars(CodeGen *g) { " is_extern: bool,\n" " is_export: bool,\n" " lib_name: ?[]const u8,\n" + " return_type: type,\n" + " arg_names: [][] const u8,\n" "\n" - " const Inline = enum {\n" + " pub const Inline = enum {\n" " Auto,\n" " Always,\n" " Never,\n" diff --git a/src/ir.cpp b/src/ir.cpp index e3add4d612..3bce9c51cd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15911,6 +15911,10 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; assert(!fn_entry->is_test); + analyze_fn_body(ira->codegen, fn_entry); + if (fn_entry->anal_state == FnAnalStateInvalid) + return; + AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); ConstExprValue *fn_def_val = create_const_vals(1); @@ -15919,8 +15923,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; - // @TODO Add fields - ConstExprValue *fn_def_fields = create_const_vals(7); + ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; // fn_type: type @@ -15940,9 +15943,10 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc); // is_var_args: bool ensure_field_index(fn_def_val->type, "is_var_args", 3); + bool is_varargs = fn_node->is_var_args; fn_def_fields[3].special = ConstValSpecialStatic; fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool; - fn_def_fields[3].data.x_bool = fn_node->is_var_args; + fn_def_fields[3].data.x_bool = is_varargs; // is_extern: bool ensure_field_index(fn_def_val->type, "is_extern", 4); fn_def_fields[4].special = ConstValSpecialStatic; @@ -15959,18 +15963,47 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true))); - - if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_maybe = create_const_vals(1); - // @TODO Figure out if lib_name is always non-null for extern fns. ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); } else - { fn_def_fields[6].data.x_maybe = nullptr; + // return_type: type + ensure_field_index(fn_def_val->type, "return_type", 7); + fn_def_fields[7].special = ConstValSpecialStatic; + fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; + // @TODO Check whether this is correct. + if (fn_entry->src_implicit_return_type != nullptr) + fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; + else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) + fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.gen_return_type; + else + fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + // arg_names: [][] const u8 + ensure_field_index(fn_def_val->type, "arg_names", 8); + size_t fn_arg_count = fn_entry->variable_list.length; + ConstExprValue *fn_arg_name_array = create_const_vals(1); + fn_arg_name_array->special = ConstValSpecialStatic; + fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, + get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true)), fn_arg_count); + fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; + fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone; + fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); + + init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); + + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) + { + VariableTableEntry *arg_var = fn_entry->variable_list.at(fn_arg_index); + ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.s_none.elements[fn_arg_index]; + ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); + init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); + fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; + fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; -- cgit v1.2.3 From 76ab1d2b6c9eedd861920ae6b6f8ee06aa482159 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 14:20:56 -0400 Subject: support foo.* for ptr deref See #770 --- doc/langref.html.in | 6 ++++-- src/all_types.hpp | 6 ++++++ src/analyze.cpp | 1 + src/ast_render.cpp | 9 +++++++++ src/ir.cpp | 12 ++++++++++-- src/parser.cpp | 30 ++++++++++++++++++++++++------ test/behavior.zig | 1 + test/cases/pointers.zig | 14 ++++++++++++++ 8 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 test/cases/pointers.zig (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 16fafdaad9..9fb2ebf9f5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5958,10 +5958,12 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression) FieldAccessExpression = "." Symbol +PtrDerefExpression = ".*" + FnCallExpression = "(" list(Expression, ",") ")" ArrayAccessExpression = "[" Expression "]" @@ -5974,7 +5976,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/src/all_types.hpp b/src/all_types.hpp index d1b2ad61d2..2993589f7b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -379,6 +379,7 @@ enum NodeType { NodeTypeArrayAccessExpr, NodeTypeSliceExpr, NodeTypeFieldAccessExpr, + NodeTypePtrDeref, NodeTypeUse, NodeTypeBoolLiteral, NodeTypeNullLiteral, @@ -603,6 +604,10 @@ struct AstNodeFieldAccessExpr { Buf *field_name; }; +struct AstNodePtrDerefExpr { + AstNode *target; +}; + enum PrefixOp { PrefixOpInvalid, PrefixOpBoolNot, @@ -911,6 +916,7 @@ struct AstNode { AstNodeCompTime comptime_expr; AstNodeAsmExpr asm_expr; AstNodeFieldAccessExpr field_access_expr; + AstNodePtrDerefExpr ptr_deref_expr; AstNodeContainerDecl container_decl; AstNodeStructField struct_field; AstNodeStringLiteral string_literal; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..99712cbfaf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3275,6 +3275,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeUnreachable: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: + case NodeTypePtrDeref: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2c3e1fc873..3e5ef0fcdb 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -222,6 +222,8 @@ static const char *node_type_str(NodeType node_type) { return "AsmExpr"; case NodeTypeFieldAccessExpr: return "FieldAccessExpr"; + case NodeTypePtrDeref: + return "PtrDerefExpr"; case NodeTypeContainerDecl: return "ContainerDecl"; case NodeTypeStructField: @@ -696,6 +698,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { print_symbol(ar, rhs); break; } + case NodeTypePtrDeref: + { + AstNode *lhs = node->data.ptr_deref_expr.target; + render_node_ungrouped(ar, lhs); + fprintf(ar->f, ".*"); + break; + } case NodeTypeUndefinedLiteral: fprintf(ar->f, "undefined"); break; diff --git a/src/ir.cpp b/src/ir.cpp index 469900bf07..8c7232722e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4548,8 +4548,14 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + AstNode *expr_node; + if (node->type == NodeTypePrefixOpExpr) { + expr_node = node->data.prefix_op_expr.primary_expr; + } else if (node->type == NodeTypePtrDeref) { + expr_node = node->data.ptr_deref_expr.target; + } else { + zig_unreachable(); + } IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) @@ -6527,6 +6533,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, ptr_instruction); } + case NodeTypePtrDeref: + return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: diff --git a/src/parser.cpp b/src/parser.cpp index 4b70e904b8..c02546a99d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1046,11 +1046,12 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index } /* -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression = "[" Expression ".." option(Expression) "]" FieldAccessExpression : token(Dot) token(Symbol) +PtrDerefExpression = ".*" StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -1131,13 +1132,27 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, } else if (first_token->id == TokenIdDot) { *token_index += 1; - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); + Token *token = &pc->tokens->at(*token_index); + + if (token->id == TokenIdSymbol) { + *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); - node->data.field_access_expr.struct_expr = primary_expr; - node->data.field_access_expr.field_name = token_buf(name_token); + AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); + node->data.field_access_expr.struct_expr = primary_expr; + node->data.field_access_expr.field_name = token_buf(token); + + primary_expr = node; + } else if (token->id == TokenIdStar) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); + node->data.ptr_deref_expr.target = primary_expr; + + primary_expr = node; + } else { + ast_invalid_token_error(pc, token); + } - primary_expr = node; } else { return primary_expr; } @@ -3012,6 +3027,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeFieldAccessExpr: visit_field(&node->data.field_access_expr.struct_expr, visit, context); break; + case NodeTypePtrDeref: + visit_field(&node->data.ptr_deref_expr.target, visit, context); + break; case NodeTypeUse: visit_field(&node->data.use.expr, visit, context); break; diff --git a/test/behavior.zig b/test/behavior.zig index 2c10c6d71b..cb484b39a5 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -33,6 +33,7 @@ comptime { _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/null.zig"); + _ = @import("cases/pointers.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig new file mode 100644 index 0000000000..87b3d25a74 --- /dev/null +++ b/test/cases/pointers.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const assert = std.debug.assert; + +test "dereference pointer" { + comptime testDerefPtr(); + testDerefPtr(); +} + +fn testDerefPtr() void { + var x: i32 = 1234; + var y = &x; + y.* += 1; + assert(x == 1235); +} -- cgit v1.2.3 From a35b366eb64272c6d4646aedc035a837ed0c3cb0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Apr 2018 20:35:54 -0400 Subject: [breaking] delete ptr deref prefix op start using zig-fmt-pointer-reform branch build of zig fmt to fix code to use the new syntax all of test/cases/* are processed, but there are more left to be done - all the std lib used by the behavior tests --- src/all_types.hpp | 1 - src/ast_render.cpp | 1 - src/ir.cpp | 2 - src/parser.cpp | 13 +- src/translate_c.cpp | 83 ++- std/debug/index.zig | 233 ++++---- std/math/index.zig | 75 +-- std/mem.zig | 197 ++++--- std/zig/parser.zig | 9 +- test/cases/align.zig | 86 ++- test/cases/alignof.zig | 6 +- test/cases/array.zig | 41 +- test/cases/bitcast.zig | 8 +- test/cases/bugs/394.zig | 17 +- test/cases/bugs/655.zig | 2 +- test/cases/bugs/656.zig | 11 +- test/cases/bugs/828.zig | 10 +- test/cases/bugs/920.zig | 19 +- test/cases/cast.zig | 72 ++- test/cases/coroutines.zig | 18 +- test/cases/defer.zig | 15 +- test/cases/enum.zig | 606 +++++++++++++++++++-- test/cases/enum_with_members.zig | 10 +- test/cases/error.zig | 56 +- test/cases/eval.zig | 143 +++-- test/cases/fn.zig | 44 +- test/cases/for.zig | 44 +- test/cases/generics.zig | 42 +- test/cases/if.zig | 1 - test/cases/import/a_namespace.zig | 4 +- test/cases/ir_block_deps.zig | 4 +- test/cases/math.zig | 92 ++-- test/cases/misc.zig | 225 +++++--- .../namespace_depends_on_compile_var/index.zig | 2 +- test/cases/null.zig | 27 +- .../ref_var_in_if_after_if_2nd_switch_prong.zig | 2 +- test/cases/reflection.zig | 5 +- test/cases/slice.zig | 8 +- test/cases/struct.zig | 83 +-- test/cases/struct_contains_null_ptr_itself.zig | 1 - test/cases/struct_contains_slice_of_itself.zig | 2 +- test/cases/switch.zig | 49 +- test/cases/switch_prong_err_enum.zig | 8 +- test/cases/switch_prong_implicit_cast.zig | 8 +- test/cases/try.zig | 8 +- test/cases/undefined.zig | 4 +- test/cases/union.zig | 87 +-- test/cases/var_args.zig | 25 +- test/cases/while.zig | 65 ++- 49 files changed, 1694 insertions(+), 880 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 2993589f7b..a25c99edda 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -614,7 +614,6 @@ enum PrefixOp { PrefixOpBinNot, PrefixOpNegation, PrefixOpNegationWrap, - PrefixOpDereference, PrefixOpMaybe, PrefixOpUnwrapMaybe, }; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3e5ef0fcdb..0cb8bf4e93 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -66,7 +66,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; - case PrefixOpDereference: return "*"; case PrefixOpMaybe: return "?"; case PrefixOpUnwrapMaybe: return "??"; } diff --git a/src/ir.cpp b/src/ir.cpp index 8c7232722e..ff5afe138c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4696,8 +4696,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); - case PrefixOpDereference: - return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case PrefixOpMaybe: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); case PrefixOpUnwrapMaybe: diff --git a/src/parser.cpp b/src/parser.cpp index c02546a99d..4763d3b987 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1165,10 +1165,8 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdDash: return PrefixOpNegation; case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; - case TokenIdStar: return PrefixOpDereference; case TokenIdMaybe: return PrefixOpMaybe; case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; - case TokenIdStarStar: return PrefixOpDereference; default: return PrefixOpInvalid; } } @@ -1214,7 +1212,7 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { /* PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression -PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1237,15 +1235,6 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); AstNode *parent_node = node; - if (token->id == TokenIdStarStar) { - // pretend that we got 2 star tokens - - parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token); - parent_node->data.prefix_op_expr.primary_expr = node; - parent_node->data.prefix_op_expr.prefix_op = PrefixOpDereference; - - node->column += 1; - } AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true); node->data.prefix_op_expr.primary_expr = prefix_op_expr; diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 965a8963bd..70a98dcc2e 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -247,6 +247,12 @@ static AstNode *trans_create_node_field_access_str(Context *c, AstNode *containe return trans_create_node_field_access(c, container, buf_create_from_str(field_name)); } +static AstNode *trans_create_node_ptr_deref(Context *c, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypePtrDeref); + node->data.ptr_deref_expr.target = child_node; + return node; +} + static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) { AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); node->data.prefix_op_expr.prefix_op = op; @@ -1412,8 +1418,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result AstNode *operation_type_cast = trans_c_cast(c, rhs_location, stmt->getComputationLHSType(), stmt->getLHS()->getType(), - trans_create_node_prefix_op(c, PrefixOpDereference, - trans_create_node_symbol(c, tmp_var_name))); + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name))); // result_type(... >> u5(rhs)) AstNode *result_type_cast = trans_c_cast(c, rhs_location, @@ -1426,7 +1431,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result // *_ref = ... AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)), BinOpTypeAssign, result_type_cast); @@ -1436,7 +1441,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result // break :x *_ref child_scope->node->data.block.statements.append( trans_create_node_break(c, label_name, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)))); } @@ -1483,11 +1488,11 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, if (rhs == nullptr) return nullptr; AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)), BinOpTypeAssign, trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)), bin_op, rhs)); @@ -1496,7 +1501,7 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, // break :x *_ref child_scope->node->data.block.statements.append( trans_create_node_break(c, label_name, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)))); return child_scope->node; @@ -1817,13 +1822,13 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr // const _tmp = *_ref; Buf* tmp_var_name = buf_create_from_str("_tmp"); AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name))); child_scope->node->data.block.statements.append(tmp_var_decl); // *_ref += 1; AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name)), assign_op, trans_create_node_unsigned(c, 1)); @@ -1871,14 +1876,14 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra // *_ref += 1; AstNode *assign_statement = trans_create_node_bin_op(c, - trans_create_node_prefix_op(c, PrefixOpDereference, + trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name)), assign_op, trans_create_node_unsigned(c, 1)); child_scope->node->data.block.statements.append(assign_statement); // break :x *_ref - AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference, + AstNode *deref_expr = trans_create_node_ptr_deref(c, trans_create_node_symbol(c, ref_var_name)); child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, deref_expr)); @@ -1923,7 +1928,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc if (is_fn_ptr) return value_node; AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); - return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped); + return trans_create_node_ptr_deref(c, unwrapped); } case UO_Plus: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); @@ -4469,27 +4474,45 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } } -static PrefixOp ctok_to_prefix_op(CTok *token) { - switch (token->id) { - case CTokIdBang: return PrefixOpBoolNot; - case CTokIdMinus: return PrefixOpNegation; - case CTokIdTilde: return PrefixOpBinNot; - case CTokIdAsterisk: return PrefixOpDereference; - default: return PrefixOpInvalid; - } -} static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) { CTok *op_tok = &ctok->tokens.at(*tok_i); - PrefixOp prefix_op = ctok_to_prefix_op(op_tok); - if (prefix_op == PrefixOpInvalid) { - return parse_ctok_suffix_op_expr(c, ctok, tok_i); - } - *tok_i += 1; - AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); - if (prefix_op_expr == nullptr) - return nullptr; - return trans_create_node_prefix_op(c, prefix_op, prefix_op_expr); + switch (op_tok->id) { + case CTokIdBang: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_prefix_op(c, PrefixOpBoolNot, prefix_op_expr); + } + case CTokIdMinus: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_prefix_op(c, PrefixOpNegation, prefix_op_expr); + } + case CTokIdTilde: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_prefix_op(c, PrefixOpBinNot, prefix_op_expr); + } + case CTokIdAsterisk: + { + *tok_i += 1; + AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i); + if (prefix_op_expr == nullptr) + return nullptr; + return trans_create_node_ptr_deref(c, prefix_op_expr); + } + default: + return parse_ctok_suffix_op_expr(c, ctok, tok_i); + } } static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 9057f157de..36ac2e8a3f 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -104,9 +104,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn { var panicking: u8 = 0; // TODO make this a bool -pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize, - comptime format: []const u8, args: ...) noreturn -{ +pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { @setCold(true); if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { @@ -132,9 +130,7 @@ const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; -pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, - debug_info: &ElfStackTrace, tty_color: bool) !void -{ +pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool) !void { var frame_index: usize = undefined; var frames_left: usize = undefined; if (stack_trace.index < stack_trace.instruction_addresses.len) { @@ -154,9 +150,7 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, } } -pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, - debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void -{ +pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { const AddressState = union(enum) { NotLookingForStartAddress, LookingForStartAddress: usize, @@ -166,14 +160,14 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, // else AddressState.NotLookingForStartAddress; var addr_state: AddressState = undefined; if (start_addr) |addr| { - addr_state = AddressState { .LookingForStartAddress = addr }; + addr_state = AddressState{ .LookingForStartAddress = addr }; } else { addr_state = AddressState.NotLookingForStartAddress; } var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) { - const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize)); + while (fp != 0) : (fp = @intToPtr(&const usize, fp).*) { + const return_address = @intToPtr(&const usize, fp + @sizeOf(usize)).*; switch (addr_state) { AddressState.NotLookingForStartAddress => {}, @@ -200,32 +194,32 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us // in practice because the compiler dumps everything in a single // object file. Future improvement: use external dSYM data when // available. - const unknown = macho.Symbol { .name = "???", .address = address }; + const unknown = macho.Symbol{ + .name = "???", + .address = address, + }; const symbol = debug_info.symbol_table.search(address) ?? &unknown; - try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ - DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", - symbol.name, address); + try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); }, else => { const compile_unit = findCompileUnit(debug_info, address) catch { - try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", - address); + try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", address); return; }; const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { defer line_info.deinit(); - try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ - DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", - line_info.file_name, line_info.line, line_info.column, - address, compile_unit_name); + try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", line_info.file_name, line_info.line, line_info.column, address, compile_unit_name); if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { if (line_info.column == 0) { try out_stream.write("\n"); } else { - {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - }} + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } + } try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); } } else |err| switch (err) { @@ -233,7 +227,8 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us else => return err, } } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { + error.MissingDebugInfo, + error.InvalidDebugInfo => { try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); }, else => return err, @@ -247,7 +242,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { builtin.ObjectFormat.elf => { const st = try allocator.create(ElfStackTrace); errdefer allocator.destroy(st); - *st = ElfStackTrace { + st.* = ElfStackTrace{ .self_exe_file = undefined, .elf = undefined, .debug_info = undefined, @@ -279,9 +274,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { const st = try allocator.create(ElfStackTrace); errdefer allocator.destroy(st); - *st = ElfStackTrace { - .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)), - }; + st.* = ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) }; return st; }, @@ -325,8 +318,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con } } - if (amt_read < buf.len) - return error.EndOfFile; + if (amt_read < buf.len) return error.EndOfFile; } } @@ -418,10 +410,8 @@ const Constant = struct { signed: bool, fn asUnsignedLe(self: &const Constant) !u64 { - if (self.payload.len > @sizeOf(u64)) - return error.InvalidDebugInfo; - if (self.signed) - return error.InvalidDebugInfo; + if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; + if (self.signed) return error.InvalidDebugInfo; return mem.readInt(self.payload, u64, builtin.Endian.Little); } }; @@ -438,15 +428,14 @@ const Die = struct { fn getAttr(self: &const Die, id: u64) ?&const FormValue { for (self.attrs.toSliceConst()) |*attr| { - if (attr.id == id) - return &attr.value; + if (attr.id == id) return &attr.value; } return null; } fn getAttrAddr(self: &const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.Address => |value| value, else => error.InvalidDebugInfo, }; @@ -454,7 +443,7 @@ const Die = struct { fn getAttrSecOffset(self: &const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), FormValue.SecOffset => |value| value, else => error.InvalidDebugInfo, @@ -463,7 +452,7 @@ const Die = struct { fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), else => error.InvalidDebugInfo, }; @@ -471,7 +460,7 @@ const Die = struct { fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; - return switch (*form_value) { + return switch (form_value.*) { FormValue.String => |value| value, FormValue.StrPtr => |offset| getString(st, offset), else => error.InvalidDebugInfo, @@ -518,10 +507,8 @@ const LineNumberProgram = struct { prev_basic_block: bool, prev_end_sequence: bool, - pub fn init(is_stmt: bool, include_dirs: []const []const u8, - file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram - { - return LineNumberProgram { + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram { + return LineNumberProgram{ .address = 0, .file = 1, .line = 1, @@ -548,14 +535,16 @@ const LineNumberProgram = struct { return error.MissingDebugInfo; } else if (self.prev_file - 1 >= self.file_entries.len) { return error.InvalidDebugInfo; - } else &self.file_entries.items[self.prev_file - 1]; + } else + &self.file_entries.items[self.prev_file - 1]; const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { return error.InvalidDebugInfo; - } else self.include_dirs[file_entry.dir_index]; + } else + self.include_dirs[file_entry.dir_index]; const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); errdefer self.file_entries.allocator.free(file_name); - return LineInfo { + return LineInfo{ .line = if (self.prev_line >= 0) usize(self.prev_line) else 0, .column = self.prev_column, .file_name = file_name, @@ -578,8 +567,7 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { var buf = ArrayList(u8).init(allocator); while (true) { const byte = try in_stream.readByte(); - if (byte == 0) - break; + if (byte == 0) break; try buf.append(byte); } return buf.toSlice(); @@ -600,7 +588,7 @@ fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue { .Block = buf }; + return FormValue{ .Block = buf }; } fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { @@ -609,26 +597,23 @@ fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) ! } fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { - return FormValue { .Const = Constant { + return FormValue{ .Const = Constant{ .signed = signed, .payload = try readAllocBytes(allocator, in_stream, size), - }}; + } }; } fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { - return if (is_64) try in_stream.readIntLe(u64) - else u64(try in_stream.readIntLe(u32)) ; + return if (is_64) try in_stream.readIntLe(u64) else u64(try in_stream.readIntLe(u32)); } fn parseFormValueTargetAddrSize(in_stream: var) !u64 { - return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) - else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) - else unreachable; + return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable; } fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue { .Ref = buf }; + return FormValue{ .Ref = buf }; } fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue { @@ -646,11 +631,9 @@ const ParseFormValueError = error { OutOfMemory, }; -fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) - ParseFormValueError!FormValue -{ +fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { return switch (form_id) { - DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) }, + DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), @@ -662,7 +645,8 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), - DW.FORM_udata, DW.FORM_sdata => { + DW.FORM_udata, + DW.FORM_sdata => { const block_len = try readULeb128(in_stream); const signed = form_id == DW.FORM_sdata; return parseFormValueConstant(allocator, in_stream, signed, block_len); @@ -670,11 +654,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 DW.FORM_exprloc => { const size = try readULeb128(in_stream); const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue { .ExprLoc = buf }; + return FormValue{ .ExprLoc = buf }; }, - DW.FORM_flag => FormValue { .Flag = (try in_stream.readByte()) != 0 }, - DW.FORM_flag_present => FormValue { .Flag = true }, - DW.FORM_sec_offset => FormValue { .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 }, + DW.FORM_flag_present => FormValue{ .Flag = true }, + DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), @@ -685,11 +669,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 return parseFormValueRefLen(allocator, in_stream, ref_len); }, - DW.FORM_ref_addr => FormValue { .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue { .RefSig8 = try in_stream.readIntLe(u64) }, + DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLe(u64) }, - DW.FORM_string => FormValue { .String = try readStringRaw(allocator, in_stream) }, - DW.FORM_strp => FormValue { .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) }, + DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, DW.FORM_indirect => { const child_form_id = try readULeb128(in_stream); return parseFormValue(allocator, in_stream, child_form_id, is_64); @@ -705,9 +689,8 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { var result = AbbrevTable.init(st.allocator()); while (true) { const abbrev_code = try readULeb128(in_stream); - if (abbrev_code == 0) - return result; - try result.append(AbbrevTableEntry { + if (abbrev_code == 0) return result; + try result.append(AbbrevTableEntry{ .abbrev_code = abbrev_code, .tag_id = try readULeb128(in_stream), .has_children = (try in_stream.readByte()) == DW.CHILDREN_yes, @@ -718,9 +701,8 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { while (true) { const attr_id = try readULeb128(in_stream); const form_id = try readULeb128(in_stream); - if (attr_id == 0 and form_id == 0) - break; - try attrs.append(AbbrevAttr { + if (attr_id == 0 and form_id == 0) break; + try attrs.append(AbbrevAttr{ .attr_id = attr_id, .form_id = form_id, }); @@ -737,7 +719,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { } } try st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset); - try st.abbrev_table_list.append(AbbrevTableHeader { + try st.abbrev_table_list.append(AbbrevTableHeader{ .offset = abbrev_offset, .table = try parseAbbrevTable(st), }); @@ -746,8 +728,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&const AbbrevTableEntry { for (abbrev_table.toSliceConst()) |*table_entry| { - if (table_entry.abbrev_code == abbrev_code) - return table_entry; + if (table_entry.abbrev_code == abbrev_code) return table_entry; } return null; } @@ -759,14 +740,14 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) ! const abbrev_code = try readULeb128(in_stream); const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; - var result = Die { + var result = Die{ .tag_id = table_entry.tag_id, .has_children = table_entry.has_children, .attrs = ArrayList(Die.Attr).init(st.allocator()), }; try result.attrs.resize(table_entry.attrs.len); for (table_entry.attrs.toSliceConst()) |attr, i| { - result.attrs.items[i] = Die.Attr { + result.attrs.items[i] = Die.Attr{ .id = attr.attr_id, .value = try parseFormValue(st.allocator(), in_stream, attr.form_id, is_64), }; @@ -790,8 +771,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe var is_64: bool = undefined; const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); - if (unit_length == 0) - return error.MissingDebugInfo; + if (unit_length == 0) return error.MissingDebugInfo; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); if (compile_unit.index != this_index) { @@ -803,8 +783,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe // TODO support 3 and 5 if (version != 2 and version != 4) return error.InvalidDebugInfo; - const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64) - else try in_stream.readInt(st.elf.endian, u32); + const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32); const prog_start_offset = (try in_file.getPos()) + prologue_length; const minimum_instruction_length = try in_stream.readByte(); @@ -819,38 +798,37 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe const line_base = try in_stream.readByteSigned(); const line_range = try in_stream.readByte(); - if (line_range == 0) - return error.InvalidDebugInfo; + if (line_range == 0) return error.InvalidDebugInfo; const opcode_base = try in_stream.readByte(); const standard_opcode_lengths = try st.allocator().alloc(u8, opcode_base - 1); - {var i: usize = 0; while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try in_stream.readByte(); - }} + { + var i: usize = 0; + while (i < opcode_base - 1) : (i += 1) { + standard_opcode_lengths[i] = try in_stream.readByte(); + } + } var include_directories = ArrayList([]u8).init(st.allocator()); try include_directories.append(compile_unit_cwd); while (true) { const dir = try st.readString(); - if (dir.len == 0) - break; + if (dir.len == 0) break; try include_directories.append(dir); } var file_entries = ArrayList(FileEntry).init(st.allocator()); - var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), - &file_entries, target_address); + var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); while (true) { const file_name = try st.readString(); - if (file_name.len == 0) - break; + if (file_name.len == 0) break; const dir_index = try readULeb128(in_stream); const mtime = try readULeb128(in_stream); const len_bytes = try readULeb128(in_stream); - try file_entries.append(FileEntry { + try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -866,8 +844,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash if (opcode == DW.LNS_extended_op) { const op_size = try readULeb128(in_stream); - if (op_size < 1) - return error.InvalidDebugInfo; + if (op_size < 1) return error.InvalidDebugInfo; sub_op = try in_stream.readByte(); switch (sub_op) { DW.LNE_end_sequence => { @@ -884,7 +861,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe const dir_index = try readULeb128(in_stream); const mtime = try readULeb128(in_stream); const len_bytes = try readULeb128(in_stream); - try file_entries.append(FileEntry { + try file_entries.append(FileEntry{ .file_name = file_name, .dir_index = dir_index, .mtime = mtime, @@ -941,11 +918,9 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe const arg = try in_stream.readInt(st.elf.endian, u16); prog.address += arg; }, - DW.LNS_set_prologue_end => { - }, + DW.LNS_set_prologue_end => {}, else => { - if (opcode - 1 >= standard_opcode_lengths.len) - return error.InvalidDebugInfo; + if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; const len_bytes = standard_opcode_lengths[opcode - 1]; try in_file.seekForward(len_bytes); }, @@ -972,16 +947,13 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { var is_64: bool = undefined; const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); - if (unit_length == 0) - return; + if (unit_length == 0) return; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); const version = try in_stream.readInt(st.elf.endian, u16); if (version < 2 or version > 5) return error.InvalidDebugInfo; - const debug_abbrev_offset = - if (is_64) try in_stream.readInt(st.elf.endian, u64) - else try in_stream.readInt(st.elf.endian, u32); + const debug_abbrev_offset = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32); const address_size = try in_stream.readByte(); if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; @@ -992,15 +964,14 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { try st.self_exe_file.seekTo(compile_unit_pos); const compile_unit_die = try st.allocator().create(Die); - *compile_unit_die = try parseDie(st, abbrev_table, is_64); + compile_unit_die.* = try parseDie(st, abbrev_table, is_64); - if (compile_unit_die.tag_id != DW.TAG_compile_unit) - return error.InvalidDebugInfo; + if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; const pc_range = x: { if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { - const pc_end = switch (*high_pc_value) { + const pc_end = switch (high_pc_value.*) { FormValue.Address => |value| value, FormValue.Const => |value| b: { const offset = try value.asUnsignedLe(); @@ -1008,7 +979,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { }, else => return error.InvalidDebugInfo, }; - break :x PcRange { + break :x PcRange{ .start = low_pc, .end = pc_end, }; @@ -1016,13 +987,12 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { break :x null; } } else |err| { - if (err != error.MissingDebugInfo) - return err; + if (err != error.MissingDebugInfo) return err; break :x null; } }; - try st.compile_unit_list.append(CompileUnit { + try st.compile_unit_list.append(CompileUnit{ .version = version, .is_64 = is_64, .pc_range = pc_range, @@ -1040,8 +1010,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit const in_stream = &in_file_stream.stream; for (st.compile_unit_list.toSlice()) |*compile_unit| { if (compile_unit.pc_range) |range| { - if (target_address >= range.start and target_address < range.end) - return compile_unit; + if (target_address >= range.start and target_address < range.end) return compile_unit; } if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { var base_address: usize = 0; @@ -1063,8 +1032,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit } } } else |err| { - if (err != error.MissingDebugInfo) - return err; + if (err != error.MissingDebugInfo) return err; continue; } } @@ -1073,8 +1041,8 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 { const first_32_bits = try in_stream.readIntLe(u32); - *is_64 = (first_32_bits == 0xffffffff); - if (*is_64) { + is_64.* = (first_32_bits == 0xffffffff); + if (is_64.*) { return in_stream.readIntLe(u64); } else { if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; @@ -1091,13 +1059,11 @@ fn readULeb128(in_stream: var) !u64 { var operand: u64 = undefined; - if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand)) - return error.InvalidDebugInfo; + if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; result |= operand; - if ((byte & 0b10000000) == 0) - return result; + if ((byte & 0b10000000) == 0) return result; shift += 7; } @@ -1112,15 +1078,13 @@ fn readILeb128(in_stream: var) !i64 { var operand: i64 = undefined; - if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand)) - return error.InvalidDebugInfo; + if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; result |= operand; shift += 7; if ((byte & 0b10000000) == 0) { - if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) - result |= -(i64(1) << u6(shift)); + if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << u6(shift)); return result; } } @@ -1131,7 +1095,6 @@ pub const global_allocator = &global_fixed_allocator.allocator; var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); var global_allocator_mem: [100 * 1024]u8 = undefined; - // TODO make thread safe var debug_info_allocator: ?&mem.Allocator = null; var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; diff --git a/std/math/index.zig b/std/math/index.zig index 83ba055329..05de604c6c 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -47,12 +47,12 @@ pub fn forceEval(value: var) void { f32 => { var x: f32 = undefined; const p = @ptrCast(&volatile f32, &x); - *p = x; + p.* = x; }, f64 => { var x: f64 = undefined; const p = @ptrCast(&volatile f64, &x); - *p = x; + p.* = x; }, else => { @compileError("forceEval not implemented for " ++ @typeName(T)); @@ -179,7 +179,6 @@ test "math" { _ = @import("complex/index.zig"); } - pub fn min(x: var, y: var) @typeOf(x + y) { return if (x < y) x else y; } @@ -280,10 +279,10 @@ pub fn rotr(comptime T: type, x: T, r: var) T { } test "math.rotr" { - assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); - assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); - assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); - assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); + assert(rotr(u8, 0b00000001, usize(0)) == 0b00000001); + assert(rotr(u8, 0b00000001, usize(9)) == 0b10000000); + assert(rotr(u8, 0b00000001, usize(8)) == 0b00000001); + assert(rotr(u8, 0b00000001, usize(4)) == 0b00010000); assert(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); } @@ -299,14 +298,13 @@ pub fn rotl(comptime T: type, x: T, r: var) T { } test "math.rotl" { - assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); - assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); - assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); - assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); + assert(rotl(u8, 0b00000001, usize(0)) == 0b00000001); + assert(rotl(u8, 0b00000001, usize(9)) == 0b00000010); + assert(rotl(u8, 0b00000001, usize(8)) == 0b00000001); + assert(rotl(u8, 0b00000001, usize(4)) == 0b00010000); assert(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); } - pub fn Log2Int(comptime T: type) type { return @IntType(false, log2(T.bit_count)); } @@ -323,14 +321,14 @@ fn testOverflow() void { assert((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); } - pub fn absInt(x: var) !@typeOf(x) { const T = @typeOf(x); comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt comptime assert(T.is_signed); // must pass a signed integer to absInt - if (x == @minValue(@typeOf(x))) + + if (x == @minValue(@typeOf(x))) { return error.Overflow; - { + } else { @setRuntimeSafety(false); return if (x < 0) -x else x; } @@ -349,10 +347,8 @@ pub const absFloat = @import("fabs.zig").fabs; pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) - return error.Overflow; + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) return error.Overflow; return @divTrunc(numerator, denominator); } @@ -372,10 +368,8 @@ fn testDivTrunc() void { pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) - return error.Overflow; + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) return error.Overflow; return @divFloor(numerator, denominator); } @@ -395,13 +389,10 @@ fn testDivFloor() void { pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) - return error.Overflow; + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == @minValue(T) and denominator == -1) return error.Overflow; const result = @divTrunc(numerator, denominator); - if (result * denominator != numerator) - return error.UnexpectedRemainder; + if (result * denominator != numerator) return error.UnexpectedRemainder; return result; } @@ -423,10 +414,8 @@ fn testDivExact() void { pub fn mod(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (denominator < 0) - return error.NegativeDenominator; + if (denominator == 0) return error.DivisionByZero; + if (denominator < 0) return error.NegativeDenominator; return @mod(numerator, denominator); } @@ -448,10 +437,8 @@ fn testMod() void { pub fn rem(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); - if (denominator == 0) - return error.DivisionByZero; - if (denominator < 0) - return error.NegativeDenominator; + if (denominator == 0) return error.DivisionByZero; + if (denominator < 0) return error.NegativeDenominator; return @rem(numerator, denominator); } @@ -475,8 +462,7 @@ fn testRem() void { /// Result is an unsigned integer. pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { const uint = @IntType(false, @typeOf(x).bit_count); - if (x >= 0) - return uint(x); + if (x >= 0) return uint(x); return uint(-(x + 1)) + 1; } @@ -495,15 +481,12 @@ test "math.absCast" { /// Returns the negation of the integer parameter. /// Result is a signed integer. pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { - if (@typeOf(x).is_signed) - return negate(x); + if (@typeOf(x).is_signed) return negate(x); const int = @IntType(true, @typeOf(x).bit_count); - if (x > -@minValue(int)) - return error.Overflow; + if (x > -@minValue(int)) return error.Overflow; - if (x == -@minValue(int)) - return @minValue(int); + if (x == -@minValue(int)) return @minValue(int); return -int(x); } @@ -546,7 +529,7 @@ pub fn floorPowerOfTwo(comptime T: type, value: T) T { var x = value; comptime var i = 1; - inline while(T.bit_count > i) : (i *= 2) { + inline while (T.bit_count > i) : (i *= 2) { x |= (x >> i); } diff --git a/std/mem.zig b/std/mem.zig index d874f8a6c9..3ca87b35d3 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -6,14 +6,14 @@ const builtin = @import("builtin"); const mem = this; pub const Allocator = struct { - const Error = error {OutOfMemory}; + const Error = error{OutOfMemory}; /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, + allocFn: fn(self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -26,10 +26,10 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, + reallocFn: fn(self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` - freeFn: fn (self: &Allocator, old_mem: []u8) void, + freeFn: fn(self: &Allocator, old_mem: []u8) void, fn create(self: &Allocator, comptime T: type) !&T { if (@sizeOf(T) == 0) return &{}; @@ -47,7 +47,7 @@ pub const Allocator = struct { if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); const ptr = &slice[0]; - *ptr = *init; + ptr.* = init.*; return ptr; } @@ -59,9 +59,7 @@ pub const Allocator = struct { return self.alignedAlloc(T, @alignOf(T), n); } - fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, - n: usize) ![]align(alignment) T - { + fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { if (n == 0) { return (&align(alignment) T)(undefined)[0..0]; } @@ -70,7 +68,7 @@ pub const Allocator = struct { assert(byte_slice.len == byte_count); // This loop gets optimized out in ReleaseFast mode for (byte_slice) |*byte| { - *byte = undefined; + byte.* = undefined; } return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } @@ -79,9 +77,7 @@ pub const Allocator = struct { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, - old_mem: []align(alignment) T, n: usize) ![]align(alignment) T - { + fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); } @@ -97,7 +93,7 @@ pub const Allocator = struct { if (n > old_mem.len) { // This loop gets optimized out in ReleaseFast mode for (byte_slice[old_byte_slice.len..]) |*byte| { - *byte = undefined; + byte.* = undefined; } } return ([]T)(@alignCast(alignment, byte_slice)); @@ -110,9 +106,7 @@ pub const Allocator = struct { return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29, - old_mem: []align(alignment) T, n: usize) []align(alignment) T - { + fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { if (n == 0) { self.free(old_mem); return old_mem[0..0]; @@ -131,8 +125,7 @@ pub const Allocator = struct { fn free(self: &Allocator, memory: var) void { const bytes = ([]const u8)(memory); - if (bytes.len == 0) - return; + if (bytes.len == 0) return; const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } @@ -146,11 +139,13 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void { // this and automatically omit safety checks for loops @setRuntimeSafety(false); assert(dest.len >= source.len); - for (source) |s, i| dest[i] = s; + for (source) |s, i| + dest[i] = s; } pub fn set(comptime T: type, dest: []T, value: T) void { - for (dest) |*d| *d = value; + for (dest) |*d| + d.* = value; } /// Returns true if lhs < rhs, false otherwise @@ -229,8 +224,7 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { var i: usize = slice.len; while (i != 0) { i -= 1; - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } return null; } @@ -238,8 +232,7 @@ pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize { pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize { var i: usize = start_index; while (i < slice.len) : (i += 1) { - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } return null; } @@ -253,8 +246,7 @@ pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?us while (i != 0) { i -= 1; for (values) |value| { - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } } return null; @@ -264,8 +256,7 @@ pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, val var i: usize = start_index; while (i < slice.len) : (i += 1) { for (values) |value| { - if (slice[i] == value) - return i; + if (slice[i] == value) return i; } } return null; @@ -279,28 +270,23 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize /// To start looking at a different index, slice the haystack first. /// TODO is there even a better algorithm for this? pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize { - if (needle.len > haystack.len) - return null; + if (needle.len > haystack.len) return null; var i: usize = haystack.len - needle.len; while (true) : (i -= 1) { - if (mem.eql(T, haystack[i..i+needle.len], needle)) - return i; - if (i == 0) - return null; + if (mem.eql(T, haystack[i..i + needle.len], needle)) return i; + if (i == 0) return null; } } // TODO boyer-moore algorithm pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize { - if (needle.len > haystack.len) - return null; + if (needle.len > haystack.len) return null; var i: usize = start_index; const end = haystack.len - needle.len; while (i <= end) : (i += 1) { - if (eql(T, haystack[i .. i + needle.len], needle)) - return i; + if (eql(T, haystack[i..i + needle.len], needle)) return i; } return null; } @@ -355,9 +341,12 @@ pub fn readIntBE(comptime T: type, bytes: []const u8) T { } assert(bytes.len == @sizeOf(T)); var result: T = 0; - {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { - result = (result << 8) | T(bytes[i]); - }} + { + comptime var i = 0; + inline while (i < @sizeOf(T)) : (i += 1) { + result = (result << 8) | T(bytes[i]); + } + } return result; } @@ -369,9 +358,12 @@ pub fn readIntLE(comptime T: type, bytes: []const u8) T { } assert(bytes.len == @sizeOf(T)); var result: T = 0; - {comptime var i = 0; inline while (i < @sizeOf(T)) : (i += 1) { - result |= T(bytes[i]) << i * 8; - }} + { + comptime var i = 0; + inline while (i < @sizeOf(T)) : (i += 1) { + result |= T(bytes[i]) << i * 8; + } + } return result; } @@ -393,7 +385,7 @@ pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) void { }, builtin.Endian.Little => { for (buf) |*b| { - *b = @truncate(u8, bits); + b.* = @truncate(u8, bits); bits >>= 8; } }, @@ -401,7 +393,6 @@ pub fn writeInt(buf: []u8, value: var, endian: builtin.Endian) void { assert(bits == 0); } - pub fn hash_slice_u8(k: []const u8) u32 { // FNV 32-bit hash var h: u32 = 2166136261; @@ -420,7 +411,7 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { /// split(" abc def ghi ", " ") /// Will return slices for "abc", "def", "ghi", null, in that order. pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { - return SplitIterator { + return SplitIterator{ .index = 0, .buffer = buffer, .split_bytes = split_bytes, @@ -436,7 +427,7 @@ test "mem.split" { } pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { - return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle); + return if (needle.len > haystack.len) false else eql(T, haystack[0..needle.len], needle); } test "mem.startsWith" { @@ -445,10 +436,9 @@ test "mem.startsWith" { } pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool { - return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle); + return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len..], needle); } - test "mem.endsWith" { assert(endsWith(u8, "Needle in haystack", "haystack")); assert(!endsWith(u8, "Bob", "Bo")); @@ -542,29 +532,47 @@ test "testReadInt" { } fn testReadIntImpl() void { { - const bytes = []u8{ 0x12, 0x34, 0x56, 0x78 }; - assert(readInt(bytes, u32, builtin.Endian.Big) == 0x12345678); - assert(readIntBE(u32, bytes) == 0x12345678); - assert(readIntBE(i32, bytes) == 0x12345678); + const bytes = []u8{ + 0x12, + 0x34, + 0x56, + 0x78, + }; + assert(readInt(bytes, u32, builtin.Endian.Big) == 0x12345678); + assert(readIntBE(u32, bytes) == 0x12345678); + assert(readIntBE(i32, bytes) == 0x12345678); assert(readInt(bytes, u32, builtin.Endian.Little) == 0x78563412); - assert(readIntLE(u32, bytes) == 0x78563412); - assert(readIntLE(i32, bytes) == 0x78563412); + assert(readIntLE(u32, bytes) == 0x78563412); + assert(readIntLE(i32, bytes) == 0x78563412); } { - const buf = []u8{0x00, 0x00, 0x12, 0x34}; + const buf = []u8{ + 0x00, + 0x00, + 0x12, + 0x34, + }; const answer = readInt(buf, u64, builtin.Endian.Big); assert(answer == 0x00001234); } { - const buf = []u8{0x12, 0x34, 0x00, 0x00}; + const buf = []u8{ + 0x12, + 0x34, + 0x00, + 0x00, + }; const answer = readInt(buf, u64, builtin.Endian.Little); assert(answer == 0x00003412); } { - const bytes = []u8{0xff, 0xfe}; - assert(readIntBE(u16, bytes) == 0xfffe); + const bytes = []u8{ + 0xff, + 0xfe, + }; + assert(readIntBE(u16, bytes) == 0xfffe); assert(readIntBE(i16, bytes) == -0x0002); - assert(readIntLE(u16, bytes) == 0xfeff); + assert(readIntLE(u16, bytes) == 0xfeff); assert(readIntLE(i16, bytes) == -0x0101); } } @@ -577,19 +585,38 @@ fn testWriteIntImpl() void { var bytes: [4]u8 = undefined; writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); + assert(eql(u8, bytes, []u8{ + 0x12, + 0x34, + 0x56, + 0x78, + })); writeInt(bytes[0..], u32(0x78563412), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ 0x12, 0x34, 0x56, 0x78 })); + assert(eql(u8, bytes, []u8{ + 0x12, + 0x34, + 0x56, + 0x78, + })); writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big); - assert(eql(u8, bytes, []u8{ 0x00, 0x00, 0x12, 0x34 })); + assert(eql(u8, bytes, []u8{ + 0x00, + 0x00, + 0x12, + 0x34, + })); writeInt(bytes[0..], u16(0x1234), builtin.Endian.Little); - assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 })); + assert(eql(u8, bytes, []u8{ + 0x34, + 0x12, + 0x00, + 0x00, + })); } - pub fn min(comptime T: type, slice: []const T) T { var best = slice[0]; for (slice[1..]) |item| { @@ -615,9 +642,9 @@ test "mem.max" { } pub fn swap(comptime T: type, a: &T, b: &T) void { - const tmp = *a; - *a = *b; - *b = tmp; + const tmp = a.*; + a.* = b.*; + b.* = tmp; } /// In-place order reversal of a slice @@ -630,10 +657,22 @@ pub fn reverse(comptime T: type, items: []T) void { } test "std.mem.reverse" { - var arr = []i32{ 5, 3, 1, 2, 4 }; + var arr = []i32{ + 5, + 3, + 1, + 2, + 4, + }; reverse(i32, arr[0..]); - assert(eql(i32, arr, []i32{ 4, 2, 1, 3, 5 })); + assert(eql(i32, arr, []i32{ + 4, + 2, + 1, + 3, + 5, + })); } /// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1) @@ -645,10 +684,22 @@ pub fn rotate(comptime T: type, items: []T, amount: usize) void { } test "std.mem.rotate" { - var arr = []i32{ 5, 3, 1, 2, 4 }; + var arr = []i32{ + 5, + 3, + 1, + 2, + 4, + }; rotate(i32, arr[0..], 2); - assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 })); + assert(eql(i32, arr, []i32{ + 1, + 2, + 4, + 5, + 3, + })); } // TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by diff --git a/std/zig/parser.zig b/std/zig/parser.zig index b5849c3e96..79a38f00ee 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -3705,7 +3705,9 @@ pub const Parser = struct { }, ast.Node.Id.PrefixOp => { const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); + if (prefix_op_node.op != ast.Node.PrefixOp.Op.Deref) { + try stack.append(RenderState { .Expression = prefix_op_node.rhs }); + } switch (prefix_op_node.op) { ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { try stream.write("&"); @@ -3742,7 +3744,10 @@ pub const Parser = struct { }, ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), - ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Deref => { + try stack.append(RenderState { .Text = ".*" }); + try stack.append(RenderState { .Expression = prefix_op_node.rhs }); + }, ast.Node.PrefixOp.Op.Negation => try stream.write("-"), ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), ast.Node.PrefixOp.Op.Try => try stream.write("try "), diff --git a/test/cases/align.zig b/test/cases/align.zig index ad3a66a2e0..a1259e96bf 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -10,7 +10,9 @@ test "global variable alignment" { assert(@typeOf(slice) == []align(4) u8); } -fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; } +fn derp() align(@sizeOf(usize) * 2) i32 { + return 1234; +} fn noop1() align(1) void {} fn noop4() align(4) void {} @@ -22,7 +24,6 @@ test "function alignment" { noop4(); } - var baz: packed struct { a: u32, b: u32, @@ -32,7 +33,6 @@ test "packed struct alignment" { assert(@typeOf(&baz.b) == &align(1) u32); } - const blah: packed struct { a: u3, b: u3, @@ -53,29 +53,43 @@ test "implicitly decreasing pointer alignment" { assert(addUnaligned(&a, &b) == 7); } -fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) u32 { return *a + *b; } +fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) u32 { + return a.* + b.*; +} test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; assert(addUnalignedSlice((&a)[0..1], (&b)[0..1]) == 7); } -fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; } +fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { + return a[0] + b[0]; +} test "specifying alignment allows pointer cast" { testBytesAlign(0x33); } fn testBytesAlign(b: u8) void { - var bytes align(4) = []u8{b, b, b, b}; + var bytes align(4) = []u8 { + b, + b, + b, + b, + }; const ptr = @ptrCast(&u32, &bytes[0]); - assert(*ptr == 0x33333333); + assert(ptr.* == 0x33333333); } test "specifying alignment allows slice cast" { testBytesAlignSlice(0x33); } fn testBytesAlignSlice(b: u8) void { - var bytes align(4) = []u8{b, b, b, b}; + var bytes align(4) = []u8 { + b, + b, + b, + b, + }; const slice = ([]u32)(bytes[0..]); assert(slice[0] == 0x33333333); } @@ -89,11 +103,14 @@ fn expectsOnly1(x: &align(1) u32) void { expects4(@alignCast(4, x)); } fn expects4(x: &align(4) u32) void { - *x += 1; + x.* += 1; } test "@alignCast slices" { - var array align(4) = []u32{1, 1}; + var array align(4) = []u32 { + 1, + 1, + }; const slice = array[0..]; sliceExpectsOnly1(slice); assert(slice[0] == 2); @@ -105,31 +122,34 @@ fn sliceExpects4(slice: []align(4) u32) void { slice[0] += 1; } - test "implicitly decreasing fn alignment" { testImplicitlyDecreaseFnAlign(alignedSmall, 1234); testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { +fn testImplicitlyDecreaseFnAlign(ptr: fn() align(1) i32, answer: i32) void { assert(ptr() == answer); } -fn alignedSmall() align(8) i32 { return 1234; } -fn alignedBig() align(16) i32 { return 5678; } - +fn alignedSmall() align(8) i32 { + return 1234; +} +fn alignedBig() align(16) i32 { + return 5678; +} test "@alignCast functions" { assert(fnExpectsOnly1(simple4) == 0x19); } -fn fnExpectsOnly1(ptr: fn()align(1) i32) i32 { +fn fnExpectsOnly1(ptr: fn() align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); } -fn fnExpects4(ptr: fn()align(4) i32) i32 { +fn fnExpects4(ptr: fn() align(4) i32) i32 { return ptr(); } -fn simple4() align(4) i32 { return 0x19; } - +fn simple4() align(4) i32 { + return 0x19; +} test "generic function with align param" { assert(whyWouldYouEverDoThis(1) == 0x1); @@ -137,8 +157,9 @@ test "generic function with align param" { assert(whyWouldYouEverDoThis(8) == 0x1); } -fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { return 0x1; } - +fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { + return 0x1; +} test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; @@ -146,24 +167,38 @@ test "@ptrCast preserves alignment of bigger source" { assert(@typeOf(ptr) == &align(16) u8); } - test "compile-time known array index has best alignment possible" { // take full advantage of over-alignment - var array align(4) = []u8 {1, 2, 3, 4}; + var array align(4) = []u8 { + 1, + 2, + 3, + 4, + }; assert(@typeOf(&array[0]) == &align(4) u8); assert(@typeOf(&array[1]) == &u8); assert(@typeOf(&array[2]) == &align(2) u8); assert(@typeOf(&array[3]) == &u8); // because align is too small but we still figure out to use 2 - var bigger align(2) = []u64{1, 2, 3, 4}; + var bigger align(2) = []u64 { + 1, + 2, + 3, + 4, + }; assert(@typeOf(&bigger[0]) == &align(2) u64); assert(@typeOf(&bigger[1]) == &align(2) u64); assert(@typeOf(&bigger[2]) == &align(2) u64); assert(@typeOf(&bigger[3]) == &align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 - var smaller align(2) = []u32{1, 2, 3, 4}; + var smaller align(2) = []u32 { + 1, + 2, + 3, + 4, + }; testIndex(&smaller[0], 0, &align(2) u32); testIndex(&smaller[0], 1, &align(2) u32); testIndex(&smaller[0], 2, &align(2) u32); @@ -182,7 +217,6 @@ fn testIndex2(ptr: &align(4) u8, index: usize, comptime T: type) void { assert(@typeOf(&ptr[index]) == T); } - test "alignstack" { assert(fnWithAlignedStack() == 1234); } diff --git a/test/cases/alignof.zig b/test/cases/alignof.zig index 27b95c7fdc..130a2a5b44 100644 --- a/test/cases/alignof.zig +++ b/test/cases/alignof.zig @@ -1,7 +1,11 @@ const assert = @import("std").debug.assert; const builtin = @import("builtin"); -const Foo = struct { x: u32, y: u32, z: u32, }; +const Foo = struct { + x: u32, + y: u32, + z: u32, +}; test "@alignOf(T) before referencing T" { comptime assert(@alignOf(Foo) != @maxValue(usize)); diff --git a/test/cases/array.zig b/test/cases/array.zig index 577161dd16..0fb61b2a9f 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -2,9 +2,9 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "arrays" { - var array : [5]u32 = undefined; + var array: [5]u32 = undefined; - var i : u32 = 0; + var i: u32 = 0; while (i < 5) { array[i] = i + 1; i = array[i]; @@ -34,24 +34,41 @@ test "void arrays" { } test "array literal" { - const hex_mult = []u16{4096, 256, 16, 1}; + const hex_mult = []u16 { + 4096, + 256, + 16, + 1, + }; assert(hex_mult.len == 4); assert(hex_mult[1] == 256); } test "array dot len const expr" { - assert(comptime x: {break :x some_array.len == 4;}); + assert(comptime x: { + break :x some_array.len == 4; + }); } const ArrayDotLenConstExpr = struct { y: [some_array.len]u8, }; -const some_array = []u8 {0, 1, 2, 3}; - +const some_array = []u8 { + 0, + 1, + 2, + 3, +}; test "nested arrays" { - const array_of_strings = [][]const 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(u8, s, "hello")); if (i == 1) assert(mem.eql(u8, s, "this")); @@ -61,7 +78,6 @@ test "nested arrays" { } } - var s_array: [8]Sub = undefined; const Sub = struct { b: u8, @@ -70,7 +86,9 @@ const Str = struct { a: []Sub, }; test "set global var array via slice embedded in struct" { - var s = Str { .a = s_array[0..]}; + var s = Str { + .a = s_array[0..], + }; s.a[0].b = 1; s.a[1].b = 2; @@ -82,7 +100,10 @@ test "set global var array via slice embedded in struct" { } test "array literal with specified size" { - var array = [2]u8{1, 2}; + var array = [2]u8 { + 1, + 2, + }; assert(array[0] == 1); assert(array[1] == 2); } diff --git a/test/cases/bitcast.zig b/test/cases/bitcast.zig index f1f2ccd672..878140954a 100644 --- a/test/cases/bitcast.zig +++ b/test/cases/bitcast.zig @@ -10,5 +10,9 @@ fn testBitCast_i32_u32() void { assert(conv2(@maxValue(u32)) == -1); } -fn conv(x: i32) u32 { return @bitCast(u32, x); } -fn conv2(x: u32) i32 { return @bitCast(i32, x); } +fn conv(x: i32) u32 { + return @bitCast(u32, x); +} +fn conv2(x: u32) i32 { + return @bitCast(i32, x); +} diff --git a/test/cases/bugs/394.zig b/test/cases/bugs/394.zig index 071619d59c..a99bd18b28 100644 --- a/test/cases/bugs/394.zig +++ b/test/cases/bugs/394.zig @@ -1,9 +1,20 @@ -const E = union(enum) { A: [9]u8, B: u64, }; -const S = struct { x: u8, y: E, }; +const E = union(enum) { + A: [9]u8, + B: u64, +}; +const S = struct { + x: u8, + y: E, +}; const assert = @import("std").debug.assert; test "bug 394 fixed" { - const x = S { .x = 3, .y = E {.B = 1 } }; + const x = S { + .x = 3, + .y = E { + .B = 1, + }, + }; assert(x.x == 3); } diff --git a/test/cases/bugs/655.zig b/test/cases/bugs/655.zig index e6a275004c..4431767d5c 100644 --- a/test/cases/bugs/655.zig +++ b/test/cases/bugs/655.zig @@ -8,5 +8,5 @@ test "function with &const parameter with type dereferenced by namespace" { } fn foo(x: &const other_file.Integer) void { - std.debug.assert(*x == 1234); + std.debug.assert(x.* == 1234); } diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig index ce3eec8046..24a28bf411 100644 --- a/test/cases/bugs/656.zig +++ b/test/cases/bugs/656.zig @@ -14,12 +14,15 @@ test "nullable if after an if in a switch prong of a switch with 2 prongs in an } fn foo(a: bool, b: bool) void { - var prefix_op = PrefixOp { .AddrOf = Value { .align_expr = 1234 } }; - if (a) { - } else { + var prefix_op = PrefixOp { + .AddrOf = Value { + .align_expr = 1234, + }, + }; + if (a) {} else { switch (prefix_op) { PrefixOp.AddrOf => |addr_of_info| { - if (b) { } + if (b) {} if (addr_of_info.align_expr) |align_expr| { assert(align_expr == 1234); } diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig index c46548cb7a..8f329e4f82 100644 --- a/test/cases/bugs/828.zig +++ b/test/cases/bugs/828.zig @@ -1,10 +1,10 @@ const CountBy = struct { a: usize, - + const One = CountBy { .a = 1, }; - + pub fn counter(self: &const CountBy) Counter { return Counter { .i = 0, @@ -14,7 +14,7 @@ const CountBy = struct { const Counter = struct { i: usize, - + pub fn count(self: &Counter) bool { self.i += 1; return self.i <= 10; @@ -24,8 +24,8 @@ const Counter = struct { fn constCount(comptime cb: &const CountBy, comptime unused: u32) void { comptime { var cnt = cb.counter(); - if(cnt.i != 0) @compileError("Counter instance reused!"); - while(cnt.count()){} + if (cnt.i != 0) @compileError("Counter instance reused!"); + while (cnt.count()) {} } } diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig index 13c03a304f..c2b6816e94 100644 --- a/test/cases/bugs/920.zig +++ b/test/cases/bugs/920.zig @@ -12,8 +12,7 @@ const ZigTable = struct { zero_case: fn(&Random, f64) f64, }; -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, - comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64, comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable { var tables: ZigTable = undefined; tables.is_symmetric = is_symmetric; @@ -26,12 +25,12 @@ fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, co for (tables.x[2..256]) |*entry, i| { const last = tables.x[2 + i - 1]; - *entry = f_inv(v / last + f(last)); + entry.* = f_inv(v / last + f(last)); } tables.x[256] = 0; for (tables.f[0..]) |*entry, i| { - *entry = f(tables.x[i]); + entry.* = f(tables.x[i]); } return tables; @@ -40,9 +39,15 @@ fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, co const norm_r = 3.6541528853610088; const norm_v = 0.00492867323399; -fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); } -fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } -fn norm_zero_case(random: &Random, u: f64) f64 { return 0.0; } +fn norm_f(x: f64) f64 { + return math.exp(-x * x / 2.0); +} +fn norm_f_inv(y: f64) f64 { + return math.sqrt(-2.0 * math.ln(y)); +} +fn norm_zero_case(random: &Random, u: f64) f64 { + return 0.0; +} const NormalDist = blk: { @setEvalBranchQuota(30000); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 024ece0055..547cca5797 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -14,10 +14,10 @@ test "integer literal to pointer cast" { } test "pointer reinterpret const float to int" { - const float: f64 = 5.99999999999994648725e-01; + const float: f64 = 5.99999999999994648725e - 01; const float_ptr = &float; const int_ptr = @ptrCast(&const i32, float_ptr); - const int_val = *int_ptr; + const int_val = int_ptr.*; assert(int_val == 858993411); } @@ -29,25 +29,31 @@ test "implicitly cast a pointer to a const pointer of it" { } fn funcWithConstPtrPtr(x: &const &i32) void { - **x += 1; + x.*.* += 1; } test "implicitly cast a container to a const pointer of it" { - const z = Struct(void) { .x = void{} }; + const z = Struct(void) { + .x = void{}, + }; assert(0 == @sizeOf(@typeOf(z))); assert(void{} == Struct(void).pointer(z).x); assert(void{} == Struct(void).pointer(&z).x); assert(void{} == Struct(void).maybePointer(z).x); assert(void{} == Struct(void).maybePointer(&z).x); assert(void{} == Struct(void).maybePointer(null).x); - const s = Struct(u8) { .x = 42 }; + const s = Struct(u8) { + .x = 42, + }; assert(0 != @sizeOf(@typeOf(s))); assert(42 == Struct(u8).pointer(s).x); assert(42 == Struct(u8).pointer(&s).x); assert(42 == Struct(u8).maybePointer(s).x); assert(42 == Struct(u8).maybePointer(&s).x); assert(0 == Struct(u8).maybePointer(null).x); - const u = Union { .x = 42 }; + const u = Union { + .x = 42, + }; assert(42 == Union.pointer(u).x); assert(42 == Union.pointer(&u).x); assert(42 == Union.maybePointer(u).x); @@ -67,12 +73,14 @@ fn Struct(comptime T: type) type { x: T, fn pointer(self: &const Self) Self { - return *self; + return self.*; } fn maybePointer(self: ?&const Self) Self { - const none = Self { .x = if (T == void) void{} else 0 }; - return *(self ?? &none); + const none = Self { + .x = if (T == void) void{} else 0, + }; + return (self ?? &none).*; } }; } @@ -81,12 +89,14 @@ const Union = union { x: u8, fn pointer(self: &const Union) Union { - return *self; + return self.*; } fn maybePointer(self: ?&const Union) Union { - const none = Union { .x = 0 }; - return *(self ?? &none); + const none = Union { + .x = 0, + }; + return (self ?? &none).*; } }; @@ -95,11 +105,11 @@ const Enum = enum { Some, fn pointer(self: &const Enum) Enum { - return *self; + return self.*; } fn maybePointer(self: ?&const Enum) Enum { - return *(self ?? &Enum.None); + return (self ?? &Enum.None).*; } }; @@ -108,19 +118,21 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const Self = this; x: u8, fn constConst(p: &const &const Self) u8 { - return (*p).x; + return (p.*).x; } fn maybeConstConst(p: ?&const &const Self) u8 { - return (*??p).x; + return (??p.*).x; } fn constConstConst(p: &const &const &const Self) u8 { - return (**p).x; + return (p.*.*).x; } fn maybeConstConstConst(p: ?&const &const &const Self) u8 { - return (**??p).x; + return (??p.*.*).x; } }; - const s = S { .x = 42 }; + const s = S { + .x = 42, + }; const p = &s; const q = &p; const r = &q; @@ -154,7 +166,6 @@ fn boolToStr(b: bool) []const u8 { return if (b) "true" else "false"; } - test "peer resolve array and const slice" { testPeerResolveArrayConstSlice(true); comptime testPeerResolveArrayConstSlice(true); @@ -168,12 +179,12 @@ fn testPeerResolveArrayConstSlice(b: bool) void { test "integer literal to &const int" { const x: &const i32 = 3; - assert(*x == 3); + assert(x.* == 3); } test "string literal to &const []const u8" { const x: &const []const u8 = "hello"; - assert(mem.eql(u8, *x, "hello")); + assert(mem.eql(u8, x.*, "hello")); } test "implicitly cast from T to error!?T" { @@ -191,7 +202,9 @@ fn castToMaybeTypeError(z: i32) void { const f = z; const g: error!?i32 = f; - const a = A{ .a = z }; + const a = A { + .a = z, + }; const b: error!?A = a; assert((??(b catch unreachable)).a == 1); } @@ -205,7 +218,6 @@ fn implicitIntLitToMaybe() void { const g: error!?i32 = 1; } - test "return null from fn() error!?&T" { const a = returnNullFromMaybeTypeErrorRef(); const b = returnNullLitFromMaybeTypeErrorRef(); @@ -235,7 +247,6 @@ fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize { return usize(3); } - test "peer type resolution: [0]u8 and []const u8" { assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); @@ -246,7 +257,7 @@ test "peer type resolution: [0]u8 and []const u8" { } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { if (a) { - return []const u8 {}; + return []const u8{}; } return slice[0..1]; @@ -261,7 +272,6 @@ fn castToMaybeSlice() ?[]const u8 { return "hi"; } - test "implicitly cast from [0]T to error![]T" { testCastZeroArrayToErrSliceMut(); comptime testCastZeroArrayToErrSliceMut(); @@ -329,7 +339,6 @@ fn foo(args: ...) void { assert(@typeOf(args[0]) == &const [5]u8); } - test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); @@ -378,7 +387,12 @@ fn cast128Float(x: u128) f128 { } test "const slice widen cast" { - const bytes align(4) = []u8{0x12, 0x12, 0x12, 0x12}; + const bytes align(4) = []u8 { + 0x12, + 0x12, + 0x12, + 0x12, + }; const u32_value = ([]const u32)(bytes[0..])[0]; assert(u32_value == 0x12121212); diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 46055d7469..d00617eb7c 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -36,7 +36,7 @@ async fn testAsyncSeq() void { suspend; seq('d'); } -var points = []u8{0} ** "abcdefg".len; +var points = []u8 {0} ** "abcdefg".len; var index: usize = 0; fn seq(c: u8) void { @@ -94,7 +94,7 @@ async fn await_another() i32 { return 1234; } -var await_points = []u8{0} ** "abcdefghi".len; +var await_points = []u8 {0} ** "abcdefghi".len; var await_seq_index: usize = 0; fn await_seq(c: u8) void { @@ -102,7 +102,6 @@ fn await_seq(c: u8) void { await_seq_index += 1; } - var early_final_result: i32 = 0; test "coroutine await early return" { @@ -126,7 +125,7 @@ async fn early_another() i32 { return 1234; } -var early_points = []u8{0} ** "abcdef".len; +var early_points = []u8 {0} ** "abcdef".len; var early_seq_index: usize = 0; fn early_seq(c: u8) void { @@ -175,8 +174,8 @@ test "async fn pointer in a struct field" { } async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { - defer *y += 2; - *y += 1; + defer y.* += 2; + y.* += 1; suspend; } @@ -205,7 +204,8 @@ test "error return trace across suspend points - async return" { cancel p2; } -fn nonFailing() promise->error!void { +// TODO https://github.com/zig-lang/zig/issues/760 +fn nonFailing() (promise->error!void) { return async suspendThenFail() catch unreachable; } @@ -238,7 +238,7 @@ async fn testBreakFromSuspend(my_result: &i32) void { s: suspend |p| { break :s; } - *my_result += 1; + my_result.* += 1; suspend; - *my_result += 1; + my_result.* += 1; } diff --git a/test/cases/defer.zig b/test/cases/defer.zig index 5470b4bbd0..d2b00d1f91 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -5,9 +5,18 @@ var index: usize = undefined; fn runSomeErrorDefers(x: bool) !bool { index = 0; - defer {result[index] = 'a'; index += 1;} - errdefer {result[index] = 'b'; index += 1;} - defer {result[index] = 'c'; index += 1;} + defer { + result[index] = 'a'; + index += 1; + } + errdefer { + result[index] = 'b'; + index += 1; + } + defer { + result[index] = 'c'; + index += 1; + } return if (x) x else error.FalseNotAllowed; } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 644c989b04..872e753f20 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -2,8 +2,15 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; test "enum type" { - const foo1 = Foo{ .One = 13}; - const foo2 = Foo{. Two = Point { .x = 1234, .y = 5678, }}; + const foo1 = Foo { + .One = 13, + }; + const foo2 = Foo { + .Two = Point { + .x = 1234, + .y = 5678, + }, + }; const bar = Bar.B; assert(bar == Bar.B); @@ -41,26 +48,31 @@ const Bar = enum { }; fn returnAnInt(x: i32) Foo { - return Foo { .One = x }; + return Foo { + .One = x, + }; } - test "constant enum with payload" { - var empty = AnEnumWithPayload {.Empty = {}}; - var full = AnEnumWithPayload {.Full = 13}; + var empty = AnEnumWithPayload { + .Empty = {}, + }; + var full = AnEnumWithPayload { + .Full = 13, + }; shouldBeEmpty(empty); shouldBeNotEmpty(full); } fn shouldBeEmpty(x: &const AnEnumWithPayload) void { - switch (*x) { + switch (x.*) { AnEnumWithPayload.Empty => {}, else => unreachable, } } fn shouldBeNotEmpty(x: &const AnEnumWithPayload) void { - switch (*x) { + switch (x.*) { AnEnumWithPayload.Empty => unreachable, else => {}, } @@ -71,8 +83,6 @@ const AnEnumWithPayload = union(enum) { Full: i32, }; - - const Number = enum { Zero, One, @@ -93,7 +103,6 @@ fn shouldEqual(n: Number, expected: u3) void { assert(u3(n) == expected); } - test "int to enum" { testIntToEnumEval(3); } @@ -108,7 +117,6 @@ const IntToEnumNumber = enum { Four, }; - test "@tagName" { assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); @@ -124,7 +132,6 @@ const BareNumber = enum { Three, }; - test "enum alignment" { comptime { assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); @@ -137,47 +144,529 @@ const AlignTestEnum = union(enum) { B: u64, }; -const ValueCount1 = enum { I0 }; -const ValueCount2 = enum { I0, I1 }; +const ValueCount1 = enum { + I0, +}; +const ValueCount2 = enum { + I0, + I1, +}; const ValueCount256 = enum { - I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, - I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, - I32, I33, I34, I35, I36, I37, I38, I39, I40, I41, I42, I43, I44, I45, I46, I47, - I48, I49, I50, I51, I52, I53, I54, I55, I56, I57, I58, I59, I60, I61, I62, I63, - I64, I65, I66, I67, I68, I69, I70, I71, I72, I73, I74, I75, I76, I77, I78, I79, - I80, I81, I82, I83, I84, I85, I86, I87, I88, I89, I90, I91, I92, I93, I94, I95, - I96, I97, I98, I99, I100, I101, I102, I103, I104, I105, I106, I107, I108, I109, - I110, I111, I112, I113, I114, I115, I116, I117, I118, I119, I120, I121, I122, I123, - I124, I125, I126, I127, I128, I129, I130, I131, I132, I133, I134, I135, I136, I137, - I138, I139, I140, I141, I142, I143, I144, I145, I146, I147, I148, I149, I150, I151, - I152, I153, I154, I155, I156, I157, I158, I159, I160, I161, I162, I163, I164, I165, - I166, I167, I168, I169, I170, I171, I172, I173, I174, I175, I176, I177, I178, I179, - I180, I181, I182, I183, I184, I185, I186, I187, I188, I189, I190, I191, I192, I193, - I194, I195, I196, I197, I198, I199, I200, I201, I202, I203, I204, I205, I206, I207, - I208, I209, I210, I211, I212, I213, I214, I215, I216, I217, I218, I219, I220, I221, - I222, I223, I224, I225, I226, I227, I228, I229, I230, I231, I232, I233, I234, I235, - I236, I237, I238, I239, I240, I241, I242, I243, I244, I245, I246, I247, I248, I249, - I250, I251, I252, I253, I254, I255 + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, }; const ValueCount257 = enum { - I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, - I16, I17, I18, I19, I20, I21, I22, I23, I24, I25, I26, I27, I28, I29, I30, I31, - I32, I33, I34, I35, I36, I37, I38, I39, I40, I41, I42, I43, I44, I45, I46, I47, - I48, I49, I50, I51, I52, I53, I54, I55, I56, I57, I58, I59, I60, I61, I62, I63, - I64, I65, I66, I67, I68, I69, I70, I71, I72, I73, I74, I75, I76, I77, I78, I79, - I80, I81, I82, I83, I84, I85, I86, I87, I88, I89, I90, I91, I92, I93, I94, I95, - I96, I97, I98, I99, I100, I101, I102, I103, I104, I105, I106, I107, I108, I109, - I110, I111, I112, I113, I114, I115, I116, I117, I118, I119, I120, I121, I122, I123, - I124, I125, I126, I127, I128, I129, I130, I131, I132, I133, I134, I135, I136, I137, - I138, I139, I140, I141, I142, I143, I144, I145, I146, I147, I148, I149, I150, I151, - I152, I153, I154, I155, I156, I157, I158, I159, I160, I161, I162, I163, I164, I165, - I166, I167, I168, I169, I170, I171, I172, I173, I174, I175, I176, I177, I178, I179, - I180, I181, I182, I183, I184, I185, I186, I187, I188, I189, I190, I191, I192, I193, - I194, I195, I196, I197, I198, I199, I200, I201, I202, I203, I204, I205, I206, I207, - I208, I209, I210, I211, I212, I213, I214, I215, I216, I217, I218, I219, I220, I221, - I222, I223, I224, I225, I226, I227, I228, I229, I230, I231, I232, I233, I234, I235, - I236, I237, I238, I239, I240, I241, I242, I243, I244, I245, I246, I247, I248, I249, - I250, I251, I252, I253, I254, I255, I256 + I0, + I1, + I2, + I3, + I4, + I5, + I6, + I7, + I8, + I9, + I10, + I11, + I12, + I13, + I14, + I15, + I16, + I17, + I18, + I19, + I20, + I21, + I22, + I23, + I24, + I25, + I26, + I27, + I28, + I29, + I30, + I31, + I32, + I33, + I34, + I35, + I36, + I37, + I38, + I39, + I40, + I41, + I42, + I43, + I44, + I45, + I46, + I47, + I48, + I49, + I50, + I51, + I52, + I53, + I54, + I55, + I56, + I57, + I58, + I59, + I60, + I61, + I62, + I63, + I64, + I65, + I66, + I67, + I68, + I69, + I70, + I71, + I72, + I73, + I74, + I75, + I76, + I77, + I78, + I79, + I80, + I81, + I82, + I83, + I84, + I85, + I86, + I87, + I88, + I89, + I90, + I91, + I92, + I93, + I94, + I95, + I96, + I97, + I98, + I99, + I100, + I101, + I102, + I103, + I104, + I105, + I106, + I107, + I108, + I109, + I110, + I111, + I112, + I113, + I114, + I115, + I116, + I117, + I118, + I119, + I120, + I121, + I122, + I123, + I124, + I125, + I126, + I127, + I128, + I129, + I130, + I131, + I132, + I133, + I134, + I135, + I136, + I137, + I138, + I139, + I140, + I141, + I142, + I143, + I144, + I145, + I146, + I147, + I148, + I149, + I150, + I151, + I152, + I153, + I154, + I155, + I156, + I157, + I158, + I159, + I160, + I161, + I162, + I163, + I164, + I165, + I166, + I167, + I168, + I169, + I170, + I171, + I172, + I173, + I174, + I175, + I176, + I177, + I178, + I179, + I180, + I181, + I182, + I183, + I184, + I185, + I186, + I187, + I188, + I189, + I190, + I191, + I192, + I193, + I194, + I195, + I196, + I197, + I198, + I199, + I200, + I201, + I202, + I203, + I204, + I205, + I206, + I207, + I208, + I209, + I210, + I211, + I212, + I213, + I214, + I215, + I216, + I217, + I218, + I219, + I220, + I221, + I222, + I223, + I224, + I225, + I226, + I227, + I228, + I229, + I230, + I231, + I232, + I233, + I234, + I235, + I236, + I237, + I238, + I239, + I240, + I241, + I242, + I243, + I244, + I245, + I246, + I247, + I248, + I249, + I250, + I251, + I252, + I253, + I254, + I255, + I256, }; test "enum sizes" { @@ -189,11 +678,11 @@ test "enum sizes" { } } -const Small2 = enum (u2) { +const Small2 = enum(u2) { One, Two, }; -const Small = enum (u2) { +const Small = enum(u2) { One, Two, Three, @@ -213,8 +702,7 @@ test "set enum tag type" { } } - -const A = enum (u3) { +const A = enum(u3) { One, Two, Three, @@ -225,7 +713,7 @@ const A = enum (u3) { Four2, }; -const B = enum (u3) { +const B = enum(u3) { One3, Two3, Three3, @@ -236,7 +724,7 @@ const B = enum (u3) { Four23, }; -const C = enum (u2) { +const C = enum(u2) { One4, Two4, Three4, @@ -389,6 +877,8 @@ test "enum with tag values don't require parens" { } test "enum with 1 field but explicit tag type should still have the tag type" { - const Enum = enum(u8) { B = 2 }; + const Enum = enum(u8) { + B = 2, + }; comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 0c2ae1c383..9e3e031f92 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -7,7 +7,7 @@ const ET = union(enum) { UINT: u32, pub fn print(a: &const ET, buf: []u8) error!usize { - return switch (*a) { + return switch (a.*) { ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), }; @@ -15,8 +15,12 @@ const ET = union(enum) { }; test "enum with members" { - const a = ET { .SINT = -42 }; - const b = ET { .UINT = 42 }; + const a = ET { + .SINT = -42, + }; + const b = ET { + .UINT = 42, + }; var buf: [20]u8 = undefined; assert((a.print(buf[0..]) catch unreachable) == 3); diff --git a/test/cases/error.zig b/test/cases/error.zig index 2a1433df5b..70d96e4d01 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -30,14 +30,12 @@ test "@errorName" { assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } - test "error values" { const a = i32(error.err1); const b = i32(error.err2); assert(a != b); } - test "redefinition of error values allowed" { shouldBeNotEqual(error.AnError, error.SecondError); } @@ -45,7 +43,6 @@ fn shouldBeNotEqual(a: error, b: error) void { if (a == b) unreachable; } - test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; @@ -56,20 +53,20 @@ fn errBinaryOperatorG(x: bool) error!isize { return if (x) error.ItBroke else isize(10); } - test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; assert(i == 13); } -fn unwrapSimpleValueFromErrorDo() error!isize { return 13; } - +fn unwrapSimpleValueFromErrorDo() error!isize { + return 13; +} test "error return in assignment" { doErrReturnInAssignment() catch unreachable; } fn doErrReturnInAssignment() error!void { - var x : i32 = undefined; + var x: i32 = undefined; x = try makeANonErr(); } @@ -95,7 +92,10 @@ test "error set type " { comptime testErrorSetType(); } -const MyErrSet = error {OutOfMemory, FileNotFound}; +const MyErrSet = error { + OutOfMemory, + FileNotFound, +}; fn testErrorSetType() void { assert(@memberCount(MyErrSet) == 2); @@ -109,14 +109,19 @@ fn testErrorSetType() void { } } - test "explicit error set cast" { testExplicitErrorSetCast(Set1.A); comptime testExplicitErrorSetCast(Set1.A); } -const Set1 = error{A, B}; -const Set2 = error{A, C}; +const Set1 = error { + A, + B, +}; +const Set2 = error { + A, + C, +}; fn testExplicitErrorSetCast(set1: Set1) void { var x = Set2(set1); @@ -129,7 +134,8 @@ test "comptime test error for empty error set" { comptime testComptimeTestErrorEmptySet(1234); } -const EmptyErrorSet = error {}; +const EmptyErrorSet = error { +}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { if (x) |v| assert(v == 1234) else |err| @compileError("bad"); @@ -145,7 +151,9 @@ test "comptime err to int of error set with only 1 possible value" { testErrToIntWithOnePossibleValue(error.A, u32(error.A)); comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A)); } -fn testErrToIntWithOnePossibleValue(x: error{A}, comptime value: u32) void { +fn testErrToIntWithOnePossibleValue(x: error { + A, +}, comptime value: u32) void { if (u32(x) != value) { @compileError("bad"); } @@ -176,7 +184,6 @@ fn quux_1() !i32 { return error.C; } - test "error: fn returning empty error set can be passed as fn returning any error" { entry(); comptime entry(); @@ -186,24 +193,24 @@ fn entry() void { foo2(bar2); } -fn foo2(f: fn()error!void) void { +fn foo2(f: fn() error!void) void { const x = f(); } -fn bar2() (error{}!void) { } - +fn bar2() (error { +}!void) {} test "error: Zero sized error set returned with value payload crash" { _ = foo3(0); _ = comptime foo3(0); } -const Error = error{}; +const Error = error { +}; fn foo3(b: usize) Error!usize { return b; } - test "error: Infer error set from literals" { _ = nullLiteral("n") catch |err| handleErrors(err); _ = floatLiteral("n") catch |err| handleErrors(err); @@ -215,29 +222,26 @@ test "error: Infer error set from literals" { fn handleErrors(err: var) noreturn { switch (err) { - error.T => {} + error.T => {}, } unreachable; } fn nullLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') - return null; + if (str[0] == 'n') return null; return error.T; } fn floatLiteral(str: []const u8) !?f64 { - if (str[0] == 'n') - return 1.0; + if (str[0] == 'n') return 1.0; return error.T; } fn intLiteral(str: []const u8) !?i64 { - if (str[0] == 'n') - return 1; + if (str[0] == 'n') return 1; return error.T; } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 364db5e152..2571686b0b 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -11,8 +11,6 @@ fn fibonacci(x: i32) i32 { return fibonacci(x - 1) + fibonacci(x - 2); } - - fn unwrapAndAddOne(blah: ?i32) i32 { return ??blah + 1; } @@ -40,13 +38,13 @@ test "inline variable gets result of const if" { assert(gimme1or2(false) == 2); } - test "static function evaluation" { assert(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); -fn staticAdd(a: i32, b: i32) i32 { return a + b; } - +fn staticAdd(a: i32, b: i32) i32 { + return a + b; +} test "const expr eval on single expr blocks" { assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); @@ -64,9 +62,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { return result; } - - - test "statically initialized list" { assert(static_point_list[0].x == 1); assert(static_point_list[0].y == 2); @@ -77,7 +72,10 @@ const Point = struct { x: i32, y: i32, }; -const static_point_list = []Point { makePoint(1, 2), makePoint(3, 4) }; +const static_point_list = []Point { + makePoint(1, 2), + makePoint(3, 4), +}; fn makePoint(x: i32, y: i32) Point { return Point { .x = x, @@ -85,7 +83,6 @@ fn makePoint(x: i32, y: i32) Point { }; } - test "static eval list init" { assert(static_vec3.data[2] == 1.0); assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); @@ -96,17 +93,19 @@ pub const Vec3 = struct { }; pub fn vec3(x: f32, y: f32, z: f32) Vec3 { return Vec3 { - .data = []f32 { x, y, z, }, + .data = []f32 { + x, + y, + z, + }, }; } - test "constant expressions" { - var array : [array_size]u8 = undefined; + var array: [array_size]u8 = undefined; assert(@sizeOf(@typeOf(array)) == 20); } -const array_size : u8 = 20; - +const array_size: u8 = 20; test "constant struct with negation" { assert(vertices[0].x == -0.6); @@ -119,12 +118,29 @@ const Vertex = struct { b: f32, }; const vertices = []Vertex { - Vertex { .x = -0.6, .y = -0.4, .r = 1.0, .g = 0.0, .b = 0.0 }, - Vertex { .x = 0.6, .y = -0.4, .r = 0.0, .g = 1.0, .b = 0.0 }, - Vertex { .x = 0.0, .y = 0.6, .r = 0.0, .g = 0.0, .b = 1.0 }, + Vertex { + .x = -0.6, + .y = -0.4, + .r = 1.0, + .g = 0.0, + .b = 0.0, + }, + Vertex { + .x = 0.6, + .y = -0.4, + .r = 0.0, + .g = 1.0, + .b = 0.0, + }, + Vertex { + .x = 0.0, + .y = 0.6, + .r = 0.0, + .g = 0.0, + .b = 1.0, + }, }; - test "statically initialized struct" { st_init_str_foo.x += 1; assert(st_init_str_foo.x == 14); @@ -133,15 +149,21 @@ const StInitStrFoo = struct { x: i32, y: bool, }; -var st_init_str_foo = StInitStrFoo { .x = 13, .y = true, }; - +var st_init_str_foo = StInitStrFoo { + .x = 13, + .y = true, +}; test "statically initalized array literal" { - const y : [4]u8 = st_init_arr_lit_x; + const y: [4]u8 = st_init_arr_lit_x; assert(y[3] == 4); } -const st_init_arr_lit_x = []u8{1,2,3,4}; - +const st_init_arr_lit_x = []u8 { + 1, + 2, + 3, + 4, +}; test "const slice" { comptime { @@ -198,14 +220,29 @@ const CmdFn = struct { func: fn(i32) i32, }; -const cmd_fns = []CmdFn{ - CmdFn {.name = "one", .func = one}, - CmdFn {.name = "two", .func = two}, - CmdFn {.name = "three", .func = three}, +const cmd_fns = []CmdFn { + CmdFn { + .name = "one", + .func = one, + }, + CmdFn { + .name = "two", + .func = two, + }, + CmdFn { + .name = "three", + .func = three, + }, }; -fn one(value: i32) i32 { return value + 1; } -fn two(value: i32) i32 { return value + 2; } -fn three(value: i32) i32 { return value + 3; } +fn one(value: i32) i32 { + return value + 1; +} +fn two(value: i32) i32 { + return value + 2; +} +fn three(value: i32) i32 { + return value + 3; +} fn performFn(comptime prefix_char: u8, start_value: i32) i32 { var result: i32 = start_value; @@ -229,7 +266,7 @@ test "eval @setRuntimeSafety at compile-time" { assert(result == 1234); } -fn fnWithSetRuntimeSafety() i32{ +fn fnWithSetRuntimeSafety() i32 { @setRuntimeSafety(true); return 1234; } @@ -244,7 +281,6 @@ fn fnWithFloatMode() f32 { return 1234.0; } - const SimpleStruct = struct { field: i32, @@ -253,7 +289,9 @@ const SimpleStruct = struct { } }; -var simple_struct = SimpleStruct{ .field = 1234, }; +var simple_struct = SimpleStruct { + .field = 1234, +}; const bound_fn = simple_struct.method; @@ -261,8 +299,6 @@ test "call method on bound fn referring to var instance" { assert(bound_fn() == 1237); } - - test "ptr to local array argument at comptime" { comptime { var bytes: [10]u8 = undefined; @@ -277,7 +313,6 @@ fn modifySomeBytes(bytes: []u8) void { bytes[9] = 'b'; } - test "comparisons 0 <= uint and 0 > uint should be comptime" { testCompTimeUIntComparisons(1234); } @@ -296,8 +331,6 @@ fn testCompTimeUIntComparisons(x: u32) void { } } - - test "const ptr to variable data changes at runtime" { assert(foo_ref.name[0] == 'a'); foo_ref.name = "b"; @@ -308,11 +341,11 @@ const Foo = struct { name: []const u8, }; -var foo_contents = Foo { .name = "a", }; +var foo_contents = Foo { + .name = "a", +}; const foo_ref = &foo_contents; - - test "create global array with for loop" { assert(global_array[5] == 5 * 5); assert(global_array[9] == 9 * 9); @@ -321,7 +354,7 @@ test "create global array with for loop" { const global_array = x: { var result: [10]usize = undefined; for (result) |*item, index| { - *item = index * index; + item.* = index * index; } break :x result; }; @@ -379,7 +412,7 @@ test "f128 at compile time is lossy" { pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { - pub const Node = struct { }; + pub const Node = struct {}; }; } @@ -401,10 +434,10 @@ fn copyWithPartialInline(s: []u32, b: []u8) void { comptime var i: usize = 0; inline while (i < 4) : (i += 1) { s[i] = 0; - s[i] |= u32(b[i*4+0]) << 24; - s[i] |= u32(b[i*4+1]) << 16; - s[i] |= u32(b[i*4+2]) << 8; - s[i] |= u32(b[i*4+3]) << 0; + s[i] |= u32(b[i * 4 + 0]) << 24; + s[i] |= u32(b[i * 4 + 1]) << 16; + s[i] |= u32(b[i * 4 + 2]) << 8; + s[i] |= u32(b[i * 4 + 3]) << 0; } } @@ -413,7 +446,7 @@ test "binary math operator in partially inlined function" { var b: [16]u8 = undefined; for (b) |*r, i| - *r = u8(i + 1); + r.* = u8(i + 1); copyWithPartialInline(s[0..], b[0..]); assert(s[0] == 0x1020304); @@ -422,7 +455,6 @@ test "binary math operator in partially inlined function" { assert(s[3] == 0xd0e0f10); } - test "comptime function with the same args is memoized" { comptime { assert(MakeType(i32) == MakeType(i32)); @@ -447,12 +479,12 @@ test "comptime function with mutable pointer is not memoized" { } fn increment(value: &i32) void { - *value += 1; + value.* += 1; } fn generateTable(comptime T: type) [1010]T { - var res : [1010]T = undefined; - var i : usize = 0; + var res: [1010]T = undefined; + var i: usize = 0; while (i < 1010) : (i += 1) { res[i] = T(i); } @@ -496,9 +528,10 @@ const SingleFieldStruct = struct { } }; test "const ptr to comptime mutable data is not memoized" { - comptime { - var foo = SingleFieldStruct {.x = 1}; + var foo = SingleFieldStruct { + .x = 1, + }; assert(foo.read_x() == 1); foo.x = 2; assert(foo.read_x() == 2); diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 5388deac10..6d47dafad4 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -7,7 +7,6 @@ fn testParamsAdd(a: i32, b: i32) i32 { return a + b; } - test "local variables" { testLocVars(2); } @@ -16,7 +15,6 @@ fn testLocVars(b: i32) void { if (a + b != 3) unreachable; } - test "void parameters" { voidFun(1, void{}, 2, {}); } @@ -27,9 +25,8 @@ fn voidFun(a: i32, b: void, c: i32, d: void) void { return vv; } - test "mutable local variables" { - var zero : i32 = 0; + var zero: i32 = 0; assert(zero == 0); var i = i32(0); @@ -41,7 +38,7 @@ test "mutable local variables" { test "separate block scopes" { { - const no_conflict : i32 = 5; + const no_conflict: i32 = 5; assert(no_conflict == 5); } @@ -56,8 +53,7 @@ test "call function with empty string" { acceptsString(""); } -fn acceptsString(foo: []u8) void { } - +fn acceptsString(foo: []u8) void {} fn @"weird function name"() i32 { return 1234; @@ -70,31 +66,43 @@ test "implicit cast function unreachable return" { wantsFnWithVoid(fnWithUnreachable); } -fn wantsFnWithVoid(f: fn() void) void { } +fn wantsFnWithVoid(f: fn() void) void {} fn fnWithUnreachable() noreturn { unreachable; } - test "function pointers" { - const fns = []@typeOf(fn1) { fn1, fn2, fn3, fn4, }; + const fns = []@typeOf(fn1) { + fn1, + fn2, + fn3, + fn4, + }; for (fns) |f, i| { assert(f() == u32(i) + 5); } } -fn fn1() u32 {return 5;} -fn fn2() u32 {return 6;} -fn fn3() u32 {return 7;} -fn fn4() u32 {return 8;} - +fn fn1() u32 { + return 5; +} +fn fn2() u32 { + return 6; +} +fn fn3() u32 { + return 7; +} +fn fn4() u32 { + return 8; +} test "inline function call" { assert(@inlineCall(add, 3, 9) == 12); } -fn add(a: i32, b: i32) i32 { return a + b; } - +fn add(a: i32, b: i32) i32 { + return a + b; +} test "number literal as an argument" { numberLiteralArg(3); @@ -110,4 +118,4 @@ test "assign inline fn to const variable" { a(); } -inline fn inlineFn() void { } +inline fn inlineFn() void {} diff --git a/test/cases/for.zig b/test/cases/for.zig index 7bb0d7c9fa..f13e6ec6e5 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -3,8 +3,14 @@ const assert = std.debug.assert; const mem = std.mem; test "continue in for loop" { - const array = []i32 {1, 2, 3, 4, 5}; - var sum : i32 = 0; + const array = []i32 { + 1, + 2, + 3, + 4, + 5, + }; + var sum: i32 = 0; for (array) |x| { sum += x; if (x < 3) { @@ -24,17 +30,39 @@ test "for loop with pointer elem var" { } fn mangleString(s: []u8) void { for (s) |*c| { - *c += 1; + c.* += 1; } } test "basic for loop" { - const expected_result = []u8{9, 8, 7, 6, 0, 1, 2, 3, 9, 8, 7, 6, 0, 1, 2, 3 }; + 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}; + const array = []u8 { + 9, + 8, + 7, + 6, + }; for (array) |item| { buffer[buf_index] = item; buf_index += 1; @@ -65,7 +93,8 @@ fn testBreakOuter() void { var array = "aoeu"; var count: usize = 0; outer: for (array) |_| { - for (array) |_2| { // TODO shouldn't get error for redeclaring "_" + // TODO shouldn't get error for redeclaring "_" + for (array) |_2| { count += 1; break :outer; } @@ -82,7 +111,8 @@ fn testContinueOuter() void { var array = "aoeu"; var counter: usize = 0; outer: for (array) |_| { - for (array) |_2| { // TODO shouldn't get error for redeclaring "_" + // TODO shouldn't get error for redeclaring "_" + for (array) |_2| { counter += 1; continue :outer; } diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 19b4a598d8..da8a7dcad6 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -37,7 +37,6 @@ test "fn with comptime args" { assert(sameButWithFloats(0.43, 0.49) == 0.49); } - test "var params" { assert(max_i32(12, 34) == 34); assert(max_f64(1.2, 3.4) == 3.4); @@ -60,7 +59,6 @@ fn max_f64(a: f64, b: f64) f64 { return max_var(a, b); } - pub fn List(comptime T: type) type { return SmallList(T, 8); } @@ -82,10 +80,15 @@ test "function with return type type" { assert(list2.prealloc_items.len == 8); } - test "generic struct" { - var a1 = GenNode(i32) {.value = 13, .next = null,}; - var b1 = GenNode(bool) {.value = true, .next = null,}; + var a1 = GenNode(i32) { + .value = 13, + .next = null, + }; + var b1 = GenNode(bool) { + .value = true, + .next = null, + }; assert(a1.value == 13); assert(a1.value == a1.getVal()); assert(b1.getVal()); @@ -94,7 +97,9 @@ fn GenNode(comptime T: type) type { return struct { value: T, next: ?&GenNode(T), - fn getVal(n: &const GenNode(T)) T { return n.value; } + fn getVal(n: &const GenNode(T)) T { + return n.value; + } }; } @@ -107,7 +112,6 @@ fn GenericDataThing(comptime count: isize) type { }; } - test "use generic param in generic param" { assert(aGenericFn(i32, 3, 4) == 7); } @@ -115,21 +119,31 @@ fn aGenericFn(comptime T: type, comptime a: T, b: T) T { return a + b; } - test "generic fn with implicit cast" { assert(getFirstByte(u8, []u8 {13}) == 13); - assert(getFirstByte(u16, []u16 {0, 13}) == 0); + assert(getFirstByte(u16, []u16 { + 0, + 13, + }) == 0); +} +fn getByte(ptr: ?&const u8) u8 { + return ??ptr.*; } -fn getByte(ptr: ?&const u8) u8 {return *??ptr;} fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(&const u8, &mem[0])); } +const foos = []fn(var) bool { + foo1, + foo2, +}; -const foos = []fn(var) bool { foo1, foo2 }; - -fn foo1(arg: var) bool { return arg; } -fn foo2(arg: var) bool { return !arg; } +fn foo1(arg: var) bool { + return arg; +} +fn foo2(arg: var) bool { + return !arg; +} test "array of generic fns" { assert(foos[0](true)); diff --git a/test/cases/if.zig b/test/cases/if.zig index 2caae7448c..808936bfa5 100644 --- a/test/cases/if.zig +++ b/test/cases/if.zig @@ -23,7 +23,6 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } } - test "else if expression" { assert(elseIfExpressionF(1) == 1); } diff --git a/test/cases/import/a_namespace.zig b/test/cases/import/a_namespace.zig index 5cf906cf91..042f1867a5 100644 --- a/test/cases/import/a_namespace.zig +++ b/test/cases/import/a_namespace.zig @@ -1 +1,3 @@ -pub fn foo() i32 { return 1234; } +pub fn foo() i32 { + return 1234; +} diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig index 202df19f62..c017eca508 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/cases/ir_block_deps.zig @@ -11,7 +11,9 @@ fn foo(id: u64) !i32 { }; } -fn getErrInt() error!i32 { return 0; } +fn getErrInt() error!i32 { + return 0; +} test "ir block deps" { assert((foo(1) catch unreachable) == 0); diff --git a/test/cases/math.zig b/test/cases/math.zig index 47d001a590..dfc5946fdb 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -28,25 +28,12 @@ fn testDivision() void { assert(divTrunc(f32, -5.0, 3.0) == -1.0); comptime { - assert( - 1194735857077236777412821811143690633098347576 % - 508740759824825164163191790951174292733114988 == - 177254337427586449086438229241342047632117600); - assert(@rem(-1194735857077236777412821811143690633098347576, - 508740759824825164163191790951174292733114988) == - -177254337427586449086438229241342047632117600); - assert(1194735857077236777412821811143690633098347576 / - 508740759824825164163191790951174292733114988 == - 2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, - 508740759824825164163191790951174292733114988) == - -2); - assert(@divTrunc(1194735857077236777412821811143690633098347576, - -508740759824825164163191790951174292733114988) == - -2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, - -508740759824825164163191790951174292733114988) == - 2); + assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600); + assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600); + assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2); + assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2); assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1); } } @@ -114,18 +101,28 @@ fn ctz(x: var) usize { test "assignment operators" { var i: u32 = 0; - i += 5; assert(i == 5); - i -= 2; assert(i == 3); - i *= 20; assert(i == 60); - i /= 3; assert(i == 20); - i %= 11; assert(i == 9); - i <<= 1; assert(i == 18); - i >>= 2; assert(i == 4); + i += 5; + assert(i == 5); + i -= 2; + assert(i == 3); + i *= 20; + assert(i == 60); + i /= 3; + assert(i == 20); + i %= 11; + assert(i == 9); + i <<= 1; + assert(i == 18); + i >>= 2; + assert(i == 4); i = 6; - i &= 5; assert(i == 4); - i ^= 6; assert(i == 2); + i &= 5; + assert(i == 4); + i ^= 6; + assert(i == 2); i = 6; - i |= 3; assert(i == 7); + i |= 3; + assert(i == 7); } test "three expr in a row" { @@ -138,7 +135,7 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(1 | 2 | 4 != 7); assertFalse(3 ^ 6 ^ 8 != 13); assertFalse(7 & 14 & 28 != 4); - assertFalse(9 << 1 << 2 != 9 << 3); + assertFalse(9 << 1 << 2 != 9 << 3); assertFalse(90 >> 1 >> 2 != 90 >> 3); assertFalse(100 - 1 + 1000 != 1099); assertFalse(5 * 4 / 2 % 3 != 1); @@ -150,7 +147,6 @@ fn assertFalse(b: bool) void { assert(!b); } - test "const number literal" { const one = 1; const eleven = ten + one; @@ -159,8 +155,6 @@ test "const number literal" { } const ten = 10; - - test "unsigned wrapping" { testUnsignedWrappingEval(@maxValue(u32)); comptime testUnsignedWrappingEval(@maxValue(u32)); @@ -214,8 +208,12 @@ const DivResult = struct { }; test "binary not" { - assert(comptime x: {break :x ~u16(0b1010101010101010) == 0b0101010101010101;}); - assert(comptime x: {break :x ~u64(2147483647) == 18446744071562067968;}); + assert(comptime x: { + break :x ~u16(0b1010101010101010) == 0b0101010101010101; + }); + assert(comptime x: { + break :x ~u64(2147483647) == 18446744071562067968; + }); testBinaryNot(0b1010101010101010); } @@ -319,27 +317,15 @@ fn testShrExact(x: u8) void { test "big number addition" { comptime { - assert( - 35361831660712422535336160538497375248 + - 101752735581729509668353361206450473702 == - 137114567242441932203689521744947848950); - assert( - 594491908217841670578297176641415611445982232488944558774612 + - 390603545391089362063884922208143568023166603618446395589768 == - 985095453608931032642182098849559179469148836107390954364380); + assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } test "big number multiplication" { comptime { - assert( - 45960427431263824329884196484953148229 * - 128339149605334697009938835852565949723 == - 5898522172026096622534201617172456926982464453350084962781392314016180490567); - assert( - 594491908217841670578297176641415611445982232488944558774612 * - 390603545391089362063884922208143568023166603618446395589768 == - 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016); + assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567); + assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016); } } @@ -380,7 +366,9 @@ test "f128" { comptime test_f128(); } -fn make_f128(x: f128) f128 { return x; } +fn make_f128(x: f128) f128 { + return x; +} fn test_f128() void { assert(@sizeOf(f128) == 16); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 95a9a46bff..66487a4946 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -4,6 +4,7 @@ const cstr = @import("std").cstr; const builtin = @import("builtin"); // normal comment + /// this is a documentation comment /// doc comment line 2 fn emptyFunctionWithComments() void {} @@ -16,8 +17,7 @@ comptime { @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal); } -extern fn disabledExternFn() void { -} +extern fn disabledExternFn() void {} test "call disabled extern fn" { disabledExternFn(); @@ -110,17 +110,29 @@ fn testShortCircuit(f: bool, t: bool) void { var hit_3 = f; var hit_4 = f; - if (t or x: {assert(f); break :x f;}) { + if (t or x: { + assert(f); + break :x f; + }) { hit_1 = t; } - if (f or x: { hit_2 = t; break :x f; }) { + if (f or x: { + hit_2 = t; + break :x f; + }) { assert(f); } - if (t and x: { hit_3 = t; break :x f; }) { + if (t and x: { + hit_3 = t; + break :x f; + }) { assert(f); } - if (f and x: {assert(f); break :x f;}) { + if (f and x: { + assert(f); + break :x f; + }) { assert(f); } else { hit_4 = t; @@ -146,8 +158,8 @@ test "return string from function" { assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } -const g1 : i32 = 1233 + 1; -var g2 : i32 = 0; +const g1: i32 = 1233 + 1; +var g2: i32 = 0; test "global variables" { assert(g2 == 0); @@ -155,10 +167,9 @@ test "global variables" { assert(g2 == 1234); } - test "memcpy and memset intrinsics" { - var foo : [20]u8 = undefined; - var bar : [20]u8 = undefined; + var foo: [20]u8 = undefined; + var bar: [20]u8 = undefined; @memset(&foo[0], 'A', foo.len); @memcpy(&bar[0], &foo[0], bar.len); @@ -167,12 +178,14 @@ test "memcpy and memset intrinsics" { } test "builtin static eval" { - const x : i32 = comptime x: {break :x 1 + 2 + 3;}; + const x: i32 = comptime x: { + break :x 1 + 2 + 3; + }; assert(x == comptime 6); } test "slicing" { - var array : [20]i32 = undefined; + var array: [20]i32 = undefined; array[5] = 1234; @@ -187,15 +200,15 @@ test "slicing" { if (slice_rest.len != 10) unreachable; } - test "constant equal function pointers" { const alias = emptyFn; - assert(comptime x: {break :x emptyFn == alias;}); + assert(comptime x: { + break :x emptyFn == alias; + }); } fn emptyFn() void {} - test "hex escape" { assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } @@ -219,7 +232,7 @@ test "string escapes" { } test "multiline string" { - const s1 = + const s1 = \\one \\two) \\three @@ -229,7 +242,7 @@ test "multiline string" { } test "multiline C string" { - const s1 = + const s1 = c\\one c\\two) c\\three @@ -238,18 +251,16 @@ test "multiline C string" { assert(cstr.cmp(s1, s2) == 0); } - test "type equality" { assert(&const u8 != &u8); } - const global_a: i32 = 1234; const global_b: &const i32 = &global_a; const global_c: &const f32 = @ptrCast(&const f32, global_b); test "compile time global reinterpret" { const d = @ptrCast(&const i32, global_c); - assert(*d == 1234); + assert(d.* == 1234); } test "explicit cast maybe pointers" { @@ -261,12 +272,11 @@ test "generic malloc free" { const a = memAlloc(u8, 10) catch unreachable; memFree(u8, a); } -var some_mem : [100]u8 = undefined; +var some_mem: [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { return @ptrCast(&T, &some_mem[0])[0..n]; } -fn memFree(comptime T: type, memory: []T) void { } - +fn memFree(comptime T: type, memory: []T) void {} test "cast undefined" { const array: [100]u8 = undefined; @@ -275,32 +285,35 @@ test "cast undefined" { } fn testCastUndefined(x: []const u8) void {} - test "cast small unsigned to larger signed" { assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } -fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; } -fn castSmallUnsignedToLargerSigned2(x: u16) i64 { return x; } - +fn castSmallUnsignedToLargerSigned1(x: u8) i16 { + return x; +} +fn castSmallUnsignedToLargerSigned2(x: u16) i64 { + return x; +} test "implicit cast after unreachable" { assert(outer() == 1234); } -fn inner() i32 { return 1234; } +fn inner() i32 { + return 1234; +} fn outer() i64 { return inner(); } - test "pointer dereferencing" { var x = i32(3); const y = &x; - *y += 1; + y.* += 1; assert(x == 4); - assert(*y == 4); + assert(y.* == 4); } test "call result of if else expression" { @@ -310,9 +323,12 @@ test "call result of if else expression" { fn f2(x: bool) []const u8 { return (if (x) fA else fB)(); } -fn fA() []const u8 { return "a"; } -fn fB() []const u8 { return "b"; } - +fn fA() []const u8 { + return "a"; +} +fn fB() []const u8 { + return "b"; +} test "const expression eval handling of variables" { var x = true; @@ -321,8 +337,6 @@ test "const expression eval handling of variables" { } } - - test "constant enum initialization with differing sizes" { test3_1(test3_foo); test3_2(test3_bar); @@ -336,10 +350,17 @@ const Test3Point = struct { x: i32, y: i32, }; -const test3_foo = Test3Foo { .Three = Test3Point {.x = 3, .y = 4}}; -const test3_bar = Test3Foo { .Two = 13}; +const test3_foo = Test3Foo { + .Three = Test3Point { + .x = 3, + .y = 4, + }, +}; +const test3_bar = Test3Foo { + .Two = 13, +}; fn test3_1(f: &const Test3Foo) void { - switch (*f) { + switch (f.*) { Test3Foo.Three => |pt| { assert(pt.x == 3); assert(pt.y == 4); @@ -348,7 +369,7 @@ fn test3_1(f: &const Test3Foo) void { } } fn test3_2(f: &const Test3Foo) void { - switch (*f) { + switch (f.*) { Test3Foo.Two => |x| { assert(x == 13); }, @@ -356,23 +377,19 @@ fn test3_2(f: &const Test3Foo) void { } } - test "character literals" { assert('\'' == single_quote); } const single_quote = '\''; - - test "take address of parameter" { testTakeAddressOfParameter(12.34); } fn testTakeAddressOfParameter(f: f32) void { const f_ptr = &f; - assert(*f_ptr == 12.34); + assert(f_ptr.* == 12.34); } - test "pointer comparison" { const a = ([]const u8)("a"); const b = &a; @@ -382,23 +399,30 @@ fn ptrEql(a: &const []const u8, b: &const []const u8) bool { return a == b; } - test "C string concatenation" { const a = c"OK" ++ c" IT " ++ c"WORKED"; const b = c"OK IT WORKED"; const len = cstr.len(b); const len_with_null = len + 1; - {var i: u32 = 0; while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); - }} + { + var i: u32 = 0; + while (i < len_with_null) : (i += 1) { + assert(a[i] == b[i]); + } + } assert(a[len] == 0); assert(b[len] == 0); } test "cast slice to u8 slice" { assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{1, 2, 3, 4}; + var big_thing_array = []i32 { + 1, + 2, + 3, + 4, + }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = ([]u8)(big_thing_slice); assert(bytes.len == 4 * 4); @@ -421,25 +445,22 @@ test "pointer to void return type" { } fn testPointerToVoidReturnType() error!void { const a = testPointerToVoidReturnType2(); - return *a; + return a.*; } const test_pointer_to_void_return_type_x = void{}; fn testPointerToVoidReturnType2() &const void { return &test_pointer_to_void_return_type_x; } - test "non const ptr to aliased type" { const int = i32; assert(?&int == ?&i32); } - - test "array 2D const double ptr" { const rect_2d_vertexes = [][1]f32 { - []f32{1.0}, - []f32{2.0}, + []f32 {1.0}, + []f32 {2.0}, }; testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); } @@ -450,10 +471,21 @@ fn testArray2DConstDoublePtr(ptr: &const f32) void { } const Tid = builtin.TypeId; -const AStruct = struct { x: i32, }; -const AnEnum = enum { One, Two, }; -const AUnionEnum = union(enum) { One: i32, Two: void, }; -const AUnion = union { One: void, Two: void }; +const AStruct = struct { + x: i32, +}; +const AnEnum = enum { + One, + Two, +}; +const AUnionEnum = union(enum) { + One: i32, + Two: void, +}; +const AUnion = union { + One: void, + Two: void, +}; test "@typeId" { comptime { @@ -481,9 +513,11 @@ test "@typeId" { assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); assert(@typeId(AUnionEnum) == Tid.Union); assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn()void) == Tid.Fn); + assert(@typeId(fn() void) == Tid.Fn); assert(@typeId(@typeOf(builtin)) == Tid.Namespace); - assert(@typeId(@typeOf(x: {break :x this;})) == Tid.Block); + assert(@typeId(@typeOf(x: { + break :x this; + })) == Tid.Block); // TODO bound fn // TODO arg tuple // TODO opaque @@ -499,8 +533,7 @@ test "@canImplicitCast" { } test "@typeName" { - const Struct = struct { - }; + const Struct = struct {}; const Union = union { unused: u8, }; @@ -525,14 +558,19 @@ fn TypeFromFn(comptime T: type) type { test "volatile load and store" { var number: i32 = 1234; const ptr = (&volatile i32)(&number); - *ptr += 1; - assert(*ptr == 1235); + ptr.* += 1; + assert(ptr.* == 1235); } test "slice string literal has type []const u8" { comptime { assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{1, 2, 3, 4}; + const array = []i32 { + 1, + 2, + 3, + 4, + }; assert(@typeOf(array[0..]) == []const i32); } } @@ -544,12 +582,15 @@ const GDTEntry = struct { field: i32, }; var gdt = []GDTEntry { - GDTEntry {.field = 1}, - GDTEntry {.field = 2}, + GDTEntry { + .field = 1, + }, + GDTEntry { + .field = 2, + }, }; var global_ptr = &gdt[0]; - // can't really run this test but we can make sure it has no compile error // and generates code const vram = @intToPtr(&volatile u8, 0x20000000)[0..0x8000]; @@ -584,7 +625,7 @@ test "comptime if inside runtime while which unconditionally breaks" { } fn testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(cond: bool) void { while (cond) { - if (false) { } + if (false) {} break; } } @@ -607,7 +648,9 @@ fn testStructInFn() void { kind: BlockKind, }; - var block = Block { .kind = 1234 }; + var block = Block { + .kind = 1234, + }; block.kind += 1; @@ -617,7 +660,9 @@ fn testStructInFn() void { fn fnThatClosesOverLocalConst() type { const c = 1; return struct { - fn g() i32 { return c; } + fn g() i32 { + return c; + } }; } @@ -635,22 +680,29 @@ fn thisIsAColdFn() void { @setCold(true); } - -const PackedStruct = packed struct { a: u8, b: u8, }; -const PackedUnion = packed union { a: u8, b: u32, }; -const PackedEnum = packed enum { A, B, }; +const PackedStruct = packed struct { + a: u8, + b: u8, +}; +const PackedUnion = packed union { + a: u8, + b: u32, +}; +const PackedEnum = packed enum { + A, + B, +}; test "packed struct, enum, union parameters in extern function" { - testPackedStuff( - PackedStruct{.a = 1, .b = 2}, - PackedUnion{.a = 1}, - PackedEnum.A, - ); -} - -export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void { + testPackedStuff(PackedStruct { + .a = 1, + .b = 2, + }, PackedUnion { + .a = 1, + }, PackedEnum.A); } +export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void {} test "slicing zero length array" { const s1 = ""[0..]; @@ -661,7 +713,6 @@ test "slicing zero length array" { assert(mem.eql(u32, s2, []u32{})); } - const addr1 = @ptrCast(&const u8, emptyFn); test "comptime cast fn to ptr" { const addr2 = @ptrCast(&const u8, emptyFn); diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/cases/namespace_depends_on_compile_var/index.zig index 95209dcef3..ccc49d9367 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/cases/namespace_depends_on_compile_var/index.zig @@ -8,7 +8,7 @@ test "namespace depends on compile var" { assert(!some_namespace.a_bool); } } -const some_namespace = switch(builtin.os) { +const some_namespace = switch (builtin.os) { builtin.Os.linux => @import("a.zig"), else => @import("b.zig"), }; diff --git a/test/cases/null.zig b/test/cases/null.zig index 35d72b729c..96a62ab1ed 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; test "nullable type" { - const x : ?bool = true; + const x: ?bool = true; if (x) |y| { if (y) { @@ -13,13 +13,13 @@ test "nullable type" { unreachable; } - const next_x : ?i32 = null; + const next_x: ?i32 = null; const z = next_x ?? 1234; assert(z == 1234); - const final_x : ?i32 = 13; + const final_x: ?i32 = 13; const num = final_x ?? unreachable; @@ -30,19 +30,17 @@ test "test maybe object and get a pointer to the inner value" { var maybe_bool: ?bool = true; if (maybe_bool) |*b| { - *b = false; + b.* = false; } assert(??maybe_bool == false); } - test "rhs maybe unwrap return" { const x: ?bool = true; const y = x ?? return; } - test "maybe return" { maybeReturnImpl(); comptime maybeReturnImpl(); @@ -50,8 +48,7 @@ test "maybe return" { fn maybeReturnImpl() void { assert(??foo(1235)); - if (foo(null) != null) - unreachable; + if (foo(null) != null) unreachable; assert(!??foo(1234)); } @@ -60,12 +57,16 @@ fn foo(x: ?i32) ?bool { return value > 1234; } - test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle {.a = 14, .b = 1, .c = 1, .d = 1}) == 15); + assert(shouldBeAPlus1(Particle { + .a = 14, + .b = 1, + .c = 1, + .d = 1, + }) == 15); } fn shouldBeAPlus1(p: &const Particle) u64 { - var maybe_particle: ?Particle = *p; + var maybe_particle: ?Particle = p.*; if (maybe_particle) |*particle| { particle.a += 1; } @@ -81,7 +82,6 @@ const Particle = struct { d: u64, }; - test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; assert(is_null); @@ -96,7 +96,6 @@ const here_is_a_null_literal = SillyStruct { .context = null, }; - test "test null runtime" { testTestNullRuntime(null); } @@ -123,8 +122,6 @@ fn bar(x: ?void) ?void { } } - - const StructWithNullable = struct { field: ?i32, }; diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig index 76cff3731a..3c94bb0d49 100644 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -23,7 +23,7 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { if (c) { const output_path = b; - if (c2) { } + if (c2) {} a(output_path); } diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 0abc46c9de..f9b64c80eb 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -23,7 +23,9 @@ test "reflection: function return type, var args, and param types" { } } -fn dummy(a: bool, b: i32, c: f32) i32 { return 1234; } +fn dummy(a: bool, b: i32, c: f32) i32 { + return 1234; +} fn dummy_varargs(args: ...) void {} test "reflection: struct member types and names" { @@ -54,7 +56,6 @@ test "reflection: enum member types and names" { assert(mem.eql(u8, @memberName(Bar, 2), "Three")); assert(mem.eql(u8, @memberName(Bar, 3), "Four")); } - } test "reflection: @field" { diff --git a/test/cases/slice.zig b/test/cases/slice.zig index ea708ba3b5..4ca194672c 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -18,7 +18,11 @@ test "slice child property" { } test "runtime safety lets us slice from len..len" { - var an_array = []u8{1, 2, 3}; + var an_array = []u8 { + 1, + 2, + 3, + }; assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } @@ -27,7 +31,7 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { } test "implicitly cast array of size 0 to slice" { - var msg = []u8 {}; + var msg = []u8{}; assertLenIsZero(msg); } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index c3df97678b..c474d99f2b 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -2,9 +2,11 @@ const assert = @import("std").debug.assert; const builtin = @import("builtin"); const StructWithNoFields = struct { - fn add(a: i32, b: i32) i32 { return a + b; } + fn add(a: i32, b: i32) i32 { + return a + b; + } }; -const empty_global_instance = StructWithNoFields {}; +const empty_global_instance = StructWithNoFields{}; test "call struct static method" { const result = StructWithNoFields.add(3, 4); @@ -34,12 +36,11 @@ test "void struct fields" { assert(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { - a : void, - b : i32, - c : void, + a: void, + b: i32, + c: void, }; - test "structs" { var foo: StructFoo = undefined; @memset(@ptrCast(&u8, &foo), 0, @sizeOf(StructFoo)); @@ -50,9 +51,9 @@ test "structs" { assert(foo.c == 100); } const StructFoo = struct { - a : i32, - b : bool, - c : f32, + a: i32, + b: bool, + c: f32, }; fn testFoo(foo: &const StructFoo) void { assert(foo.b); @@ -61,7 +62,6 @@ fn testMutation(foo: &StructFoo) void { foo.c = 100; } - const Node = struct { val: Val, next: &Node, @@ -72,10 +72,10 @@ const Val = struct { }; test "struct point to self" { - var root : Node = undefined; + var root: Node = undefined; root.val.x = 1; - var node : Node = undefined; + var node: Node = undefined; node.next = &root; node.val.x = 2; @@ -85,8 +85,8 @@ test "struct point to self" { } test "struct byval assign" { - var foo1 : StructFoo = undefined; - var foo2 : StructFoo = undefined; + var foo1: StructFoo = undefined; + var foo2: StructFoo = undefined; foo1.a = 1234; foo2.a = 0; @@ -96,46 +96,57 @@ test "struct byval assign" { } fn structInitializer() void { - const val = Val { .x = 42 }; + const val = Val { + .x = 42, + }; assert(val.x == 42); } - test "fn call of struct field" { - assert(callStructField(Foo {.ptr = aFunc,}) == 13); + assert(callStructField(Foo { + .ptr = aFunc, + }) == 13); } const Foo = struct { ptr: fn() i32, }; -fn aFunc() i32 { return 13; } +fn aFunc() i32 { + return 13; +} fn callStructField(foo: &const Foo) i32 { return foo.ptr(); } - test "store member function in variable" { - const instance = MemberFnTestFoo { .x = 1234, }; + const instance = MemberFnTestFoo { + .x = 1234, + }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); assert(result == 1234); } const MemberFnTestFoo = struct { x: i32, - fn member(foo: &const MemberFnTestFoo) i32 { return foo.x; } + fn member(foo: &const MemberFnTestFoo) i32 { + return foo.x; + } }; - test "call member function directly" { - const instance = MemberFnTestFoo { .x = 1234, }; + const instance = MemberFnTestFoo { + .x = 1234, + }; const result = MemberFnTestFoo.member(instance); assert(result == 1234); } test "member functions" { - const r = MemberFnRand {.seed = 1234}; + const r = MemberFnRand { + .seed = 1234, + }; assert(r.getSeed() == 1234); } const MemberFnRand = struct { @@ -170,17 +181,16 @@ const EmptyStruct = struct { } }; - test "return empty struct from fn" { _ = testReturnEmptyStructFromFn(); } const EmptyStruct2 = struct {}; fn testReturnEmptyStructFromFn() EmptyStruct2 { - return EmptyStruct2 {}; + return EmptyStruct2{}; } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1); + assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2 {EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -201,7 +211,6 @@ test "packed struct" { assert(four == 4); } - const BitField1 = packed struct { a: u3, b: u3, @@ -301,7 +310,7 @@ test "packed array 24bits" { assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } - var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); + var bytes = []u8 {0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &([]FooArray24Bits)(bytes[0..bytes.len - 1])[0]; assert(ptr.a == 0); @@ -351,7 +360,7 @@ test "aligned array of packed struct" { assert(@sizeOf(FooArrayOfAligned) == 2 * 2); } - var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); + var bytes = []u8 {0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &([]FooArrayOfAligned)(bytes[0..bytes.len])[0]; assert(ptr.a[0].a == 0xbb); @@ -360,11 +369,15 @@ test "aligned array of packed struct" { assert(ptr.a[1].b == 0xbb); } - - test "runtime struct initialization of bitfield" { - const s1 = Nibbles { .x = x1, .y = x1 }; - const s2 = Nibbles { .x = u4(x2), .y = u4(x2) }; + const s1 = Nibbles { + .x = x1, + .y = x1, + }; + const s2 = Nibbles { + .x = u4(x2), + .y = u4(x2), + }; assert(s1.x == x1); assert(s1.y == x1); @@ -394,7 +407,7 @@ test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; @memcpy(&bytes[0], @ptrCast(&u8, &all), 8); - var bitfields = *@ptrCast(&Bitfields, &bytes[0]); + var bitfields = @ptrCast(&Bitfields, &bytes[0]).*; assert(bitfields.f1 == 0x1111); assert(bitfields.f2 == 0x2222); diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/cases/struct_contains_null_ptr_itself.zig index 5864ef4038..b6cb1a94cc 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/cases/struct_contains_null_ptr_itself.zig @@ -19,4 +19,3 @@ pub const Node = struct { pub const NodeLineComment = struct { base: Node, }; - diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 45ec56c1e2..ee34c16baf 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -6,7 +6,7 @@ const Node = struct { }; test "struct contains slice of itself" { - var other_nodes = []Node{ + var other_nodes = []Node { Node { .payload = 31, .children = []Node{}, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index a0ac646160..b870297f18 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -6,7 +6,10 @@ test "switch with numbers" { fn testSwitchWithNumbers(x: u32) void { const result = switch (x) { - 1, 2, 3, 4 ... 8 => false, + 1, + 2, + 3, + 4 ... 8 => false, 13 => true, else => false, }; @@ -34,8 +37,10 @@ test "implicit comptime switch" { const result = switch (x) { 3 => 10, 4 => 11, - 5, 6 => 12, - 7, 8 => 13, + 5, + 6 => 12, + 7, + 8 => 13, else => 14, }; @@ -61,7 +66,6 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void { } } - test "switch statement" { nonConstSwitch(SwitchStatmentFoo.C); } @@ -81,11 +85,16 @@ const SwitchStatmentFoo = enum { D, }; - test "switch prong with variable" { - switchProngWithVarFn(SwitchProngWithVarEnum { .One = 13}); - switchProngWithVarFn(SwitchProngWithVarEnum { .Two = 13.0}); - switchProngWithVarFn(SwitchProngWithVarEnum { .Meh = {}}); + switchProngWithVarFn(SwitchProngWithVarEnum { + .One = 13, + }); + switchProngWithVarFn(SwitchProngWithVarEnum { + .Two = 13.0, + }); + switchProngWithVarFn(SwitchProngWithVarEnum { + .Meh = {}, + }); } const SwitchProngWithVarEnum = union(enum) { One: i32, @@ -93,7 +102,7 @@ const SwitchProngWithVarEnum = union(enum) { Meh: void, }; fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) void { - switch(*a) { + switch (a.*) { SwitchProngWithVarEnum.One => |x| { assert(x == 13); }, @@ -112,9 +121,11 @@ test "switch on enum using pointer capture" { } fn testSwitchEnumPtrCapture() void { - var value = SwitchProngWithVarEnum { .One = 1234 }; + var value = SwitchProngWithVarEnum { + .One = 1234, + }; switch (value) { - SwitchProngWithVarEnum.One => |*x| *x += 1, + SwitchProngWithVarEnum.One => |*x| x.* += 1, else => unreachable, } switch (value) { @@ -125,8 +136,12 @@ fn testSwitchEnumPtrCapture() void { test "switch with multiple expressions" { const x = switch (returnsFive()) { - 1, 2, 3 => 1, - 4, 5, 6 => 2, + 1, + 2, + 3 => 1, + 4, + 5, + 6 => 2, else => i32(3), }; assert(x == 2); @@ -135,14 +150,15 @@ fn returnsFive() i32 { return 5; } - const Number = union(enum) { One: u64, Two: u8, Three: f32, }; -const number = Number { .Three = 1.23 }; +const number = Number { + .Three = 1.23, +}; fn returnsFalse() bool { switch (number) { @@ -198,7 +214,8 @@ fn testSwitchHandleAllCasesRange(x: u8) u8 { return switch (x) { 0 ... 100 => u8(0), 101 ... 200 => 1, - 201, 203 => 2, + 201, + 203 => 2, 202 => 4, 204 ... 255 => 3, }; diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index 136e8834e6..2d28d2f4c7 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -14,14 +14,18 @@ const FormValue = union(enum) { fn doThing(form_id: u64) error!FormValue { return switch (form_id) { - 17 => FormValue { .Address = try readOnce() }, + 17 => FormValue { + .Address = try readOnce(), + }, else => error.InvalidDebugInfo, }; } test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { - FormValue.Address => |payload| { assert(payload == 1); }, + FormValue.Address => |payload| { + assert(payload == 1); + }, else => unreachable, } assert(read_count == 1); diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 335feeef43..3d80f3fdb2 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -7,8 +7,12 @@ const FormValue = union(enum) { fn foo(id: u64) !FormValue { return switch (id) { - 2 => FormValue { .Two = true }, - 1 => FormValue { .One = {} }, + 2 => FormValue { + .Two = true, + }, + 1 => FormValue { + .One = {}, + }, else => return error.Whatever, }; } diff --git a/test/cases/try.zig b/test/cases/try.zig index 4a0425e22e..483bf6a915 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -3,14 +3,12 @@ const assert = @import("std").debug.assert; test "try on error union" { tryOnErrorUnionImpl(); comptime tryOnErrorUnionImpl(); - } fn tryOnErrorUnionImpl() void { - const x = if (returnsTen()) |val| - val + 1 - else |err| switch (err) { - error.ItBroke, error.NoMem => 1, + const x = if (returnsTen()) |val| val + 1 else |err| switch (err) { + error.ItBroke, + error.NoMem => 1, error.CrappedOut => i32(2), else => unreachable, }; diff --git a/test/cases/undefined.zig b/test/cases/undefined.zig index bc81f9cf84..f1af10e532 100644 --- a/test/cases/undefined.zig +++ b/test/cases/undefined.zig @@ -63,6 +63,6 @@ test "assign undefined to struct with method" { } test "type name of undefined" { - const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + const x = undefined; + assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } diff --git a/test/cases/union.zig b/test/cases/union.zig index dc2a7c3414..50cf8004b9 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -10,38 +10,41 @@ const Agg = struct { val2: Value, }; -const v1 = Value { .Int = 1234 }; -const v2 = Value { .Array = []u8{3} ** 9 }; +const v1 = Value{ .Int = 1234 }; +const v2 = Value{ .Array = []u8{3} ** 9 }; -const err = (error!Agg)(Agg { +const err = (error!Agg)(Agg{ .val1 = v1, .val2 = v2, }); -const array = []Value { v1, v2, v1, v2}; - +const array = []Value{ + v1, + v2, + v1, + v2, +}; test "unions embedded in aggregate types" { switch (array[1]) { Value.Array => |arr| assert(arr[4] == 3), else => unreachable, } - switch((err catch unreachable).val1) { + switch ((err catch unreachable).val1) { Value.Int => |x| assert(x == 1234), else => unreachable, } } - const Foo = union { float: f64, int: i32, }; test "basic unions" { - var foo = Foo { .int = 1 }; + var foo = Foo{ .int = 1 }; assert(foo.int == 1); - foo = Foo {.float = 12.34}; + foo = Foo{ .float = 12.34 }; assert(foo.float == 12.34); } @@ -56,11 +59,11 @@ test "init union with runtime value" { } fn setFloat(foo: &Foo, x: f64) void { - *foo = Foo { .float = x }; + foo.* = Foo{ .float = x }; } fn setInt(foo: &Foo, x: i32) void { - *foo = Foo { .int = x }; + foo.* = Foo{ .int = x }; } const FooExtern = extern union { @@ -69,13 +72,12 @@ const FooExtern = extern union { }; test "basic extern unions" { - var foo = FooExtern { .int = 1 }; + var foo = FooExtern{ .int = 1 }; assert(foo.int == 1); foo.float = 12.34; assert(foo.float == 12.34); } - const Letter = enum { A, B, @@ -93,12 +95,12 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload {.A = 1234}) == -10); + assert(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: &const Payload) i32 { - assert(Letter(*value) == Letter.A); - return switch (*value) { + assert(Letter(value.*) == Letter.A); + return switch (value.*) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) i32(20) else 21, Payload.C => |x| if (x) i32(30) else 31, @@ -131,13 +133,13 @@ const MultipleChoice2 = union(enum(u32)) { test "union(enum(u32)) with specified and unspecified tag values" { comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); - testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 {.C = 123}); - comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2 { .C = 123} ); + testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); + comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void { - assert(u32(@TagType(MultipleChoice2)(*x)) == 60); - assert(1123 == switch (*x) { + assert(u32(@TagType(MultipleChoice2)(x.*)) == 60); + assert(1123 == switch (x.*) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -150,10 +152,9 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void }); } - const ExternPtrOrInt = extern union { ptr: &u8, - int: u64 + int: u64, }; test "extern union size" { comptime assert(@sizeOf(ExternPtrOrInt) == 8); @@ -161,7 +162,7 @@ test "extern union size" { const PackedPtrOrInt = packed union { ptr: &u8, - int: u64 + int: u64, }; test "extern union size" { comptime assert(@sizeOf(PackedPtrOrInt) == 8); @@ -174,8 +175,16 @@ test "union with only 1 field which is void should be zero bits" { comptime assert(@sizeOf(ZeroBits) == 0); } -const TheTag = enum {A, B, C}; -const TheUnion = union(TheTag) { A: i32, B: i32, C: i32 }; +const TheTag = enum { + A, + B, + C, +}; +const TheUnion = union(TheTag) { + A: i32, + B: i32, + C: i32, +}; test "union field access gives the enum values" { assert(TheUnion.A == TheTag.A); assert(TheUnion.B == TheTag.B); @@ -183,20 +192,28 @@ test "union field access gives the enum values" { } test "cast union to tag type of union" { - testCastUnionToTagType(TheUnion {.B = 1234}); - comptime testCastUnionToTagType(TheUnion {.B = 1234}); + testCastUnionToTagType(TheUnion{ .B = 1234 }); + comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); } fn testCastUnionToTagType(x: &const TheUnion) void { - assert(TheTag(*x) == TheTag.B); + assert(TheTag(x.*) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; assert(Letter2(x) == Letter2.B); } -const Letter2 = enum { A, B, C }; -const Value2 = union(Letter2) { A: i32, B, C, }; +const Letter2 = enum { + A, + B, + C, +}; +const Value2 = union(Letter2) { + A: i32, + B, + C, +}; test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; @@ -217,19 +234,16 @@ const TheUnion2 = union(enum) { }; fn assertIsTheUnion2Item1(value: &const TheUnion2) void { - assert(*value == TheUnion2.Item1); + assert(value.* == TheUnion2.Item1); } - pub const PackThis = union(enum) { Invalid: bool, StringLiteral: u2, }; test "constant packed union" { - testConstPackedUnion([]PackThis { - PackThis { .StringLiteral = 1 }, - }); + testConstPackedUnion([]PackThis{PackThis{ .StringLiteral = 1 }}); } fn testConstPackedUnion(expected_tokens: []const PackThis) void { @@ -242,7 +256,7 @@ test "switch on union with only 1 field" { switch (r) { PartialInst.Compiled => { var z: PartialInstWithPayload = undefined; - z = PartialInstWithPayload { .Compiled = 1234 }; + z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { assert(x == 1234); @@ -261,4 +275,3 @@ const PartialInst = union(enum) { const PartialInstWithPayload = union(enum) { Compiled: i32, }; - diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index cead9eb8bf..81f800568c 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -2,9 +2,12 @@ const assert = @import("std").debug.assert; fn add(args: ...) i32 { var sum = i32(0); - {comptime var i: usize = 0; inline while (i < args.len) : (i += 1) { - sum += args[i]; - }} + { + comptime var i: usize = 0; + inline while (i < args.len) : (i += 1) { + sum += args[i]; + } + } return sum; } @@ -55,18 +58,23 @@ fn extraFn(extra: u32, args: ...) usize { return args.len; } +const foos = []fn(...) bool { + foo1, + foo2, +}; -const foos = []fn(...) bool { foo1, foo2 }; - -fn foo1(args: ...) bool { return true; } -fn foo2(args: ...) bool { return false; } +fn foo1(args: ...) bool { + return true; +} +fn foo2(args: ...) bool { + return false; +} test "array of var args functions" { assert(foos[0]()); assert(!foos[1]()); } - test "pass array and slice of same array to var args should have same pointers" { const array = "hi"; const slice: []const u8 = array; @@ -79,7 +87,6 @@ fn assertSlicePtrsEql(args: ...) void { assert(s1.ptr == s2.ptr); } - test "pass zero length array to var args param" { doNothingWithFirstArg(""); } diff --git a/test/cases/while.zig b/test/cases/while.zig index 33d5a5623a..574a7b7e76 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; test "while loop" { - var i : i32 = 0; + var i: i32 = 0; while (i < 4) { i += 1; } @@ -35,7 +35,7 @@ test "continue and break" { } var continue_and_break_counter: i32 = 0; fn runContinueAndBreakTest() void { - var i : i32 = 0; + var i: i32 = 0; while (true) { continue_and_break_counter += 2; i += 1; @@ -58,10 +58,13 @@ fn returnWithImplicitCastFromWhileLoopTest() error!void { test "while with continue expression" { var sum: i32 = 0; - {var i: i32 = 0; while (i < 10) : (i += 1) { - if (i == 5) continue; - sum += i; - }} + { + var i: i32 = 0; + while (i < 10) : (i += 1) { + if (i == 5) continue; + sum += i; + } + } assert(sum == 40); } @@ -117,17 +120,13 @@ test "while with error union condition" { var numbers_left: i32 = undefined; fn getNumberOrErr() error!i32 { - return if (numbers_left == 0) - error.OutOfNumbers - else x: { + return if (numbers_left == 0) error.OutOfNumbers else x: { numbers_left -= 1; break :x numbers_left; }; } fn getNumberOrNull() ?i32 { - return if (numbers_left == 0) - null - else x: { + return if (numbers_left == 0) null else x: { numbers_left -= 1; break :x numbers_left; }; @@ -136,42 +135,48 @@ fn getNumberOrNull() ?i32 { test "while on nullable with else result follow else prong" { const result = while (returnNull()) |value| { break value; - } else i32(2); + } else + i32(2); assert(result == 2); } test "while on nullable with else result follow break prong" { const result = while (returnMaybe(10)) |value| { break value; - } else i32(2); + } else + i32(2); assert(result == 10); } test "while on error union with else result follow else prong" { const result = while (returnError()) |value| { break value; - } else |err| i32(2); + } else|err| + i32(2); assert(result == 2); } test "while on error union with else result follow break prong" { const result = while (returnSuccess(10)) |value| { break value; - } else |err| i32(2); + } else|err| + i32(2); assert(result == 10); } test "while on bool with else result follow else prong" { const result = while (returnFalse()) { break i32(10); - } else i32(2); + } else + i32(2); assert(result == 2); } test "while on bool with else result follow break prong" { const result = while (returnTrue()) { break i32(10); - } else i32(2); + } else + i32(2); assert(result == 10); } @@ -202,9 +207,21 @@ fn testContinueOuter() void { } } -fn returnNull() ?i32 { return null; } -fn returnMaybe(x: i32) ?i32 { return x; } -fn returnError() error!i32 { return error.YouWantedAnError; } -fn returnSuccess(x: i32) error!i32 { return x; } -fn returnFalse() bool { return false; } -fn returnTrue() bool { return true; } +fn returnNull() ?i32 { + return null; +} +fn returnMaybe(x: i32) ?i32 { + return x; +} +fn returnError() error!i32 { + return error.YouWantedAnError; +} +fn returnSuccess(x: i32) error!i32 { + return x; +} +fn returnFalse() bool { + return false; +} +fn returnTrue() bool { + return true; +} -- cgit v1.2.3 From ff1c4e1f13943b63dcb6d257a2ee58ae88d4b12a Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 13:00:39 +0300 Subject: Added tests. --- src/ir.cpp | 1 - test/cases/union.zig | 10 ++++++++++ test/compile_errors.zig | 12 ++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 3bce9c51cd..dfffb41873 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15975,7 +15975,6 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; fn_def_fields[7].type = ira->codegen->builtin_types.entry_type; - // @TODO Check whether this is correct. if (fn_entry->src_implicit_return_type != nullptr) fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type; else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr) diff --git a/test/cases/union.zig b/test/cases/union.zig index dc2a7c3414..e7d9c23d77 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -45,6 +45,16 @@ test "basic unions" { assert(foo.float == 12.34); } +test "comptime union field access" { + comptime { + var foo = Foo { .int = 0 }; + assert(foo.int == 0); + + foo = Foo { .float = 42.42 }; + assert(foo.float == 42.42); + } +} + test "init union with runtime value" { var foo: Foo = undefined; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8febc27b8..d9454adf2c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3209,4 +3209,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} , ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset"); + + cases.add("invalid union field access in comptime", + \\const Foo = union { + \\ Bar: u8, + \\ Baz: void, + \\}; + \\comptime { + \\ var foo = Foo {.Baz = {}}; + \\ const bar_val = foo.Bar; + \\} + , + ".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set"); } -- cgit v1.2.3 From 849ea61fa11460b1a6df2529063a6b0cabc6e5e4 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 1 May 2018 17:10:50 +0300 Subject: Small fix. --- src/ir.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index ff81ebfd86..cffee9bebc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15765,6 +15765,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na type_info_var = get_builtin_value(ira->codegen, "TypeInfo"); assert(type_info_var->type->id == TypeTableEntryIdMetaType); + ensure_complete_type(ira->codegen, type_info_var->data.x_type); type_info_type = type_info_var->data.x_type; assert(type_info_type->id == TypeTableEntryIdUnion); } @@ -15790,6 +15791,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na VariableTableEntry *var = tld->var; + ensure_complete_type(ira->codegen, var->value->type); assert(var->value->type->id == TypeTableEntryIdMetaType); return var->value->data.x_type; } -- cgit v1.2.3 From 1a9403f38a89d4a55f746d077d725424b8852d44 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 2 May 2018 21:50:41 +0200 Subject: Added better support for none pure enums in tranlate C --- src/ast_render.cpp | 2 +- src/translate_c.cpp | 108 +++++++++++++++++++-------------------------------- test/translate_c.zig | 22 +++++++++++ 3 files changed, 62 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2c3e1fc873..7f44cb7b65 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -728,7 +728,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_grouped(ar, field_node->data.struct_field.type); } if (field_node->data.struct_field.value != nullptr) { - fprintf(ar->f, "= "); + fprintf(ar->f, " = "); render_node_grouped(ar, field_node->data.struct_field.value); } fprintf(ar->f, ",\n"); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 965a8963bd..c0a76b8969 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3744,6 +3744,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name); } + bool pure_enum = true; uint32_t field_count = 0; for (auto it = enum_def->enumerator_begin(), @@ -3755,84 +3756,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { pure_enum = false; } } - AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation()); assert(tag_int_type); - if (pure_enum) { - AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); - enum_node->data.container_decl.kind = ContainerKindEnum; - enum_node->data.container_decl.layout = ContainerLayoutExtern; - // TODO only emit this tag type if the enum tag type is not the default. - // I don't know what the default is, need to figure out how clang is deciding. - // it appears to at least be different across gcc/msvc - if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && - !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) - { - enum_node->data.container_decl.init_arg_expr = tag_int_type; - } - - enum_node->data.container_decl.fields.resize(field_count); - uint32_t i = 0; - for (auto it = enum_def->enumerator_begin(), - it_end = enum_def->enumerator_end(); - it != it_end; ++it, i += 1) - { - const EnumConstantDecl *enum_const = *it; - - Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); - Buf *field_name; - if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) { - field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); - } else { - field_name = enum_val_name; - } - - AstNode *field_node = trans_create_node(c, NodeTypeStructField); - field_node->data.struct_field.name = field_name; - field_node->data.struct_field.type = nullptr; - enum_node->data.container_decl.fields.items[i] = field_node; - - // in C each enum value is in the global namespace. so we put them there too. - // at this point we can rely on the enum emitting successfully - if (is_anonymous) { - AstNode *lit_node = trans_create_node_unsigned(c, i); - add_global_var(c, enum_val_name, lit_node); - } else { - AstNode *field_access_node = trans_create_node_field_access(c, - trans_create_node_symbol(c, full_type_name), field_name); - add_global_var(c, enum_val_name, field_access_node); - } - } - - if (is_anonymous) { - c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node); - return enum_node; - } else { - AstNode *symbol_node = trans_create_node_symbol(c, full_type_name); - add_global_weak_alias(c, bare_name, full_type_name); - add_global_var(c, full_type_name, enum_node); - c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node); - return enum_node; - } + AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); + enum_node->data.container_decl.kind = ContainerKindEnum; + enum_node->data.container_decl.layout = ContainerLayoutExtern; + // TODO only emit this tag type if the enum tag type is not the default. + // I don't know what the default is, need to figure out how clang is deciding. + // it appears to at least be different across gcc/msvc + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && + !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) + { + enum_node->data.container_decl.init_arg_expr = tag_int_type; } - - // TODO after issue #305 is solved, make this be an enum with tag_int_type - // as the integer type and set the custom enum values - AstNode *enum_node = tag_int_type; - - - // add variables for all the values with enum_node + enum_node->data.container_decl.fields.resize(field_count); + uint32_t i = 0; for (auto it = enum_def->enumerator_begin(), it_end = enum_def->enumerator_end(); - it != it_end; ++it) + it != it_end; ++it, i += 1) { const EnumConstantDecl *enum_const = *it; Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); - AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal()); - AstNode *var_node = add_global_var(c, enum_val_name, int_node); - var_node->data.variable_declaration.type = tag_int_type; + Buf *field_name; + if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) { + field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name)); + } else { + field_name = enum_val_name; + } + + AstNode *int_node = pure_enum && !is_anonymous ? nullptr : trans_create_node_apint(c, enum_const->getInitVal()); + AstNode *field_node = trans_create_node(c, NodeTypeStructField); + field_node->data.struct_field.name = field_name; + field_node->data.struct_field.type = nullptr; + field_node->data.struct_field.value = int_node; + enum_node->data.container_decl.fields.items[i] = field_node; + + // in C each enum value is in the global namespace. so we put them there too. + // at this point we can rely on the enum emitting successfully + if (is_anonymous) { + Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); + add_global_var(c, enum_val_name, int_node); + } else { + AstNode *field_access_node = trans_create_node_field_access(c, + trans_create_node_symbol(c, full_type_name), field_name); + add_global_var(c, enum_val_name, field_access_node); + } } if (is_anonymous) { @@ -3843,7 +3813,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { add_global_weak_alias(c, bare_name, full_type_name); add_global_var(c, full_type_name, enum_node); c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node); - return symbol_node; + return enum_node; } } diff --git a/test/translate_c.zig b/test/translate_c.zig index 9a69c2b03e..a5b5f3ae2a 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -53,6 +53,28 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub const Foo = enum_Foo; ); + cases.add("enums", + \\enum Foo { + \\ FooA = 2, + \\ FooB = 5, + \\ Foo1, + \\}; + , + \\pub const enum_Foo = extern enum { + \\ A = 2, + \\ B = 5, + \\ @"1" = 6, + \\}; + , + \\pub const FooA = enum_Foo.A; + , + \\pub const FooB = enum_Foo.B; + , + \\pub const Foo1 = enum_Foo.@"1"; + , + \\pub const Foo = enum_Foo; + ); + cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , -- cgit v1.2.3 From 131c133bb74eab2012158c8ecfaa78944db197c7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 3 May 2018 04:43:07 +0300 Subject: Fixed inlining determination test (#972) When deciding wether we should inline a scope, look up the parents until we get to a function definition scope --- src/ir.cpp | 2 ++ test/behavior.zig | 1 + test/cases/fn_in_struct_in_comptime.zig | 17 +++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 test/cases/fn_in_struct_in_comptime.zig (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 469900bf07..47f188fdf8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -145,6 +145,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) { while (scope != nullptr) { if (scope->id == ScopeIdCompTime) return true; + if (scope->id == ScopeIdFnDef) + break; scope = scope->parent; } return false; diff --git a/test/behavior.zig b/test/behavior.zig index 2c10c6d71b..3e540e0cf4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -52,4 +52,5 @@ comptime { _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); + _ = @import("cases/fn_in_struct_in_comptime.zig"); } diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig new file mode 100644 index 0000000000..4f181d7ffb --- /dev/null +++ b/test/cases/fn_in_struct_in_comptime.zig @@ -0,0 +1,17 @@ +const assert = @import("std").debug.assert; + +fn get_foo() fn(&u8)usize { + comptime { + return struct { + fn func(ptr: &u8) usize { + var u = @ptrToInt(ptr); + return u; + } + }.func; + } +} + +test "define a function in an anonymous struct in comptime" { + const foo = get_foo(); + assert(foo(@intToPtr(&u8, 12345)) == 12345); +} -- cgit v1.2.3 From aa2586de182e5587c924740e80468c4c4d509500 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Fri, 4 May 2018 04:27:04 +0200 Subject: Fixed extern enums having the wrong size (#970) Fixed extern enums having the wrong size See #977 --- src/analyze.cpp | 8 +++++++- test/cases/enum.zig | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 1ecfe32f4c..0f2fdf15de 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2325,8 +2325,14 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { HashMap occupied_tag_values = {}; occupied_tag_values.init(field_count); - TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + TypeTableEntry *tag_int_type; + if (enum_type->data.enumeration.layout == ContainerLayoutExtern) { + tag_int_type = get_c_int_type(g, CIntTypeInt); + } else { + tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1); + } + // TODO: Are extern enums allowed to have an init_arg_expr? if (decl_node->data.container_decl.init_arg_expr != nullptr) { TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); if (type_is_invalid(wanted_tag_int_type)) { diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 644c989b04..0a2658eaf7 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -392,3 +392,12 @@ test "enum with 1 field but explicit tag type should still have the tag type" { const Enum = enum(u8) { B = 2 }; comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); } + +test "empty extern enum with members" { + const E = extern enum { + A, + B, + C, + }; + assert(@sizeOf(E) == @sizeOf(c_int)); +} -- cgit v1.2.3 From d7b029995c8ac678598de39aa106076dca232902 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sat, 5 May 2018 22:40:29 +1200 Subject: Fix bigint multi-limb shift and masks --- src/bigint.cpp | 10 ++++------ test/cases/math.zig | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/bigint.cpp b/src/bigint.cpp index 2a688debd5..64bc59e5cf 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1259,12 +1259,11 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) { bigint_normalize(dest); return; } - // TODO this code path is untested - uint64_t first_digit = dest->data.digit; + dest->digit_count = max(op1->digit_count, op2->digit_count); dest->data.digits = allocate_nonzero(dest->digit_count); - dest->data.digits[0] = first_digit; - size_t i = 1; + + size_t i = 0; for (; i < op1->digit_count && i < op2->digit_count; i += 1) { dest->data.digits[i] = op1_digits[i] & op2_digits[i]; } @@ -1412,7 +1411,6 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { return; } - // TODO this code path is untested size_t digit_shift_count = shift_amt / 64; size_t leftover_shift_count = shift_amt % 64; @@ -1427,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { uint64_t digit = op1_digits[op_digit_index]; size_t dest_digit_index = op_digit_index - digit_shift_count; dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = (0xffffffffffffffffULL << leftover_shift_count) & digit; + carry = digit << leftover_shift_count; if (dest_digit_index == 0) { break; } op_digit_index -= 1; diff --git a/test/cases/math.zig b/test/cases/math.zig index 47d001a590..3c33b14fbf 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -349,6 +349,23 @@ test "big number shifting" { } } +test "big number multi-limb shift and mask" { + comptime { + var a = 0xefffffffa0000001eeeeeeefaaaaaaab; + + assert(u32(a & 0xffffffff) == 0xaaaaaaab); + a >>= 32; + assert(u32(a & 0xffffffff) == 0xeeeeeeef); + a >>= 32; + assert(u32(a & 0xffffffff) == 0xa0000001); + a >>= 32; + assert(u32(a & 0xffffffff) == 0xefffffff); + a >>= 32; + + assert(a == 0); + } +} + test "xor" { test_xor(); comptime test_xor(); -- cgit v1.2.3 From 77a1a216d2b3ceb956869ba1716fbb6c0c7eabe8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 16:43:20 -0400 Subject: tagged union field access prioritizes members over enum tags closes #959 --- src/ir.cpp | 19 ++++++++++--------- test/cases/union.zig | 12 ++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 5339931590..cdf56f7fee 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13736,7 +13736,16 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru create_const_enum(child_type, &field->value), child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } - } else if (child_type->id == TypeTableEntryIdUnion && + } + ScopeDecls *container_scope = get_container_scope(child_type); + if (container_scope != nullptr) { + auto entry = container_scope->decl_table.maybe_get(field_name); + Tld *tld = entry ? entry->value : nullptr; + if (tld) { + return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); + } + } + if (child_type->id == TypeTableEntryIdUnion && (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || child_type->data.unionation.decl_node->data.container_decl.auto_enum)) { @@ -13753,14 +13762,6 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } } - ScopeDecls *container_scope = get_container_scope(child_type); - if (container_scope != nullptr) { - auto entry = container_scope->decl_table.maybe_get(field_name); - Tld *tld = entry ? entry->value : nullptr; - if (tld) { - return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); - } - } ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); diff --git a/test/cases/union.zig b/test/cases/union.zig index e7d9c23d77..f1fef46657 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -272,3 +272,15 @@ const PartialInstWithPayload = union(enum) { Compiled: i32, }; + +test "access a member of tagged union with conflicting enum tag name" { + const Bar = union(enum) { + A: A, + B: B, + + const A = u8; + const B = void; + }; + + comptime assert(Bar.A == u8); +} -- cgit v1.2.3 From 69ef6ae0f9c2a99119bb4a39ef2112b2250a98c5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 May 2018 21:57:44 -0400 Subject: rework std.zig.parser --- src/ir.cpp | 2 +- std/segmented_list.zig | 11 + std/zig/ast.zig | 730 ++-- std/zig/index.zig | 3 +- std/zig/parser.zig | 8480 ++++++++++++++++++++++++----------------------- std/zig/parser_test.zig | 158 +- std/zig/tokenizer.zig | 35 - 7 files changed, 4833 insertions(+), 4586 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index cdf56f7fee..095caa65ed 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14709,7 +14709,7 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source } if (value->value.type->id != TypeTableEntryIdUnion) { - ir_add_error(ira, source_instr, + ir_add_error(ira, value, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->invalid_instruction; } diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 6c7c879919..a89d332556 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -91,6 +91,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type allocator: &Allocator, len: usize, + pub const prealloc_count = prealloc_item_count; + /// Deinitialize with `deinit` pub fn init(allocator: &Allocator) Self { return Self { @@ -287,6 +289,15 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } + + pub fn peek(it: &Iterator) ?&T { + if (it.index >= it.list.len) + return null; + if (it.index < prealloc_item_count) + return &it.list.prealloc_segment[it.index]; + + return &it.list.dynamic_segments[it.shelf_index][it.box_index]; + } }; pub fn iterator(self: &Self, start_index: usize) Iterator { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index d1d7fe7914..664ab25a28 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1,12 +1,221 @@ const std = @import("../index.zig"); const assert = std.debug.assert; -const ArrayList = std.ArrayList; -const Token = std.zig.Token; +const SegmentedList = std.SegmentedList; const mem = std.mem; +const Token = std.zig.Token; + +pub const TokenIndex = usize; + +pub const Tree = struct { + source: []const u8, + tokens: TokenList, + root_node: &Node.Root, + arena_allocator: std.heap.ArenaAllocator, + errors: ErrorList, + + pub const TokenList = SegmentedList(Token, 64); + pub const ErrorList = SegmentedList(Error, 0); + + pub fn deinit(self: &Tree) void { + self.arena_allocator.deinit(); + } + + pub fn renderError(self: &Tree, parse_error: &Error, stream: var) !void { + return parse_error.render(&self.tokens, stream); + } + + pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 { + const token = self.tokens.at(token_index); + return self.source[token.start..token.end]; + } + + pub const Location = struct { + line: usize, + column: usize, + line_start: usize, + line_end: usize, + }; + + pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { + var loc = Location { + .line = 0, + .column = 0, + .line_start = start_index, + .line_end = self.source.len, + }; + const token_start = self.tokens.at(token_index).start; + for (self.source[start_index..]) |c, i| { + if (i + start_index == token_start) { + loc.line_end = i + start_index; + while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {} + return loc; + } + if (c == '\n') { + loc.line += 1; + loc.column = 0; + loc.line_start = i + 1; + } else { + loc.column += 1; + } + } + return loc; + } + +}; + +pub const Error = union(enum) { + InvalidToken: InvalidToken, + ExpectedVarDeclOrFn: ExpectedVarDeclOrFn, + ExpectedAggregateKw: ExpectedAggregateKw, + UnattachedDocComment: UnattachedDocComment, + ExpectedEqOrSemi: ExpectedEqOrSemi, + ExpectedSemiOrLBrace: ExpectedSemiOrLBrace, + ExpectedLabelable: ExpectedLabelable, + ExpectedInlinable: ExpectedInlinable, + ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType, + ExpectedCall: ExpectedCall, + ExpectedCallOrFnProto: ExpectedCallOrFnProto, + ExpectedSliceOrRBracket: ExpectedSliceOrRBracket, + ExtraAlignQualifier: ExtraAlignQualifier, + ExtraConstQualifier: ExtraConstQualifier, + ExtraVolatileQualifier: ExtraVolatileQualifier, + ExpectedPrimaryExpr: ExpectedPrimaryExpr, + ExpectedToken: ExpectedToken, + ExpectedCommaOrEnd: ExpectedCommaOrEnd, + + pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void { + switch (*self) { + // TODO https://github.com/zig-lang/zig/issues/683 + @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream), + @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedLabelable => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedInlinable => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedCall => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedCallOrFnProto => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraAlignQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraConstQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExtraVolatileQualifier => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedToken => |*x| return x.render(tokens, stream), + @TagType(Error).ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), + } + } + + pub fn loc(self: &Error) TokenIndex { + switch (*self) { + // TODO https://github.com/zig-lang/zig/issues/683 + @TagType(Error).InvalidToken => |x| return x.token, + @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token, + @TagType(Error).ExpectedAggregateKw => |x| return x.token, + @TagType(Error).UnattachedDocComment => |x| return x.token, + @TagType(Error).ExpectedEqOrSemi => |x| return x.token, + @TagType(Error).ExpectedSemiOrLBrace => |x| return x.token, + @TagType(Error).ExpectedLabelable => |x| return x.token, + @TagType(Error).ExpectedInlinable => |x| return x.token, + @TagType(Error).ExpectedAsmOutputReturnOrType => |x| return x.token, + @TagType(Error).ExpectedCall => |x| return x.node.firstToken(), + @TagType(Error).ExpectedCallOrFnProto => |x| return x.node.firstToken(), + @TagType(Error).ExpectedSliceOrRBracket => |x| return x.token, + @TagType(Error).ExtraAlignQualifier => |x| return x.token, + @TagType(Error).ExtraConstQualifier => |x| return x.token, + @TagType(Error).ExtraVolatileQualifier => |x| return x.token, + @TagType(Error).ExpectedPrimaryExpr => |x| return x.token, + @TagType(Error).ExpectedToken => |x| return x.token, + @TagType(Error).ExpectedCommaOrEnd => |x| return x.token, + } + } + + pub const InvalidToken = SingleTokenError("Invalid token {}"); + pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}"); + pub const ExpectedAggregateKw = SingleTokenError("Expected " ++ + @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++ + @tagName(Token.Id.Keyword_enum) ++ ", found {}"); + pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}"); + pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}"); + pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found {}"); + pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found {}"); + pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++ + @tagName(Token.Id.Identifier) ++ ", found {}"); + pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found {}"); + pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found {}"); + + pub const UnattachedDocComment = SimpleError("Unattached documentation comment"); + pub const ExtraAlignQualifier = SimpleError("Extra align qualifier"); + pub const ExtraConstQualifier = SimpleError("Extra const qualifier"); + pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); + + pub const ExpectedCall = struct { + node: &Node, + + pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", + @tagName(self.node.id)); + } + }; + + pub const ExpectedCallOrFnProto = struct { + node: &Node, + + pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { + return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ + @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); + } + }; + + pub const ExpectedToken = struct { + token: TokenIndex, + expected_id: @TagType(Token.Id), + + pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { + const token_name = @tagName(tokens.at(self.token).id); + return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); + } + }; + + pub const ExpectedCommaOrEnd = struct { + token: TokenIndex, + end_id: @TagType(Token.Id), + + pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { + const token_name = @tagName(tokens.at(self.token).id); + return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); + } + }; + + fn SingleTokenError(comptime msg: []const u8) type { + return struct { + const ThisError = this; + + token: TokenIndex, + + pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { + const token_name = @tagName(tokens.at(self.token).id); + return stream.print(msg, token_name); + } + }; + } + + fn SimpleError(comptime msg: []const u8) type { + return struct { + const ThisError = this; + + token: TokenIndex, + + pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { + return stream.write(msg); + } + }; + } +}; pub const Node = struct { id: Id, - same_line_comment: ?&Token, pub const Id = enum { // Top level @@ -95,7 +304,7 @@ pub const Node = struct { unreachable; } - pub fn firstToken(base: &Node) Token { + pub fn firstToken(base: &Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -106,7 +315,7 @@ pub const Node = struct { unreachable; } - pub fn lastToken(base: &Node) Token { + pub fn lastToken(base: &Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -130,8 +339,10 @@ pub const Node = struct { pub const Root = struct { base: Node, doc_comments: ?&DocComment, - decls: ArrayList(&Node), - eof_token: Token, + decls: DeclList, + eof_token: TokenIndex, + + pub const DeclList = SegmentedList(&Node, 4); pub fn iterate(self: &Root, index: usize) ?&Node { if (index < self.decls.len) { @@ -140,29 +351,29 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Root) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); + pub fn firstToken(self: &Root) TokenIndex { + return if (self.decls.len == 0) self.eof_token else (*self.decls.at(0)).firstToken(); } - pub fn lastToken(self: &Root) Token { - return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); + pub fn lastToken(self: &Root) TokenIndex { + return if (self.decls.len == 0) self.eof_token else (*self.decls.at(self.decls.len - 1)).lastToken(); } }; pub const VarDecl = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, - name_token: Token, - eq_token: Token, - mut_token: Token, - comptime_token: ?Token, - extern_export_token: ?Token, + visib_token: ?TokenIndex, + name_token: TokenIndex, + eq_token: TokenIndex, + mut_token: TokenIndex, + comptime_token: ?TokenIndex, + extern_export_token: ?TokenIndex, lib_name: ?&Node, type_node: ?&Node, align_node: ?&Node, init_node: ?&Node, - semicolon_token: Token, + semicolon_token: TokenIndex, pub fn iterate(self: &VarDecl, index: usize) ?&Node { var i = index; @@ -185,7 +396,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &VarDecl) Token { + pub fn firstToken(self: &VarDecl) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.comptime_token) |comptime_token| return comptime_token; if (self.extern_export_token) |extern_export_token| return extern_export_token; @@ -193,7 +404,7 @@ pub const Node = struct { return self.mut_token; } - pub fn lastToken(self: &VarDecl) Token { + pub fn lastToken(self: &VarDecl) TokenIndex { return self.semicolon_token; } }; @@ -201,9 +412,9 @@ pub const Node = struct { pub const Use = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, + visib_token: ?TokenIndex, expr: &Node, - semicolon_token: Token, + semicolon_token: TokenIndex, pub fn iterate(self: &Use, index: usize) ?&Node { var i = index; @@ -214,48 +425,52 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Use) Token { + pub fn firstToken(self: &Use) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.expr.firstToken(); } - pub fn lastToken(self: &Use) Token { + pub fn lastToken(self: &Use) TokenIndex { return self.semicolon_token; } }; pub const ErrorSetDecl = struct { base: Node, - error_token: Token, - decls: ArrayList(&Node), - rbrace_token: Token, + error_token: TokenIndex, + decls: DeclList, + rbrace_token: TokenIndex, + + pub const DeclList = SegmentedList(&Node, 2); pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { var i = index; - if (i < self.decls.len) return self.decls.at(i); + if (i < self.decls.len) return *self.decls.at(i); i -= self.decls.len; return null; } - pub fn firstToken(self: &ErrorSetDecl) Token { + pub fn firstToken(self: &ErrorSetDecl) TokenIndex { return self.error_token; } - pub fn lastToken(self: &ErrorSetDecl) Token { + pub fn lastToken(self: &ErrorSetDecl) TokenIndex { return self.rbrace_token; } }; pub const ContainerDecl = struct { base: Node, - ltoken: Token, + ltoken: TokenIndex, layout: Layout, kind: Kind, init_arg_expr: InitArg, - fields_and_decls: ArrayList(&Node), - rbrace_token: Token, + fields_and_decls: DeclList, + rbrace_token: TokenIndex, + + pub const DeclList = Root.DeclList; const Layout = enum { Auto, @@ -287,17 +502,17 @@ pub const Node = struct { InitArg.Enum => { } } - if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i); + if (i < self.fields_and_decls.len) return *self.fields_and_decls.at(i); i -= self.fields_and_decls.len; return null; } - pub fn firstToken(self: &ContainerDecl) Token { + pub fn firstToken(self: &ContainerDecl) TokenIndex { return self.ltoken; } - pub fn lastToken(self: &ContainerDecl) Token { + pub fn lastToken(self: &ContainerDecl) TokenIndex { return self.rbrace_token; } }; @@ -305,8 +520,8 @@ pub const Node = struct { pub const StructField = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, - name_token: Token, + visib_token: ?TokenIndex, + name_token: TokenIndex, type_expr: &Node, pub fn iterate(self: &StructField, index: usize) ?&Node { @@ -318,12 +533,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &StructField) Token { + pub fn firstToken(self: &StructField) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.name_token; } - pub fn lastToken(self: &StructField) Token { + pub fn lastToken(self: &StructField) TokenIndex { return self.type_expr.lastToken(); } }; @@ -331,7 +546,7 @@ pub const Node = struct { pub const UnionTag = struct { base: Node, doc_comments: ?&DocComment, - name_token: Token, + name_token: TokenIndex, type_expr: ?&Node, value_expr: ?&Node, @@ -351,11 +566,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &UnionTag) Token { + pub fn firstToken(self: &UnionTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &UnionTag) Token { + pub fn lastToken(self: &UnionTag) TokenIndex { if (self.value_expr) |value_expr| { return value_expr.lastToken(); } @@ -370,7 +585,7 @@ pub const Node = struct { pub const EnumTag = struct { base: Node, doc_comments: ?&DocComment, - name_token: Token, + name_token: TokenIndex, value: ?&Node, pub fn iterate(self: &EnumTag, index: usize) ?&Node { @@ -384,11 +599,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &EnumTag) Token { + pub fn firstToken(self: &EnumTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &EnumTag) Token { + pub fn lastToken(self: &EnumTag) TokenIndex { if (self.value) |value| { return value.lastToken(); } @@ -400,7 +615,7 @@ pub const Node = struct { pub const ErrorTag = struct { base: Node, doc_comments: ?&DocComment, - name_token: Token, + name_token: TokenIndex, pub fn iterate(self: &ErrorTag, index: usize) ?&Node { var i = index; @@ -413,37 +628,37 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ErrorTag) Token { + pub fn firstToken(self: &ErrorTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &ErrorTag) Token { + pub fn lastToken(self: &ErrorTag) TokenIndex { return self.name_token; } }; pub const Identifier = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &Identifier, index: usize) ?&Node { return null; } - pub fn firstToken(self: &Identifier) Token { + pub fn firstToken(self: &Identifier) TokenIndex { return self.token; } - pub fn lastToken(self: &Identifier) Token { + pub fn lastToken(self: &Identifier) TokenIndex { return self.token; } }; pub const AsyncAttribute = struct { base: Node, - async_token: Token, + async_token: TokenIndex, allocator_type: ?&Node, - rangle_bracket: ?Token, + rangle_bracket: ?TokenIndex, pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node { var i = index; @@ -456,11 +671,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsyncAttribute) Token { + pub fn firstToken(self: &AsyncAttribute) TokenIndex { return self.async_token; } - pub fn lastToken(self: &AsyncAttribute) Token { + pub fn lastToken(self: &AsyncAttribute) TokenIndex { if (self.rangle_bracket) |rangle_bracket| { return rangle_bracket; } @@ -472,19 +687,21 @@ pub const Node = struct { pub const FnProto = struct { base: Node, doc_comments: ?&DocComment, - visib_token: ?Token, - fn_token: Token, - name_token: ?Token, - params: ArrayList(&Node), + visib_token: ?TokenIndex, + fn_token: TokenIndex, + name_token: ?TokenIndex, + params: ParamList, return_type: ReturnType, - var_args_token: ?Token, - extern_export_inline_token: ?Token, - cc_token: ?Token, + var_args_token: ?TokenIndex, + extern_export_inline_token: ?TokenIndex, + cc_token: ?TokenIndex, async_attr: ?&AsyncAttribute, body_node: ?&Node, lib_name: ?&Node, // populated if this is an extern declaration align_expr: ?&Node, // populated if align(A) is present + pub const ParamList = SegmentedList(&Node, 2); + pub const ReturnType = union(enum) { Explicit: &Node, InferErrorSet: &Node, @@ -526,7 +743,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FnProto) Token { + pub fn firstToken(self: &FnProto) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; assert(self.lib_name == null); @@ -534,7 +751,7 @@ pub const Node = struct { return self.fn_token; } - pub fn lastToken(self: &FnProto) Token { + pub fn lastToken(self: &FnProto) TokenIndex { if (self.body_node) |body_node| return body_node.lastToken(); switch (self.return_type) { // TODO allow this and next prong to share bodies since the types are the same @@ -546,11 +763,11 @@ pub const Node = struct { pub const PromiseType = struct { base: Node, - promise_token: Token, + promise_token: TokenIndex, result: ?Result, pub const Result = struct { - arrow_token: Token, + arrow_token: TokenIndex, return_type: &Node, }; @@ -565,11 +782,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PromiseType) Token { + pub fn firstToken(self: &PromiseType) TokenIndex { return self.promise_token; } - pub fn lastToken(self: &PromiseType) Token { + pub fn lastToken(self: &PromiseType) TokenIndex { if (self.result) |result| return result.return_type.lastToken(); return self.promise_token; } @@ -577,11 +794,11 @@ pub const Node = struct { pub const ParamDecl = struct { base: Node, - comptime_token: ?Token, - noalias_token: ?Token, - name_token: ?Token, + comptime_token: ?TokenIndex, + noalias_token: ?TokenIndex, + name_token: ?TokenIndex, type_node: &Node, - var_args_token: ?Token, + var_args_token: ?TokenIndex, pub fn iterate(self: &ParamDecl, index: usize) ?&Node { var i = index; @@ -592,14 +809,14 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ParamDecl) Token { + pub fn firstToken(self: &ParamDecl) TokenIndex { if (self.comptime_token) |comptime_token| return comptime_token; if (self.noalias_token) |noalias_token| return noalias_token; if (self.name_token) |name_token| return name_token; return self.type_node.firstToken(); } - pub fn lastToken(self: &ParamDecl) Token { + pub fn lastToken(self: &ParamDecl) TokenIndex { if (self.var_args_token) |var_args_token| return var_args_token; return self.type_node.lastToken(); } @@ -607,10 +824,12 @@ pub const Node = struct { pub const Block = struct { base: Node, - label: ?Token, - lbrace: Token, - statements: ArrayList(&Node), - rbrace: Token, + label: ?TokenIndex, + lbrace: TokenIndex, + statements: StatementList, + rbrace: TokenIndex, + + pub const StatementList = Root.DeclList; pub fn iterate(self: &Block, index: usize) ?&Node { var i = index; @@ -621,7 +840,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Block) Token { + pub fn firstToken(self: &Block) TokenIndex { if (self.label) |label| { return label; } @@ -629,14 +848,14 @@ pub const Node = struct { return self.lbrace; } - pub fn lastToken(self: &Block) Token { + pub fn lastToken(self: &Block) TokenIndex { return self.rbrace; } }; pub const Defer = struct { base: Node, - defer_token: Token, + defer_token: TokenIndex, kind: Kind, expr: &Node, @@ -654,11 +873,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Defer) Token { + pub fn firstToken(self: &Defer) TokenIndex { return self.defer_token; } - pub fn lastToken(self: &Defer) Token { + pub fn lastToken(self: &Defer) TokenIndex { return self.expr.lastToken(); } }; @@ -666,7 +885,7 @@ pub const Node = struct { pub const Comptime = struct { base: Node, doc_comments: ?&DocComment, - comptime_token: Token, + comptime_token: TokenIndex, expr: &Node, pub fn iterate(self: &Comptime, index: usize) ?&Node { @@ -678,20 +897,20 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Comptime) Token { + pub fn firstToken(self: &Comptime) TokenIndex { return self.comptime_token; } - pub fn lastToken(self: &Comptime) Token { + pub fn lastToken(self: &Comptime) TokenIndex { return self.expr.lastToken(); } }; pub const Payload = struct { base: Node, - lpipe: Token, + lpipe: TokenIndex, error_symbol: &Node, - rpipe: Token, + rpipe: TokenIndex, pub fn iterate(self: &Payload, index: usize) ?&Node { var i = index; @@ -702,21 +921,21 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Payload) Token { + pub fn firstToken(self: &Payload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &Payload) Token { + pub fn lastToken(self: &Payload) TokenIndex { return self.rpipe; } }; pub const PointerPayload = struct { base: Node, - lpipe: Token, - ptr_token: ?Token, + lpipe: TokenIndex, + ptr_token: ?TokenIndex, value_symbol: &Node, - rpipe: Token, + rpipe: TokenIndex, pub fn iterate(self: &PointerPayload, index: usize) ?&Node { var i = index; @@ -727,22 +946,22 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerPayload) Token { + pub fn firstToken(self: &PointerPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerPayload) Token { + pub fn lastToken(self: &PointerPayload) TokenIndex { return self.rpipe; } }; pub const PointerIndexPayload = struct { base: Node, - lpipe: Token, - ptr_token: ?Token, + lpipe: TokenIndex, + ptr_token: ?TokenIndex, value_symbol: &Node, index_symbol: ?&Node, - rpipe: Token, + rpipe: TokenIndex, pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node { var i = index; @@ -758,18 +977,18 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerIndexPayload) Token { + pub fn firstToken(self: &PointerIndexPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerIndexPayload) Token { + pub fn lastToken(self: &PointerIndexPayload) TokenIndex { return self.rpipe; } }; pub const Else = struct { base: Node, - else_token: Token, + else_token: TokenIndex, payload: ?&Node, body: &Node, @@ -787,22 +1006,24 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Else) Token { + pub fn firstToken(self: &Else) TokenIndex { return self.else_token; } - pub fn lastToken(self: &Else) Token { + pub fn lastToken(self: &Else) TokenIndex { return self.body.lastToken(); } }; pub const Switch = struct { base: Node, - switch_token: Token, + switch_token: TokenIndex, expr: &Node, /// these can be SwitchCase nodes or LineComment nodes - cases: ArrayList(&Node), - rbrace: Token, + cases: CaseList, + rbrace: TokenIndex, + + pub const CaseList = SegmentedList(&Node, 2); pub fn iterate(self: &Switch, index: usize) ?&Node { var i = index; @@ -810,31 +1031,33 @@ pub const Node = struct { if (i < 1) return self.expr; i -= 1; - if (i < self.cases.len) return self.cases.at(i); + if (i < self.cases.len) return *self.cases.at(i); i -= self.cases.len; return null; } - pub fn firstToken(self: &Switch) Token { + pub fn firstToken(self: &Switch) TokenIndex { return self.switch_token; } - pub fn lastToken(self: &Switch) Token { + pub fn lastToken(self: &Switch) TokenIndex { return self.rbrace; } }; pub const SwitchCase = struct { base: Node, - items: ArrayList(&Node), + items: ItemList, payload: ?&Node, expr: &Node, + pub const ItemList = SegmentedList(&Node, 1); + pub fn iterate(self: &SwitchCase, index: usize) ?&Node { var i = index; - if (i < self.items.len) return self.items.at(i); + if (i < self.items.len) return *self.items.at(i); i -= self.items.len; if (self.payload) |payload| { @@ -848,37 +1071,37 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SwitchCase) Token { - return self.items.at(0).firstToken(); + pub fn firstToken(self: &SwitchCase) TokenIndex { + return (*self.items.at(0)).firstToken(); } - pub fn lastToken(self: &SwitchCase) Token { + pub fn lastToken(self: &SwitchCase) TokenIndex { return self.expr.lastToken(); } }; pub const SwitchElse = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &SwitchElse, index: usize) ?&Node { return null; } - pub fn firstToken(self: &SwitchElse) Token { + pub fn firstToken(self: &SwitchElse) TokenIndex { return self.token; } - pub fn lastToken(self: &SwitchElse) Token { + pub fn lastToken(self: &SwitchElse) TokenIndex { return self.token; } }; pub const While = struct { base: Node, - label: ?Token, - inline_token: ?Token, - while_token: Token, + label: ?TokenIndex, + inline_token: ?TokenIndex, + while_token: TokenIndex, condition: &Node, payload: ?&Node, continue_expr: ?&Node, @@ -912,7 +1135,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &While) Token { + pub fn firstToken(self: &While) TokenIndex { if (self.label) |label| { return label; } @@ -924,7 +1147,7 @@ pub const Node = struct { return self.while_token; } - pub fn lastToken(self: &While) Token { + pub fn lastToken(self: &While) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -935,9 +1158,9 @@ pub const Node = struct { pub const For = struct { base: Node, - label: ?Token, - inline_token: ?Token, - for_token: Token, + label: ?TokenIndex, + inline_token: ?TokenIndex, + for_token: TokenIndex, array_expr: &Node, payload: ?&Node, body: &Node, @@ -965,7 +1188,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &For) Token { + pub fn firstToken(self: &For) TokenIndex { if (self.label) |label| { return label; } @@ -977,7 +1200,7 @@ pub const Node = struct { return self.for_token; } - pub fn lastToken(self: &For) Token { + pub fn lastToken(self: &For) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -988,7 +1211,7 @@ pub const Node = struct { pub const If = struct { base: Node, - if_token: Token, + if_token: TokenIndex, condition: &Node, payload: ?&Node, body: &Node, @@ -1016,11 +1239,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &If) Token { + pub fn firstToken(self: &If) TokenIndex { return self.if_token; } - pub fn lastToken(self: &If) Token { + pub fn lastToken(self: &If) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1031,7 +1254,7 @@ pub const Node = struct { pub const InfixOp = struct { base: Node, - op_token: Token, + op_token: TokenIndex, lhs: &Node, op: Op, rhs: &Node, @@ -1146,18 +1369,18 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &InfixOp) Token { + pub fn firstToken(self: &InfixOp) TokenIndex { return self.lhs.firstToken(); } - pub fn lastToken(self: &InfixOp) Token { + pub fn lastToken(self: &InfixOp) TokenIndex { return self.rhs.lastToken(); } }; pub const PrefixOp = struct { base: Node, - op_token: Token, + op_token: TokenIndex, op: Op, rhs: &Node, @@ -1180,10 +1403,10 @@ pub const Node = struct { const AddrOfInfo = struct { align_expr: ?&Node, - bit_offset_start_token: ?Token, - bit_offset_end_token: ?Token, - const_token: ?Token, - volatile_token: ?Token, + bit_offset_start_token: ?TokenIndex, + bit_offset_end_token: ?TokenIndex, + const_token: ?TokenIndex, + volatile_token: ?TokenIndex, }; pub fn iterate(self: &PrefixOp, index: usize) ?&Node { @@ -1225,19 +1448,19 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PrefixOp) Token { + pub fn firstToken(self: &PrefixOp) TokenIndex { return self.op_token; } - pub fn lastToken(self: &PrefixOp) Token { + pub fn lastToken(self: &PrefixOp) TokenIndex { return self.rhs.lastToken(); } }; pub const FieldInitializer = struct { base: Node, - period_token: Token, - name_token: Token, + period_token: TokenIndex, + name_token: TokenIndex, expr: &Node, pub fn iterate(self: &FieldInitializer, index: usize) ?&Node { @@ -1249,11 +1472,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FieldInitializer) Token { + pub fn firstToken(self: &FieldInitializer) TokenIndex { return self.period_token; } - pub fn lastToken(self: &FieldInitializer) Token { + pub fn lastToken(self: &FieldInitializer) TokenIndex { return self.expr.lastToken(); } }; @@ -1262,24 +1485,28 @@ pub const Node = struct { base: Node, lhs: &Node, op: Op, - rtoken: Token, + rtoken: TokenIndex, - const Op = union(enum) { - Call: CallInfo, + pub const Op = union(enum) { + Call: Call, ArrayAccess: &Node, - Slice: SliceRange, - ArrayInitializer: ArrayList(&Node), - StructInitializer: ArrayList(&Node), - }; + Slice: Slice, + ArrayInitializer: InitList, + StructInitializer: InitList, - const CallInfo = struct { - params: ArrayList(&Node), - async_attr: ?&AsyncAttribute, - }; + pub const InitList = SegmentedList(&Node, 2); + + pub const Call = struct { + params: ParamList, + async_attr: ?&AsyncAttribute, - const SliceRange = struct { - start: &Node, - end: ?&Node, + pub const ParamList = SegmentedList(&Node, 2); + }; + + pub const Slice = struct { + start: &Node, + end: ?&Node, + }; }; pub fn iterate(self: &SuffixOp, index: usize) ?&Node { @@ -1290,7 +1517,7 @@ pub const Node = struct { switch (self.op) { Op.Call => |call_info| { - if (i < call_info.params.len) return call_info.params.at(i); + if (i < call_info.params.len) return *call_info.params.at(i); i -= call_info.params.len; }, Op.ArrayAccess => |index_expr| { @@ -1307,11 +1534,11 @@ pub const Node = struct { } }, Op.ArrayInitializer => |exprs| { - if (i < exprs.len) return exprs.at(i); + if (i < exprs.len) return *exprs.at(i); i -= exprs.len; }, Op.StructInitializer => |fields| { - if (i < fields.len) return fields.at(i); + if (i < fields.len) return *fields.at(i); i -= fields.len; }, } @@ -1319,20 +1546,20 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SuffixOp) Token { + pub fn firstToken(self: &SuffixOp) TokenIndex { return self.lhs.firstToken(); } - pub fn lastToken(self: &SuffixOp) Token { + pub fn lastToken(self: &SuffixOp) TokenIndex { return self.rtoken; } }; pub const GroupedExpression = struct { base: Node, - lparen: Token, + lparen: TokenIndex, expr: &Node, - rparen: Token, + rparen: TokenIndex, pub fn iterate(self: &GroupedExpression, index: usize) ?&Node { var i = index; @@ -1343,18 +1570,18 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &GroupedExpression) Token { + pub fn firstToken(self: &GroupedExpression) TokenIndex { return self.lparen; } - pub fn lastToken(self: &GroupedExpression) Token { + pub fn lastToken(self: &GroupedExpression) TokenIndex { return self.rparen; } }; pub const ControlFlowExpression = struct { base: Node, - ltoken: Token, + ltoken: TokenIndex, kind: Kind, rhs: ?&Node, @@ -1391,11 +1618,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ControlFlowExpression) Token { + pub fn firstToken(self: &ControlFlowExpression) TokenIndex { return self.ltoken; } - pub fn lastToken(self: &ControlFlowExpression) Token { + pub fn lastToken(self: &ControlFlowExpression) TokenIndex { if (self.rhs) |rhs| { return rhs.lastToken(); } @@ -1420,8 +1647,8 @@ pub const Node = struct { pub const Suspend = struct { base: Node, - label: ?Token, - suspend_token: Token, + label: ?TokenIndex, + suspend_token: TokenIndex, payload: ?&Node, body: ?&Node, @@ -1441,12 +1668,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Suspend) Token { + pub fn firstToken(self: &Suspend) TokenIndex { if (self.label) |label| return label; return self.suspend_token; } - pub fn lastToken(self: &Suspend) Token { + pub fn lastToken(self: &Suspend) TokenIndex { if (self.body) |body| { return body.lastToken(); } @@ -1461,177 +1688,181 @@ pub const Node = struct { pub const IntegerLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &IntegerLiteral) Token { + pub fn firstToken(self: &IntegerLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &IntegerLiteral) Token { + pub fn lastToken(self: &IntegerLiteral) TokenIndex { return self.token; } }; pub const FloatLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &FloatLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &FloatLiteral) Token { + pub fn firstToken(self: &FloatLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &FloatLiteral) Token { + pub fn lastToken(self: &FloatLiteral) TokenIndex { return self.token; } }; pub const BuiltinCall = struct { base: Node, - builtin_token: Token, - params: ArrayList(&Node), - rparen_token: Token, + builtin_token: TokenIndex, + params: ParamList, + rparen_token: TokenIndex, + + pub const ParamList = SegmentedList(&Node, 2); pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { var i = index; - if (i < self.params.len) return self.params.at(i); + if (i < self.params.len) return *self.params.at(i); i -= self.params.len; return null; } - pub fn firstToken(self: &BuiltinCall) Token { + pub fn firstToken(self: &BuiltinCall) TokenIndex { return self.builtin_token; } - pub fn lastToken(self: &BuiltinCall) Token { + pub fn lastToken(self: &BuiltinCall) TokenIndex { return self.rparen_token; } }; pub const StringLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &StringLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &StringLiteral) Token { + pub fn firstToken(self: &StringLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &StringLiteral) Token { + pub fn lastToken(self: &StringLiteral) TokenIndex { return self.token; } }; pub const MultilineStringLiteral = struct { base: Node, - tokens: ArrayList(Token), + lines: LineList, + + pub const LineList = SegmentedList(TokenIndex, 4); pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &MultilineStringLiteral) Token { - return self.tokens.at(0); + pub fn firstToken(self: &MultilineStringLiteral) TokenIndex { + return *self.lines.at(0); } - pub fn lastToken(self: &MultilineStringLiteral) Token { - return self.tokens.at(self.tokens.len - 1); + pub fn lastToken(self: &MultilineStringLiteral) TokenIndex { + return *self.lines.at(self.lines.len - 1); } }; pub const CharLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &CharLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &CharLiteral) Token { + pub fn firstToken(self: &CharLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &CharLiteral) Token { + pub fn lastToken(self: &CharLiteral) TokenIndex { return self.token; } }; pub const BoolLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &BoolLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &BoolLiteral) Token { + pub fn firstToken(self: &BoolLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &BoolLiteral) Token { + pub fn lastToken(self: &BoolLiteral) TokenIndex { return self.token; } }; pub const NullLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &NullLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &NullLiteral) Token { + pub fn firstToken(self: &NullLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &NullLiteral) Token { + pub fn lastToken(self: &NullLiteral) TokenIndex { return self.token; } }; pub const UndefinedLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &UndefinedLiteral) Token { + pub fn firstToken(self: &UndefinedLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &UndefinedLiteral) Token { + pub fn lastToken(self: &UndefinedLiteral) TokenIndex { return self.token; } }; pub const ThisLiteral = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &ThisLiteral, index: usize) ?&Node { return null; } - pub fn firstToken(self: &ThisLiteral) Token { + pub fn firstToken(self: &ThisLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &ThisLiteral) Token { + pub fn lastToken(self: &ThisLiteral) TokenIndex { return self.token; } }; @@ -1670,11 +1901,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmOutput) Token { + pub fn firstToken(self: &AsmOutput) TokenIndex { return self.symbolic_name.firstToken(); } - pub fn lastToken(self: &AsmOutput) Token { + pub fn lastToken(self: &AsmOutput) TokenIndex { return switch (self.kind) { Kind.Variable => |variable_name| variable_name.lastToken(), Kind.Return => |return_type| return_type.lastToken(), @@ -1703,139 +1934,144 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmInput) Token { + pub fn firstToken(self: &AsmInput) TokenIndex { return self.symbolic_name.firstToken(); } - pub fn lastToken(self: &AsmInput) Token { + pub fn lastToken(self: &AsmInput) TokenIndex { return self.expr.lastToken(); } }; pub const Asm = struct { base: Node, - asm_token: Token, - volatile_token: ?Token, + asm_token: TokenIndex, + volatile_token: ?TokenIndex, template: &Node, - //tokens: ArrayList(AsmToken), - outputs: ArrayList(&AsmOutput), - inputs: ArrayList(&AsmInput), - cloppers: ArrayList(&Node), - rparen: Token, + outputs: OutputList, + inputs: InputList, + clobbers: ClobberList, + rparen: TokenIndex, + + const OutputList = SegmentedList(&AsmOutput, 2); + const InputList = SegmentedList(&AsmInput, 2); + const ClobberList = SegmentedList(&Node, 2); pub fn iterate(self: &Asm, index: usize) ?&Node { var i = index; - if (i < self.outputs.len) return &self.outputs.at(index).base; + if (i < self.outputs.len) return &(*self.outputs.at(index)).base; i -= self.outputs.len; - if (i < self.inputs.len) return &self.inputs.at(index).base; + if (i < self.inputs.len) return &(*self.inputs.at(index)).base; i -= self.inputs.len; - if (i < self.cloppers.len) return self.cloppers.at(index); - i -= self.cloppers.len; + if (i < self.clobbers.len) return *self.clobbers.at(index); + i -= self.clobbers.len; return null; } - pub fn firstToken(self: &Asm) Token { + pub fn firstToken(self: &Asm) TokenIndex { return self.asm_token; } - pub fn lastToken(self: &Asm) Token { + pub fn lastToken(self: &Asm) TokenIndex { return self.rparen; } }; pub const Unreachable = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &Unreachable, index: usize) ?&Node { return null; } - pub fn firstToken(self: &Unreachable) Token { + pub fn firstToken(self: &Unreachable) TokenIndex { return self.token; } - pub fn lastToken(self: &Unreachable) Token { + pub fn lastToken(self: &Unreachable) TokenIndex { return self.token; } }; pub const ErrorType = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &ErrorType, index: usize) ?&Node { return null; } - pub fn firstToken(self: &ErrorType) Token { + pub fn firstToken(self: &ErrorType) TokenIndex { return self.token; } - pub fn lastToken(self: &ErrorType) Token { + pub fn lastToken(self: &ErrorType) TokenIndex { return self.token; } }; pub const VarType = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &VarType, index: usize) ?&Node { return null; } - pub fn firstToken(self: &VarType) Token { + pub fn firstToken(self: &VarType) TokenIndex { return self.token; } - pub fn lastToken(self: &VarType) Token { + pub fn lastToken(self: &VarType) TokenIndex { return self.token; } }; pub const LineComment = struct { base: Node, - token: Token, + token: TokenIndex, pub fn iterate(self: &LineComment, index: usize) ?&Node { return null; } - pub fn firstToken(self: &LineComment) Token { + pub fn firstToken(self: &LineComment) TokenIndex { return self.token; } - pub fn lastToken(self: &LineComment) Token { + pub fn lastToken(self: &LineComment) TokenIndex { return self.token; } }; pub const DocComment = struct { base: Node, - lines: ArrayList(Token), + lines: LineList, + + pub const LineList = SegmentedList(TokenIndex, 4); pub fn iterate(self: &DocComment, index: usize) ?&Node { return null; } - pub fn firstToken(self: &DocComment) Token { - return self.lines.at(0); + pub fn firstToken(self: &DocComment) TokenIndex { + return *self.lines.at(0); } - pub fn lastToken(self: &DocComment) Token { - return self.lines.at(self.lines.len - 1); + pub fn lastToken(self: &DocComment) TokenIndex { + return *self.lines.at(self.lines.len - 1); } }; pub const TestDecl = struct { base: Node, doc_comments: ?&DocComment, - test_token: Token, + test_token: TokenIndex, name: &Node, body_node: &Node, @@ -1848,11 +2084,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &TestDecl) Token { + pub fn firstToken(self: &TestDecl) TokenIndex { return self.test_token; } - pub fn lastToken(self: &TestDecl) Token { + pub fn lastToken(self: &TestDecl) TokenIndex { return self.body_node.lastToken(); } }; diff --git a/std/zig/index.zig b/std/zig/index.zig index 32699935d9..42965f3710 100644 --- a/std/zig/index.zig +++ b/std/zig/index.zig @@ -1,7 +1,8 @@ const tokenizer = @import("tokenizer.zig"); pub const Token = tokenizer.Token; pub const Tokenizer = tokenizer.Tokenizer; -pub const Parser = @import("parser.zig").Parser; +pub const parse = @import("parser.zig").parse; +pub const render = @import("parser.zig").renderSource; pub const ast = @import("ast.zig"); test "std.zig tests" { diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 74271f1aaf..306d460cff 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -1,4188 +1,4159 @@ const std = @import("../index.zig"); const assert = std.debug.assert; -const ArrayList = std.ArrayList; +const SegmentedList = std.SegmentedList; const mem = std.mem; const ast = std.zig.ast; const Tokenizer = std.zig.Tokenizer; const Token = std.zig.Token; +const TokenIndex = ast.TokenIndex; +const Error = ast.Error; const builtin = @import("builtin"); const io = std.io; -// TODO when we make parse errors into error types instead of printing directly, -// get rid of this -const warn = std.debug.warn; - -pub const Parser = struct { - util_allocator: &mem.Allocator, - tokenizer: &Tokenizer, - put_back_tokens: [2]Token, - put_back_count: usize, - source_file_name: []const u8, - - pub const Tree = struct { - root_node: &ast.Node.Root, - arena_allocator: std.heap.ArenaAllocator, - - pub fn deinit(self: &Tree) void { - self.arena_allocator.deinit(); +/// Returns an AST tree, allocated with the parser's allocator. +/// Result should be freed with tree.deinit() when there are +/// no more references to any AST nodes of the tree. +pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { + var tree_arena = std.heap.ArenaAllocator.init(allocator); + errdefer tree_arena.deinit(); + + var stack = SegmentedList(State, 32).init(allocator); + defer stack.deinit(); + + const arena = &tree_arena.allocator; + const root_node = try createNode(arena, ast.Node.Root, + ast.Node.Root { + .base = undefined, + .decls = ast.Node.Root.DeclList.init(arena), + .doc_comments = null, + // initialized when we get the eof token + .eof_token = undefined, } + ); + + var tree = ast.Tree { + .source = source, + .root_node = root_node, + .arena_allocator = tree_arena, + .tokens = ast.Tree.TokenList.init(arena), + .errors = ast.Tree.ErrorList.init(arena), }; - // This memory contents are used only during a function call. It's used to repurpose memory; - // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and - // source rendering. - const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } ); - utility_bytes: []align(utility_bytes_align) u8, - - /// allocator must outlive the returned Parser and all the parse trees you create with it. - pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser { - return Parser { - .util_allocator = allocator, - .tokenizer = tokenizer, - .put_back_tokens = undefined, - .put_back_count = 0, - .source_file_name = source_file_name, - .utility_bytes = []align(utility_bytes_align) u8{}, - }; - } - - pub fn deinit(self: &Parser) void { - self.util_allocator.free(self.utility_bytes); - } - - const TopLevelDeclCtx = struct { - decls: &ArrayList(&ast.Node), - visib_token: ?Token, - extern_export_inline_token: ?Token, - lib_name: ?&ast.Node, - comments: ?&ast.Node.DocComment, - }; - - const VarDeclCtx = struct { - mut_token: Token, - visib_token: ?Token, - comptime_token: ?Token, - extern_export_token: ?Token, - lib_name: ?&ast.Node, - list: &ArrayList(&ast.Node), - comments: ?&ast.Node.DocComment, - }; - - const TopLevelExternOrFieldCtx = struct { - visib_token: Token, - container_decl: &ast.Node.ContainerDecl, - comments: ?&ast.Node.DocComment, - }; - - const ExternTypeCtx = struct { - opt_ctx: OptionalCtx, - extern_token: Token, - comments: ?&ast.Node.DocComment, - }; - - const ContainerKindCtx = struct { - opt_ctx: OptionalCtx, - ltoken: Token, - layout: ast.Node.ContainerDecl.Layout, - }; - - const ExpectTokenSave = struct { - id: Token.Id, - ptr: &Token, - }; - - const OptionalTokenSave = struct { - id: Token.Id, - ptr: &?Token, - }; - - const ExprListCtx = struct { - list: &ArrayList(&ast.Node), - end: Token.Id, - ptr: &Token, - }; - - fn ListSave(comptime T: type) type { - return struct { - list: &ArrayList(T), - ptr: &Token, - }; + var tokenizer = Tokenizer.init(tree.source); + while (true) { + const token_ptr = try tree.tokens.addOne(); + *token_ptr = tokenizer.next(); + if (token_ptr.id == Token.Id.Eof) + break; } + var tok_it = tree.tokens.iterator(0); - const MaybeLabeledExpressionCtx = struct { - label: Token, - opt_ctx: OptionalCtx, - }; - - const LabelCtx = struct { - label: ?Token, - opt_ctx: OptionalCtx, - }; - - const InlineCtx = struct { - label: ?Token, - inline_token: ?Token, - opt_ctx: OptionalCtx, - }; - - const LoopCtx = struct { - label: ?Token, - inline_token: ?Token, - loop_token: Token, - opt_ctx: OptionalCtx, - }; - - const AsyncEndCtx = struct { - ctx: OptionalCtx, - attribute: &ast.Node.AsyncAttribute, - }; - - const ErrorTypeOrSetDeclCtx = struct { - opt_ctx: OptionalCtx, - error_token: Token, - }; - - const ParamDeclEndCtx = struct { - fn_proto: &ast.Node.FnProto, - param_decl: &ast.Node.ParamDecl, - }; - - const ComptimeStatementCtx = struct { - comptime_token: Token, - block: &ast.Node.Block, - }; - - const OptionalCtx = union(enum) { - Optional: &?&ast.Node, - RequiredNull: &?&ast.Node, - Required: &&ast.Node, - - pub fn store(self: &const OptionalCtx, value: &ast.Node) void { - switch (*self) { - OptionalCtx.Optional => |ptr| *ptr = value, - OptionalCtx.RequiredNull => |ptr| *ptr = value, - OptionalCtx.Required => |ptr| *ptr = value, - } - } - - pub fn get(self: &const OptionalCtx) ?&ast.Node { - switch (*self) { - OptionalCtx.Optional => |ptr| return *ptr, - OptionalCtx.RequiredNull => |ptr| return ??*ptr, - OptionalCtx.Required => |ptr| return *ptr, - } - } + try stack.push(State.TopLevel); - pub fn toRequired(self: &const OptionalCtx) OptionalCtx { - switch (*self) { - OptionalCtx.Optional => |ptr| { - return OptionalCtx { .RequiredNull = ptr }; - }, - OptionalCtx.RequiredNull => |ptr| return *self, - OptionalCtx.Required => |ptr| return *self, - } - } - }; + while (true) { + // This gives us 1 free push that can't fail + const state = ??stack.pop(); - const AddCommentsCtx = struct { - node_ptr: &&ast.Node, - comments: ?&ast.Node.DocComment, - }; - - const State = union(enum) { - TopLevel, - TopLevelExtern: TopLevelDeclCtx, - TopLevelLibname: TopLevelDeclCtx, - TopLevelDecl: TopLevelDeclCtx, - TopLevelExternOrField: TopLevelExternOrFieldCtx, - - ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.Node.ContainerDecl, - ContainerInitArg: &ast.Node.ContainerDecl, - ContainerDecl: &ast.Node.ContainerDecl, - - VarDecl: VarDeclCtx, - VarDeclAlign: &ast.Node.VarDecl, - VarDeclEq: &ast.Node.VarDecl, - - FnDef: &ast.Node.FnProto, - FnProto: &ast.Node.FnProto, - FnProtoAlign: &ast.Node.FnProto, - FnProtoReturnType: &ast.Node.FnProto, - - ParamDecl: &ast.Node.FnProto, - ParamDeclAliasOrComptime: &ast.Node.ParamDecl, - ParamDeclName: &ast.Node.ParamDecl, - ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.Node.FnProto, - - MaybeLabeledExpression: MaybeLabeledExpressionCtx, - LabeledExpression: LabelCtx, - Inline: InlineCtx, - While: LoopCtx, - WhileContinueExpr: &?&ast.Node, - For: LoopCtx, - Else: &?&ast.Node.Else, - - Block: &ast.Node.Block, - Statement: &ast.Node.Block, - ComptimeStatement: ComptimeStatementCtx, - Semicolon: &&ast.Node, - LookForSameLineComment: &&ast.Node, - LookForSameLineCommentDirect: &ast.Node, - - AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), - AsmOutputReturnOrType: &ast.Node.AsmOutput, - AsmInputItems: &ArrayList(&ast.Node.AsmInput), - AsmClopperItems: &ArrayList(&ast.Node), - - ExprListItemOrEnd: ExprListCtx, - ExprListCommaOrEnd: ExprListCtx, - FieldInitListItemOrEnd: ListSave(&ast.Node), - FieldInitListCommaOrEnd: ListSave(&ast.Node), - FieldListCommaOrEnd: &ast.Node.ContainerDecl, - FieldInitValue: OptionalCtx, - ErrorTagListItemOrEnd: ListSave(&ast.Node), - ErrorTagListCommaOrEnd: ListSave(&ast.Node), - SwitchCaseOrEnd: ListSave(&ast.Node), - SwitchCaseCommaOrEnd: ListSave(&ast.Node), - SwitchCaseFirstItem: &ArrayList(&ast.Node), - SwitchCaseItem: &ArrayList(&ast.Node), - SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), - - SuspendBody: &ast.Node.Suspend, - AsyncAllocator: &ast.Node.AsyncAttribute, - AsyncEnd: AsyncEndCtx, - - ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.Node.SuffixOp, - SliceOrArrayType: &ast.Node.PrefixOp, - AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, - - Payload: OptionalCtx, - PointerPayload: OptionalCtx, - PointerIndexPayload: OptionalCtx, - - Expression: OptionalCtx, - RangeExpressionBegin: OptionalCtx, - RangeExpressionEnd: OptionalCtx, - AssignmentExpressionBegin: OptionalCtx, - AssignmentExpressionEnd: OptionalCtx, - UnwrapExpressionBegin: OptionalCtx, - UnwrapExpressionEnd: OptionalCtx, - BoolOrExpressionBegin: OptionalCtx, - BoolOrExpressionEnd: OptionalCtx, - BoolAndExpressionBegin: OptionalCtx, - BoolAndExpressionEnd: OptionalCtx, - ComparisonExpressionBegin: OptionalCtx, - ComparisonExpressionEnd: OptionalCtx, - BinaryOrExpressionBegin: OptionalCtx, - BinaryOrExpressionEnd: OptionalCtx, - BinaryXorExpressionBegin: OptionalCtx, - BinaryXorExpressionEnd: OptionalCtx, - BinaryAndExpressionBegin: OptionalCtx, - BinaryAndExpressionEnd: OptionalCtx, - BitShiftExpressionBegin: OptionalCtx, - BitShiftExpressionEnd: OptionalCtx, - AdditionExpressionBegin: OptionalCtx, - AdditionExpressionEnd: OptionalCtx, - MultiplyExpressionBegin: OptionalCtx, - MultiplyExpressionEnd: OptionalCtx, - CurlySuffixExpressionBegin: OptionalCtx, - CurlySuffixExpressionEnd: OptionalCtx, - TypeExprBegin: OptionalCtx, - TypeExprEnd: OptionalCtx, - PrefixOpExpression: OptionalCtx, - SuffixOpExpressionBegin: OptionalCtx, - SuffixOpExpressionEnd: OptionalCtx, - PrimaryExpression: OptionalCtx, - - ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, - StringLiteral: OptionalCtx, - Identifier: OptionalCtx, - ErrorTag: &&ast.Node, - - - IfToken: @TagType(Token.Id), - IfTokenSave: ExpectTokenSave, - ExpectToken: @TagType(Token.Id), - ExpectTokenSave: ExpectTokenSave, - OptionalTokenSave: OptionalTokenSave, - }; + switch (state) { + State.TopLevel => { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try root_node.decls.push(&line_comment.base); + } - /// Returns an AST tree, allocated with the parser's allocator. - /// Result should be freed with tree.deinit() when there are - /// no more references to any AST nodes of the tree. - pub fn parse(self: &Parser) !Tree { - var stack = self.initUtilityArrayList(State); - defer self.deinitUtilityArrayList(stack); - - var arena_allocator = std.heap.ArenaAllocator.init(self.util_allocator); - errdefer arena_allocator.deinit(); - - const arena = &arena_allocator.allocator; - const root_node = try self.createNode(arena, ast.Node.Root, - ast.Node.Root { - .base = undefined, - .decls = ArrayList(&ast.Node).init(arena), - .doc_comments = null, - // initialized when we get the eof token - .eof_token = undefined, - } - ); - - try stack.append(State.TopLevel); - - while (true) { - //{ - // const token = self.getNextToken(); - // warn("{} ", @tagName(token.id)); - // self.putBackToken(token); - // var i: usize = stack.len; - // while (i != 0) { - // i -= 1; - // warn("{} ", @tagName(stack.items[i])); - // } - // warn("\n"); - //} - - // This gives us 1 free append that can't fail - const state = stack.pop(); - - switch (state) { - State.TopLevel => { - while (try self.eatLineComment(arena)) |line_comment| { - try root_node.decls.append(&line_comment.base); - } + const comments = try eatDocComments(arena, &tok_it); - const comments = try self.eatDocComments(arena); - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_test => { - stack.append(State.TopLevel) catch unreachable; + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_test => { + stack.push(State.TopLevel) catch unreachable; - const block = try arena.construct(ast.Node.Block { - .base = ast.Node { - .id = ast.Node.Id.Block, - .same_line_comment = null, - }, + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { + .id = ast.Node.Id.Block, + }, + .label = null, + .lbrace = undefined, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + const test_node = try arena.construct(ast.Node.TestDecl { + .base = ast.Node { + .id = ast.Node.Id.TestDecl, + }, + .doc_comments = comments, + .test_token = token_index, + .name = undefined, + .body_node = &block.base, + }); + try root_node.decls.push(&test_node.base); + try stack.push(State { .Block = block }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); + continue; + }, + Token.Id.Eof => { + root_node.eof_token = token_index; + root_node.doc_comments = comments; + return tree; + }, + Token.Id.Keyword_pub => { + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &root_node.decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + Token.Id.Keyword_comptime => { + const block = try createNode(arena, ast.Node.Block, + ast.Node.Block { + .base = undefined, .label = null, .lbrace = undefined, - .statements = ArrayList(&ast.Node).init(arena), + .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const test_node = try arena.construct(ast.Node.TestDecl { - .base = ast.Node { - .id = ast.Node.Id.TestDecl, - .same_line_comment = null, - }, - .doc_comments = comments, - .test_token = token, - .name = undefined, - .body_node = &block.base, - }); - try root_node.decls.append(&test_node.base); - try stack.append(State { .Block = block }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } }); - continue; - }, - Token.Id.Eof => { - root_node.eof_token = token; - root_node.doc_comments = comments; - return Tree { - .root_node = root_node, - .arena_allocator = arena_allocator, - }; - }, - Token.Id.Keyword_pub => { - stack.append(State.TopLevel) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - Token.Id.Keyword_comptime => { - const block = try self.createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = undefined, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime, - ast.Node.Comptime { - .base = undefined, - .comptime_token = token, - .expr = &block.base, - .doc_comments = comments, - } - ); - stack.append(State.TopLevel) catch unreachable; - try stack.append(State { .Block = block }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.LBrace, - .ptr = &block.rbrace, - } - }); - continue; - }, - else => { - self.putBackToken(token); - stack.append(State.TopLevel) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &root_node.decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - } - }, - State.TopLevelExtern => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_export, Token.Id.Keyword_inline => { - stack.append(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = token, - .lib_name = null, - .comments = ctx.comments, + } + ); + const node = try arena.construct(ast.Node.Comptime { + .base = ast.Node { + .id = ast.Node.Id.Comptime, + }, + .comptime_token = token_index, + .expr = &block.base, + .doc_comments = comments, + }); + try root_node.decls.push(&node.base); + + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { .Block = block }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.LBrace, + .ptr = &block.rbrace, + } + }); + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State.TopLevel) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &root_node.decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; + }, + } + }, + State.TopLevelExtern => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_export, Token.Id.Keyword_inline => { + stack.push(State { + .TopLevelDecl = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken { + .index = token_index, + .ptr = token_ptr, }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_extern => { - stack.append(State { - .TopLevelLibname = TopLevelDeclCtx { - .decls = ctx.decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = token, - .lib_name = null, - .comments = ctx.comments, + .lib_name = null, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.push(State { + .TopLevelLibname = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = AnnotatedToken { + .index = token_index, + .ptr = token_ptr, }, - }) catch unreachable; - continue; - }, - else => { - self.putBackToken(token); - stack.append(State { .TopLevelDecl = ctx }) catch unreachable; - continue; - } + .lib_name = null, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State { .TopLevelDecl = ctx }) catch unreachable; + continue; } - }, - State.TopLevelLibname => |ctx| { - const lib_name = blk: { - const lib_name_token = self.getNextToken(); - break :blk (try self.parseStringLiteral(arena, lib_name_token)) ?? { - self.putBackToken(lib_name_token); - break :blk null; - }; + } + }, + State.TopLevelLibname => |ctx| { + const lib_name = blk: { + const lib_name_token_index = tok_it.index; + const lib_name_token_ptr = ??tok_it.next(); + break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? { + _ = tok_it.prev(); + break :blk null; }; + }; + + stack.push(State { + .TopLevelDecl = TopLevelDeclCtx { + .decls = ctx.decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = ctx.extern_export_inline_token, + .lib_name = lib_name, + .comments = ctx.comments, + }, + }) catch unreachable; + continue; + }, + State.TopLevelDecl => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_use => { + if (ctx.extern_export_inline_token) |annotated_token| { + *(try tree.errors.addOne()) = Error { + .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, + }; + return tree; + } - stack.append(State { - .TopLevelDecl = TopLevelDeclCtx { - .decls = ctx.decls, + const node = try arena.construct(ast.Node.Use { + .base = ast.Node {.id = ast.Node.Id.Use }, .visib_token = ctx.visib_token, - .extern_export_inline_token = ctx.extern_export_inline_token, - .lib_name = lib_name, - .comments = ctx.comments, - }, - }) catch unreachable; - continue; - }, - State.TopLevelDecl => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_use => { - if (ctx.extern_export_inline_token != null) { - return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id)); - } + .expr = undefined, + .semicolon_token = undefined, + .doc_comments = ctx.comments, + }); + try ctx.decls.push(&node.base); - const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use, - ast.Node.Use { - .base = undefined, - .visib_token = ctx.visib_token, - .expr = undefined, - .semicolon_token = undefined, - .doc_comments = ctx.comments, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &node.semicolon_token, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - if (ctx.extern_export_inline_token) |extern_export_inline_token| { - if (extern_export_inline_token.id == Token.Id.Keyword_inline) { - return self.parseError(token, "Invalid token {}", @tagName(extern_export_inline_token.id)); - } + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &node.semicolon_token, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + if (ctx.extern_export_inline_token) |annotated_token| { + if (annotated_token.ptr.id == Token.Id.Keyword_inline) { + *(try tree.errors.addOne()) = Error { + .InvalidToken = Error.InvalidToken { .token = annotated_token.index }, + }; + return tree; } + } - try stack.append(State { - .VarDecl = VarDeclCtx { - .comments = ctx.comments, - .visib_token = ctx.visib_token, - .lib_name = ctx.lib_name, - .comptime_token = null, - .extern_export_token = ctx.extern_export_inline_token, - .mut_token = token, - .list = ctx.decls - } - }); - continue; - }, - Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, - Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = ctx.comments, + try stack.push(State { + .VarDecl = VarDeclCtx { + .comments = ctx.comments, .visib_token = ctx.visib_token, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_export_inline_token, - .cc_token = null, - .async_attr = null, - .body_node = null, .lib_name = ctx.lib_name, - .align_expr = null, - }); - try ctx.decls.append(&fn_proto.base); - stack.append(State { .FnDef = fn_proto }) catch unreachable; - try stack.append(State { .FnProto = fn_proto }); - - switch (token.id) { - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - fn_proto.cc_token = token; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - continue; - }, - Token.Id.Keyword_async => { - const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - fn_proto.async_attr = async_node; - - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token, - } - }); - try stack.append(State { .AsyncAllocator = async_node }); - continue; - }, - Token.Id.Keyword_fn => { - fn_proto.fn_token = token; - continue; - }, - else => unreachable, + .comptime_token = null, + .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .mut_token = token_index, + .list = ctx.decls } - }, - else => { - return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)); - }, - } - }, - State.TopLevelExternOrField => |ctx| { - if (self.eatToken(Token.Id.Identifier)) |identifier| { - std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); - const node = try arena.construct(ast.Node.StructField { + }); + continue; + }, + Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, + Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { + const fn_proto = try arena.construct(ast.Node.FnProto { .base = ast.Node { - .id = ast.Node.Id.StructField, - .same_line_comment = null, + .id = ast.Node.Id.FnProto, }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, - .name_token = identifier, - .type_expr = undefined, + .name_token = null, + .fn_token = undefined, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = ctx.lib_name, + .align_expr = null, }); - const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; - - stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); - continue; - } + try ctx.decls.push(&fn_proto.base); + stack.push(State { .FnDef = fn_proto }) catch unreachable; + try stack.push(State { .FnProto = fn_proto }); + + switch (token_ptr.id) { + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + fn_proto.cc_token = token_index; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + continue; + }, + Token.Id.Keyword_async => { + const async_node = try createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { + .base = undefined, + .async_token = token_index, + .allocator_type = null, + .rangle_bracket = null, + } + ); + fn_proto.async_attr = async_node; - stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &ctx.container_decl.fields_and_decls, - .visib_token = ctx.visib_token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = ctx.comments, + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token, + } + }); + try stack.push(State { .AsyncAllocator = async_node }); + continue; + }, + Token.Id.Keyword_fn => { + fn_proto.fn_token = token_index; + continue; + }, + else => unreachable, } + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index }, + }; + return tree; + }, + } + }, + State.TopLevelExternOrField => |ctx| { + if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| { + std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .name_token = identifier, + .type_expr = undefined, }); + const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; + + stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); + try stack.push(State { .ExpectToken = Token.Id.Colon }); continue; - }, + } - State.FieldInitValue => |ctx| { - const eq_tok = self.getNextToken(); - if (eq_tok.id != Token.Id.Equal) { - self.putBackToken(eq_tok); - continue; + stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &ctx.container_decl.fields_and_decls, + .visib_token = ctx.visib_token, + .extern_export_inline_token = null, + .lib_name = null, + .comments = ctx.comments, } - stack.append(State { .Expression = ctx }) catch unreachable; + }); + continue; + }, + + State.FieldInitValue => |ctx| { + const eq_tok_index = tok_it.index; + const eq_tok_ptr = ??tok_it.next(); + if (eq_tok_ptr.id != Token.Id.Equal) { + _ = tok_it.prev(); continue; - }, + } + stack.push(State { .Expression = ctx }) catch unreachable; + continue; + }, - State.ContainerKind => |ctx| { - const token = self.getNextToken(); - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, - ast.Node.ContainerDecl { - .base = undefined, - .ltoken = ctx.ltoken, - .layout = ctx.layout, - .kind = switch (token.id) { - Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, - Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, - Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, - else => { - return self.parseError(token, "expected {}, {} or {}, found {}", - @tagName(Token.Id.Keyword_struct), - @tagName(Token.Id.Keyword_union), - @tagName(Token.Id.Keyword_enum), - @tagName(token.id)); - }, + State.ContainerKind => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl, + ast.Node.ContainerDecl { + .base = undefined, + .ltoken = ctx.ltoken, + .layout = ctx.layout, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct, + Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union, + Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index }, + }; + return tree; }, - .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, - .fields_and_decls = ArrayList(&ast.Node).init(arena), - .rbrace_token = undefined, - } - ); + }, + .init_arg_expr = ast.Node.ContainerDecl.InitArg.None, + .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), + .rbrace_token = undefined, + } + ); - stack.append(State { .ContainerDecl = node }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LBrace }); - try stack.append(State { .ContainerInitArgStart = node }); + stack.push(State { .ContainerDecl = node }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LBrace }); + try stack.push(State { .ContainerInitArgStart = node }); + continue; + }, + + State.ContainerInitArgStart => |container_decl| { + if (eatToken(&tok_it, Token.Id.LParen) == null) { continue; - }, + } - State.ContainerInitArgStart => |container_decl| { - if (self.eatToken(Token.Id.LParen) == null) { - continue; - } + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.push(State { .ContainerInitArg = container_decl }); + continue; + }, - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .ContainerInitArg = container_decl }); - continue; - }, + State.ContainerInitArg => |container_decl| { + const init_arg_token_index = tok_it.index; + const init_arg_token_ptr = ??tok_it.next(); + switch (init_arg_token_ptr.id) { + Token.Id.Keyword_enum => { + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; + const lparen_tok_index = tok_it.index; + const lparen_tok_ptr = ??tok_it.next(); + if (lparen_tok_ptr.id == Token.Id.LParen) { + try stack.push(State { .ExpectToken = Token.Id.RParen } ); + try stack.push(State { .Expression = OptionalCtx { + .RequiredNull = &container_decl.init_arg_expr.Enum, + } }); + } else { + _ = tok_it.prev(); + } + }, + else => { + _ = tok_it.prev(); + container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; + stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; + }, + } + continue; + }, - State.ContainerInitArg => |container_decl| { - const init_arg_token = self.getNextToken(); - switch (init_arg_token.id) { - Token.Id.Keyword_enum => { - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; - const lparen_tok = self.getNextToken(); - if (lparen_tok.id == Token.Id.LParen) { - try stack.append(State { .ExpectToken = Token.Id.RParen } ); - try stack.append(State { .Expression = OptionalCtx { - .RequiredNull = &container_decl.init_arg_expr.Enum, - } }); - } else { - self.putBackToken(lparen_tok); - } - }, - else => { - self.putBackToken(init_arg_token); - container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined }; - stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable; - }, - } - continue; - }, + State.ContainerDecl => |container_decl| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try container_decl.fields_and_decls.push(&line_comment.base); + } - State.ContainerDecl => |container_decl| { - while (try self.eatLineComment(arena)) |line_comment| { - try container_decl.fields_and_decls.append(&line_comment.base); - } + const comments = try eatDocComments(arena, &tok_it); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Identifier => { + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => { + const node = try arena.construct(ast.Node.StructField { + .base = ast.Node { + .id = ast.Node.Id.StructField, + }, + .doc_comments = comments, + .visib_token = null, + .name_token = token_index, + .type_expr = undefined, + }); + const node_ptr = try container_decl.fields_and_decls.addOne(); + *node_ptr = &node.base; - const comments = try self.eatDocComments(arena); - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Identifier => { - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => { - const node = try arena.construct(ast.Node.StructField { - .base = ast.Node { - .id = ast.Node.Id.StructField, - .same_line_comment = null, - }, - .doc_comments = comments, - .visib_token = null, - .name_token = token, - .type_expr = undefined, - }); - const node_ptr = try container_decl.fields_and_decls.addOne(); - *node_ptr = &node.base; - - try stack.append(State { .FieldListCommaOrEnd = container_decl }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); - try stack.append(State { .ExpectToken = Token.Id.Colon }); - continue; - }, - ast.Node.ContainerDecl.Kind.Union => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag, - ast.Node.UnionTag { - .base = undefined, - .name_token = token, - .type_expr = null, - .value_expr = null, - .doc_comments = comments, - } - ); + try stack.push(State { .FieldListCommaOrEnd = container_decl }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); + try stack.push(State { .ExpectToken = Token.Id.Colon }); + continue; + }, + ast.Node.ContainerDecl.Kind.Union => { + const node = try arena.construct(ast.Node.UnionTag { + .base = ast.Node {.id = ast.Node.Id.UnionTag }, + .name_token = token_index, + .type_expr = null, + .value_expr = null, + .doc_comments = comments, + }); + try container_decl.fields_and_decls.push(&node.base); - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); - try stack.append(State { .IfToken = Token.Id.Colon }); - continue; - }, - ast.Node.ContainerDecl.Kind.Enum => { - const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag, - ast.Node.EnumTag { - .base = undefined, - .name_token = token, - .value = null, - .doc_comments = comments, - } - ); + stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + continue; + }, + ast.Node.ContainerDecl.Kind.Enum => { + const node = try arena.construct(ast.Node.EnumTag { + .base = ast.Node { .id = ast.Node.Id.EnumTag }, + .name_token = token_index, + .value = null, + .doc_comments = comments, + }); + try container_decl.fields_and_decls.push(&node.base); - stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); - try stack.append(State { .IfToken = Token.Id.Equal }); - continue; - }, - } - }, - Token.Id.Keyword_pub => { - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => { - try stack.append(State { - .TopLevelExternOrField = TopLevelExternOrFieldCtx { - .visib_token = token, - .container_decl = container_decl, - .comments = comments, - } - }); - continue; - }, - else => { - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - } + stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } }); + try stack.push(State { .IfToken = Token.Id.Equal }); + continue; + }, + } + }, + Token.Id.Keyword_pub => { + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => { + try stack.push(State { + .TopLevelExternOrField = TopLevelExternOrFieldCtx { + .visib_token = token_index, + .container_decl = container_decl, + .comments = comments, + } + }); + continue; + }, + else => { + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; } - }, - Token.Id.Keyword_export => { - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = token, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; - }, - Token.Id.RBrace => { - if (comments != null) { - return self.parseError(token, "doc comments must be attached to a node"); + } + }, + Token.Id.Keyword_export => { + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = token_index, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, } - container_decl.rbrace_token = token; - continue; - }, - else => { - self.putBackToken(token); - stack.append(State{ .ContainerDecl = container_decl }) catch unreachable; - try stack.append(State { - .TopLevelExtern = TopLevelDeclCtx { - .decls = &container_decl.fields_and_decls, - .visib_token = null, - .extern_export_inline_token = null, - .lib_name = null, - .comments = comments, - } - }); - continue; + }); + continue; + }, + Token.Id.RBrace => { + if (comments != null) { + *(try tree.errors.addOne()) = Error { + .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index }, + }; + return tree; } + container_decl.rbrace_token = token_index; + continue; + }, + else => { + _ = tok_it.prev(); + stack.push(State{ .ContainerDecl = container_decl }) catch unreachable; + try stack.push(State { + .TopLevelExtern = TopLevelDeclCtx { + .decls = &container_decl.fields_and_decls, + .visib_token = null, + .extern_export_inline_token = null, + .lib_name = null, + .comments = comments, + } + }); + continue; } - }, + } + }, - State.VarDecl => |ctx| { - const var_decl = try arena.construct(ast.Node.VarDecl { - .base = ast.Node { - .id = ast.Node.Id.VarDecl, - .same_line_comment = null, - }, - .doc_comments = ctx.comments, - .visib_token = ctx.visib_token, - .mut_token = ctx.mut_token, - .comptime_token = ctx.comptime_token, - .extern_export_token = ctx.extern_export_token, - .type_node = null, - .align_node = null, - .init_node = null, - .lib_name = ctx.lib_name, - // initialized later - .name_token = undefined, - .eq_token = undefined, - .semicolon_token = undefined, - }); - try ctx.list.append(&var_decl.base); - - try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base }); - try stack.append(State { .VarDeclAlign = var_decl }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &var_decl.name_token, - } - }); - continue; - }, - State.VarDeclAlign => |var_decl| { - try stack.append(State { .VarDeclEq = var_decl }); - - const next_token = self.getNextToken(); - if (next_token.id == Token.Id.Keyword_align) { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; + State.VarDecl => |ctx| { + const var_decl = try arena.construct(ast.Node.VarDecl { + .base = ast.Node { + .id = ast.Node.Id.VarDecl, + }, + .doc_comments = ctx.comments, + .visib_token = ctx.visib_token, + .mut_token = ctx.mut_token, + .comptime_token = ctx.comptime_token, + .extern_export_token = ctx.extern_export_token, + .type_node = null, + .align_node = null, + .init_node = null, + .lib_name = ctx.lib_name, + // initialized later + .name_token = undefined, + .eq_token = undefined, + .semicolon_token = undefined, + }); + try ctx.list.push(&var_decl.base); + + try stack.push(State { .VarDeclAlign = var_decl }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &var_decl.name_token, } - - self.putBackToken(next_token); + }); + continue; + }, + State.VarDeclAlign => |var_decl| { + try stack.push(State { .VarDeclEq = var_decl }); + + const next_token_index = tok_it.index; + const next_token_ptr = ??tok_it.next(); + if (next_token_ptr.id == Token.Id.Keyword_align) { + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); continue; - }, - State.VarDeclEq => |var_decl| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Equal => { - var_decl.eq_token = token; - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Semicolon, - .ptr = &var_decl.semicolon_token, - }, - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); - continue; - }, - Token.Id.Semicolon => { - var_decl.semicolon_token = token; - continue; - }, - else => { - return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id)); - } - } - }, - + } - State.FnDef => |fn_proto| { - const token = self.getNextToken(); - switch(token.id) { - Token.Id.LBrace => { - const block = try self.createNode(arena, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - fn_proto.body_node = &block.base; - stack.append(State { .Block = block }) catch unreachable; - continue; - }, - Token.Id.Semicolon => continue, - else => { - return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)); - }, + _ = tok_it.prev(); + continue; + }, + State.VarDeclEq => |var_decl| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Equal => { + var_decl.eq_token = token_index; + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Semicolon, + .ptr = &var_decl.semicolon_token, + }, + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } }); + continue; + }, + Token.Id.Semicolon => { + var_decl.semicolon_token = token_index; + continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index }, + }; + return tree; } - }, - State.FnProto => |fn_proto| { - stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable; - try stack.append(State { .ParamDecl = fn_proto }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); + } + }, - if (self.eatToken(Token.Id.Identifier)) |name_token| { - fn_proto.name_token = name_token; - } - continue; - }, - State.FnProtoAlign => |fn_proto| { - stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable; - if (self.eatToken(Token.Id.Keyword_align)) |align_token| { - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - } - continue; - }, - State.FnProtoReturnType => |fn_proto| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Bang => { - fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; - stack.append(State { - .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, - }) catch unreachable; - continue; - }, - else => { - // TODO: this is a special case. Remove this when #760 is fixed - if (token.id == Token.Id.Keyword_error) { - if (self.isPeekToken(Token.Id.LBrace)) { - fn_proto.return_type = ast.Node.FnProto.ReturnType { - .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base - }; - continue; - } - } + State.FnDef => |fn_proto| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch(token_ptr.id) { + Token.Id.LBrace => { + const block = try arena.construct(ast.Node.Block { + .base = ast.Node { .id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + fn_proto.body_node = &block.base; + stack.push(State { .Block = block }) catch unreachable; + continue; + }, + Token.Id.Semicolon => continue, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index }, + }; + return tree; + }, + } + }, + State.FnProto => |fn_proto| { + stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable; + try stack.push(State { .ParamDecl = fn_proto }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); - self.putBackToken(token); - fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; - continue; - }, - } - }, + if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| { + fn_proto.name_token = name_token; + } + continue; + }, + State.FnProtoAlign => |fn_proto| { + stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable; + if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| { + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + } + continue; + }, + State.FnProtoReturnType => |fn_proto| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Bang => { + fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined }; + stack.push(State { + .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet }, + }) catch unreachable; + continue; + }, + else => { + // TODO: this is a special case. Remove this when #760 is fixed + if (token_ptr.id == Token.Id.Keyword_error) { + if ((??tok_it.peek()).id == Token.Id.LBrace) { + const error_type_node = try arena.construct(ast.Node.ErrorType { + .base = ast.Node { .id = ast.Node.Id.ErrorType }, + .token = token_index, + }); + fn_proto.return_type = ast.Node.FnProto.ReturnType { + .Explicit = &error_type_node.base, + }; + continue; + } + } - State.ParamDecl => |fn_proto| { - if (self.eatToken(Token.Id.RParen)) |_| { + _ = tok_it.prev(); + fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable; continue; - } - const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl, - ast.Node.ParamDecl { - .base = undefined, - .comptime_token = null, - .noalias_token = null, - .name_token = null, - .type_node = undefined, - .var_args_token = null, - }, - ); + }, + } + }, - stack.append(State { - .ParamDeclEnd = ParamDeclEndCtx { - .param_decl = param_decl, - .fn_proto = fn_proto, - } - }) catch unreachable; - try stack.append(State { .ParamDeclName = param_decl }); - try stack.append(State { .ParamDeclAliasOrComptime = param_decl }); + + State.ParamDecl => |fn_proto| { + if (eatToken(&tok_it, Token.Id.RParen)) |_| { continue; - }, - State.ParamDeclAliasOrComptime => |param_decl| { - if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| { - param_decl.comptime_token = comptime_token; - } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| { - param_decl.noalias_token = noalias_token; + } + const param_decl = try arena.construct(ast.Node.ParamDecl { + .base = ast.Node {.id = ast.Node.Id.ParamDecl }, + .comptime_token = null, + .noalias_token = null, + .name_token = null, + .type_node = undefined, + .var_args_token = null, + }); + try fn_proto.params.push(¶m_decl.base); + + stack.push(State { + .ParamDeclEnd = ParamDeclEndCtx { + .param_decl = param_decl, + .fn_proto = fn_proto, } - continue; - }, - State.ParamDeclName => |param_decl| { - // TODO: Here, we eat two tokens in one state. This means that we can't have - // comments between these two tokens. - if (self.eatToken(Token.Id.Identifier)) |ident_token| { - if (self.eatToken(Token.Id.Colon)) |_| { - param_decl.name_token = ident_token; - } else { - self.putBackToken(ident_token); - } + }) catch unreachable; + try stack.push(State { .ParamDeclName = param_decl }); + try stack.push(State { .ParamDeclAliasOrComptime = param_decl }); + continue; + }, + State.ParamDeclAliasOrComptime => |param_decl| { + if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| { + param_decl.comptime_token = comptime_token; + } else if (eatToken(&tok_it, Token.Id.Keyword_noalias)) |noalias_token| { + param_decl.noalias_token = noalias_token; + } + continue; + }, + State.ParamDeclName => |param_decl| { + // TODO: Here, we eat two tokens in one state. This means that we can't have + // comments between these two tokens. + if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + if (eatToken(&tok_it, Token.Id.Colon)) |_| { + param_decl.name_token = ident_token; + } else { + _ = tok_it.prev(); } + } + continue; + }, + State.ParamDeclEnd => |ctx| { + if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + ctx.param_decl.var_args_token = ellipsis3; + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; continue; - }, - State.ParamDeclEnd => |ctx| { - if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - ctx.param_decl.var_args_token = ellipsis3; - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + } + + try stack.push(State { .ParamDeclComma = ctx.fn_proto }); + try stack.push(State { + .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } + }); + continue; + }, + State.ParamDeclComma => |fn_proto| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + ExpectCommaOrEndResult.end_token => |t| { + if (t == null) { + stack.push(State { .ParamDecl = fn_proto }) catch unreachable; + } continue; - } + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, - try stack.append(State { .ParamDeclComma = ctx.fn_proto }); - try stack.append(State { - .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node } - }); - continue; - }, - State.ParamDeclComma => |fn_proto| { - if ((try self.expectCommaOrEnd(Token.Id.RParen)) == null) { - stack.append(State { .ParamDecl = fn_proto }) catch unreachable; - } + State.MaybeLabeledExpression => |ctx| { + if (eatToken(&tok_it, Token.Id.Colon)) |_| { + stack.push(State { + .LabeledExpression = LabelCtx { + .label = ctx.label, + .opt_ctx = ctx.opt_ctx, + } + }) catch unreachable; continue; - }, + } - State.MaybeLabeledExpression => |ctx| { - if (self.eatToken(Token.Id.Colon)) |_| { - stack.append(State { - .LabeledExpression = LabelCtx { + _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); + continue; + }, + State.LabeledExpression => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.LBrace => { + const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, + ast.Node.Block { + .base = undefined, .label = ctx.label, - .opt_ctx = ctx.opt_ctx, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + } + ); + stack.push(State { .Block = block }) catch unreachable; + continue; + }, + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .label = ctx.label, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } }) catch unreachable; continue; - } - - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label); - continue; - }, - State.LabeledExpression => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = ctx.label, - .lbrace = token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - stack.append(State { .Block = block }) catch unreachable; - continue; - }, - Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .label = ctx.label, - .inline_token = null, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_suspend => { - const node = try arena.construct(ast.Node.Suspend { - .base = ast.Node { - .id = ast.Node.Id.Suspend, - .same_line_comment = null, - }, + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { .label = ctx.label, - .suspend_token = token, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - continue; - }, - Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = ctx.label, - .inline_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - else => { - if (ctx.opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id)); - } - - self.putBackToken(token); - continue; - }, - } - }, - State.Inline => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .inline_token = ctx.inline_token, - .label = ctx.label, - .loop_token = token, - .opt_ctx = ctx.opt_ctx.toRequired(), - } - }) catch unreachable; - continue; - }, - else => { - if (ctx.opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id)); + .inline_token = null, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } - - self.putBackToken(token); - continue; - }, - } - }, - State.While => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, - ast.Node.While { - .base = undefined, - .label = ctx.label, - .inline_token = ctx.inline_token, - .while_token = ctx.loop_token, - .condition = undefined, - .payload = null, - .continue_expr = null, - .body = undefined, - .@"else" = null, - } - ); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .WhileContinueExpr = &node.continue_expr }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.WhileContinueExpr => |dest| { - stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.For => |ctx| { - const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, - ast.Node.For { - .base = undefined, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_suspend => { + const node = try arena.construct(ast.Node.Suspend { + .base = ast.Node { + .id = ast.Node.Id.Suspend, + }, .label = ctx.label, - .inline_token = ctx.inline_token, - .for_token = ctx.loop_token, - .array_expr = undefined, + .suspend_token = token_index, .payload = null, - .body = undefined, - .@"else" = null, - } - ); - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - State.Else => |dest| { - if (self.eatToken(Token.Id.Keyword_else)) |else_token| { - const node = try self.createNode(arena, ast.Node.Else, - ast.Node.Else { - .base = undefined, - .else_token = else_token, - .payload = null, - .body = undefined, + .body = null, + }); + ctx.opt_ctx.store(&node.base); + stack.push(State { .SuspendBody = node }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + continue; + }, + Token.Id.Keyword_inline => { + stack.push(State { + .Inline = InlineCtx { + .label = ctx.label, + .inline_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } - ); - *dest = node; + }) catch unreachable; + continue; + }, + else => { + if (ctx.opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index }, + }; + return tree; + } - stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + _ = tok_it.prev(); continue; - } else { + }, + } + }, + State.Inline => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), + } + }) catch unreachable; continue; - } - }, - - - State.Block => |block| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.RBrace => { - block.rbrace = token; - continue; - }, - else => { - self.putBackToken(token); - stack.append(State { .Block = block }) catch unreachable; - - var any_comments = false; - while (try self.eatLineComment(arena)) |line_comment| { - try block.statements.append(&line_comment.base); - any_comments = true; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .inline_token = ctx.inline_token, + .label = ctx.label, + .loop_token = token_index, + .opt_ctx = ctx.opt_ctx.toRequired(), } - if (any_comments) continue; - - try stack.append(State { .Statement = block }); - continue; - }, - } - }, - State.Statement => |block| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_comptime => { - stack.append(State { - .ComptimeStatement = ComptimeStatementCtx { - .comptime_token = token, - .block = block, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.append(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = null, - .extern_export_token = null, - .lib_name = null, - .mut_token = token, - .list = &block.statements, - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.construct(ast.Node.Defer { - .base = ast.Node { - .id = ast.Node.Id.Defer, - .same_line_comment = null, - }, - .defer_token = token, - .kind = switch (token.id) { - Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, - Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, - else => unreachable, - }, - .expr = undefined, - }); - const node_ptr = try block.statements.addOne(); - *node_ptr = &node.base; - - stack.append(State { .Semicolon = node_ptr }) catch unreachable; - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); - continue; - }, - Token.Id.LBrace => { - const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - stack.append(State { .Block = inner_block }) catch unreachable; - continue; - }, - else => { - self.putBackToken(token); - const statement = try block.statements.addOne(); - stack.append(State { .LookForSameLineComment = statement }) catch unreachable; - try stack.append(State { .Semicolon = statement }); - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); - continue; - } - } - }, - State.ComptimeStatement => |ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_var, Token.Id.Keyword_const => { - stack.append(State { - .VarDecl = VarDeclCtx { - .comments = null, - .visib_token = null, - .comptime_token = ctx.comptime_token, - .extern_export_token = null, - .lib_name = null, - .mut_token = token, - .list = &ctx.block.statements, - } - }) catch unreachable; - continue; - }, - else => { - self.putBackToken(token); - self.putBackToken(ctx.comptime_token); - const statement = try ctx.block.statements.addOne(); - stack.append(State { .LookForSameLineComment = statement }) catch unreachable; - try stack.append(State { .Semicolon = statement }); - try stack.append(State { .Expression = OptionalCtx { .Required = statement } }); - continue; - } - } - }, - State.Semicolon => |node_ptr| { - const node = *node_ptr; - if (requireSemiColon(node)) { - stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + }) catch unreachable; continue; - } - continue; - }, - - State.LookForSameLineComment => |node_ptr| { - try self.lookForSameLineComment(arena, *node_ptr); - continue; - }, - - State.LookForSameLineCommentDirect => |node| { - try self.lookForSameLineComment(arena, node); - continue; - }, - + }, + else => { + if (ctx.opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index }, + }; + return tree; + } - State.AsmOutputItems => |items| { - const lbracket = self.getNextToken(); - if (lbracket.id != Token.Id.LBracket) { - self.putBackToken(lbracket); + _ = tok_it.prev(); continue; + }, + } + }, + State.While => |ctx| { + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While, + ast.Node.While { + .base = undefined, + .label = ctx.label, + .inline_token = ctx.inline_token, + .while_token = ctx.loop_token, + .condition = undefined, + .payload = null, + .continue_expr = null, + .body = undefined, + .@"else" = null, } - - const node = try self.createNode(arena, ast.Node.AsmOutput, - ast.Node.AsmOutput { - .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .kind = undefined, - } - ); - try items.append(node); - - stack.append(State { .AsmOutputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .AsmOutputReturnOrType = node }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); - continue; - }, - State.AsmOutputReturnOrType => |node| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Identifier => { - node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) }; - continue; - }, - Token.Id.Arrow => { - node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; - try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); - continue; - }, - else => { - return self.parseError(token, "expected '->' or {}, found {}", - @tagName(Token.Id.Identifier), - @tagName(token.id)); - }, - } - }, - State.AsmInputItems => |items| { - const lbracket = self.getNextToken(); - if (lbracket.id != Token.Id.LBracket) { - self.putBackToken(lbracket); - continue; + ); + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .WhileContinueExpr = &node.continue_expr }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.WhileContinueExpr => |dest| { + stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable; + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.For => |ctx| { + const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For, + ast.Node.For { + .base = undefined, + .label = ctx.label, + .inline_token = ctx.inline_token, + .for_token = ctx.loop_token, + .array_expr = undefined, + .payload = null, + .body = undefined, + .@"else" = null, } - - const node = try self.createNode(arena, ast.Node.AsmInput, - ast.Node.AsmInput { + ); + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + State.Else => |dest| { + if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| { + const node = try createNode(arena, ast.Node.Else, + ast.Node.Else { .base = undefined, - .symbolic_name = undefined, - .constraint = undefined, - .expr = undefined, + .else_token = else_token, + .payload = null, + .body = undefined, } ); - try items.append(node); - - stack.append(State { .AsmInputItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + *dest = node; + + stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); continue; - }, - State.AsmClopperItems => |items| { - stack.append(State { .AsmClopperItems = items }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + } else { continue; - }, + } + }, - State.ExprListItemOrEnd => |list_state| { - if (self.eatToken(list_state.end)) |token| { - *list_state.ptr = token; + State.Block => |block| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.RBrace => { + block.rbrace = token_index; continue; - } + }, + else => { + _ = tok_it.prev(); + stack.push(State { .Block = block }) catch unreachable; + + var any_comments = false; + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try block.statements.push(&line_comment.base); + any_comments = true; + } + if (any_comments) continue; - stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); - continue; - }, - State.ExprListCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(list_state.end)) |end| { - *list_state.ptr = end; + try stack.push(State { .Statement = block }); continue; - } else { - stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable; + }, + } + }, + State.Statement => |block| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_comptime => { + stack.push(State { + .ComptimeStatement = ComptimeStatementCtx { + .comptime_token = token_index, + .block = block, + } + }) catch unreachable; continue; - } - }, - State.FieldInitListItemOrEnd => |list_state| { - while (try self.eatLineComment(arena)) |line_comment| { - try list_state.list.append(&line_comment.base); - } + }, + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.push(State { + .VarDecl = VarDeclCtx { + .comments = null, + .visib_token = null, + .comptime_token = null, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &block.statements, + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { + const node = try arena.construct(ast.Node.Defer { + .base = ast.Node { + .id = ast.Node.Id.Defer, + }, + .defer_token = token_index, + .kind = switch (token_ptr.id) { + Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, + Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, + else => unreachable, + }, + .expr = undefined, + }); + const node_ptr = try block.statements.addOne(); + *node_ptr = &node.base; - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; + stack.push(State { .Semicolon = node_ptr }) catch unreachable; + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); continue; - } + }, + Token.Id.LBrace => { + const inner_block = try arena.construct(ast.Node.Block { + .base = ast.Node { .id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + try block.statements.push(&inner_block.base); - const node = try arena.construct(ast.Node.FieldInitializer { - .base = ast.Node { - .id = ast.Node.Id.FieldInitializer, - .same_line_comment = null, - }, - .period_token = undefined, - .name_token = undefined, - .expr = undefined, - }); - try list_state.list.append(&node.base); - - stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.Equal }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Identifier, - .ptr = &node.name_token, - } - }); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Period, - .ptr = &node.period_token, - } - }); - continue; - }, - State.FieldInitListCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { - *list_state.ptr = end; + stack.push(State { .Block = inner_block }) catch unreachable; continue; - } else { - stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; + }, + else => { + _ = tok_it.prev(); + const statement = try block.statements.addOne(); + try stack.push(State { .Semicolon = statement }); + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); continue; } - }, - State.FieldListCommaOrEnd => |container_decl| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { - container_decl.rbrace_token = end; + } + }, + State.ComptimeStatement => |ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_var, Token.Id.Keyword_const => { + stack.push(State { + .VarDecl = VarDeclCtx { + .comments = null, + .visib_token = null, + .comptime_token = ctx.comptime_token, + .extern_export_token = null, + .lib_name = null, + .mut_token = token_index, + .list = &ctx.block.statements, + } + }) catch unreachable; + continue; + }, + else => { + _ = tok_it.prev(); + _ = tok_it.prev(); + const statement = try ctx.block.statements.addOne(); + try stack.push(State { .Semicolon = statement }); + try stack.push(State { .Expression = OptionalCtx { .Required = statement } }); continue; } + } + }, + State.Semicolon => |node_ptr| { + const node = *node_ptr; + if (requireSemiColon(node)) { + stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; + continue; + } + continue; + }, - try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]); - try stack.append(State { .ContainerDecl = container_decl }); + State.AsmOutputItems => |items| { + const lbracket_index = tok_it.index; + const lbracket_ptr = ??tok_it.next(); + if (lbracket_ptr.id != Token.Id.LBracket) { + _ = tok_it.prev(); continue; - }, - State.ErrorTagListItemOrEnd => |list_state| { - while (try self.eatLineComment(arena)) |line_comment| { - try list_state.list.append(&line_comment.base); - } + } - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; + const node = try createNode(arena, ast.Node.AsmOutput, + ast.Node.AsmOutput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .kind = undefined, } - - const node_ptr = try list_state.list.addOne(); - - try stack.append(State { .ErrorTagListCommaOrEnd = list_state }); - try stack.append(State { .ErrorTag = node_ptr }); - continue; - }, - State.ErrorTagListCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { - *list_state.ptr = end; + ); + try items.push(node); + + stack.push(State { .AsmOutputItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .AsmOutputReturnOrType = node }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmOutputReturnOrType => |node| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Identifier => { + node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) }; continue; - } else { - stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + }, + Token.Id.Arrow => { + node.kind = ast.Node.AsmOutput.Kind { .Return = undefined }; + try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } }); continue; - } - }, - State.SwitchCaseOrEnd => |list_state| { - while (try self.eatLineComment(arena)) |line_comment| { - try list_state.list.append(&line_comment.base); - } + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType { + .token = token_index, + }, + }; + return tree; + }, + } + }, + State.AsmInputItems => |items| { + const lbracket_index = tok_it.index; + const lbracket_ptr = ??tok_it.next(); + if (lbracket_ptr.id != Token.Id.LBracket) { + _ = tok_it.prev(); + continue; + } - if (self.eatToken(Token.Id.RBrace)) |rbrace| { - *list_state.ptr = rbrace; - continue; + const node = try createNode(arena, ast.Node.AsmInput, + ast.Node.AsmInput { + .base = undefined, + .symbolic_name = undefined, + .constraint = undefined, + .expr = undefined, } + ); + try items.push(node); + + stack.push(State { .AsmInputItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } }); + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } }); + continue; + }, + State.AsmClobberItems => |items| { + stack.push(State { .AsmClobberItems = items }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } }); + continue; + }, - const comments = try self.eatDocComments(arena); - const node = try arena.construct(ast.Node.SwitchCase { - .base = ast.Node { - .id = ast.Node.Id.SwitchCase, - .same_line_comment = null, - }, - .items = ArrayList(&ast.Node).init(arena), - .payload = null, - .expr = undefined, - }); - try list_state.list.append(&node.base); - try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .SwitchCaseFirstItem = &node.items }); + State.ExprListItemOrEnd => |list_state| { + if (eatToken(&tok_it, list_state.end)) |token_index| { + *list_state.ptr = token_index; continue; - }, + } - State.SwitchCaseCommaOrEnd => |list_state| { - if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { + stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } }); + continue; + }, + State.ExprListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, list_state.end)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { *list_state.ptr = end; continue; - } + } else { + stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.FieldInitListItemOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } - const node = list_state.list.toSlice()[list_state.list.len - 1]; - try self.lookForSameLineComment(arena, node); - try stack.append(State { .SwitchCaseOrEnd = list_state }); + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; continue; - }, + } - State.SwitchCaseFirstItem => |case_items| { - const token = self.getNextToken(); - if (token.id == Token.Id.Keyword_else) { - const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse, - ast.Node.SwitchElse { - .base = undefined, - .token = token, - } - ); - try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + const node = try arena.construct(ast.Node.FieldInitializer { + .base = ast.Node { + .id = ast.Node.Id.FieldInitializer, + }, + .period_token = undefined, + .name_token = undefined, + .expr = undefined, + }); + try list_state.list.push(&node.base); + + stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.Equal }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Identifier, + .ptr = &node.name_token, + } + }); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Period, + .ptr = &node.period_token, + } + }); + continue; + }, + State.FieldInitListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; continue; } else { - self.putBackToken(token); - try stack.append(State { .SwitchCaseItem = case_items }); + stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable; continue; - } - }, - State.SwitchCaseItem => |case_items| { - stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; - try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); - }, - State.SwitchCaseItemCommaOrEnd => |case_items| { - if ((try self.expectCommaOrEnd(Token.Id.EqualAngleBracketRight)) == null) { - stack.append(State { .SwitchCaseItem = case_items }) catch unreachable; - } + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.FieldListCommaOrEnd => |container_decl| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + container_decl.rbrace_token = end; + continue; + } else { + try stack.push(State { .ContainerDecl = container_decl }); + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.ErrorTagListItemOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } + + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; continue; - }, + } + const node_ptr = try list_state.list.addOne(); - State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); - } - continue; - }, - State.AsyncAllocator => |async_node| { - if (self.eatToken(Token.Id.AngleBracketLeft) == null) { + try stack.push(State { .ErrorTagListCommaOrEnd = list_state }); + try stack.push(State { .ErrorTag = node_ptr }); + continue; + }, + State.ErrorTagListCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; continue; - } + } else { + stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, + State.SwitchCaseOrEnd => |list_state| { + while (try eatLineComment(arena, &tok_it)) |line_comment| { + try list_state.list.push(&line_comment.base); + } - async_node.rangle_bracket = Token(undefined); - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - } - }); - try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| { + *list_state.ptr = rbrace; continue; - }, - State.AsyncEnd => |ctx| { - const node = ctx.ctx.get() ?? continue; - - switch (node.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); - fn_proto.async_attr = ctx.attribute; - continue; - }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); - if (suffix_op.op == ast.Node.SuffixOp.Op.Call) { - suffix_op.op.Call.async_attr = ctx.attribute; - continue; - } + } - return self.parseError(node.firstToken(), "expected {}, found {}.", - @tagName(ast.Node.SuffixOp.Op.Call), - @tagName(suffix_op.op)); - }, - else => { - return self.parseError(node.firstToken(), "expected {} or {}, found {}.", - @tagName(ast.Node.SuffixOp.Op.Call), - @tagName(ast.Node.Id.FnProto), - @tagName(node.id)); - } - } - }, + const comments = try eatDocComments(arena, &tok_it); + const node = try arena.construct(ast.Node.SwitchCase { + .base = ast.Node { + .id = ast.Node.Id.SwitchCase, + }, + .items = ast.Node.SwitchCase.ItemList.init(arena), + .payload = null, + .expr = undefined, + }); + try list_state.list.push(&node.base); + try stack.push(State { .SwitchCaseCommaOrEnd = list_state }); + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .SwitchCaseFirstItem = &node.items }); + continue; + }, - State.ExternType => |ctx| { - if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = ctx.comments, - .visib_token = null, - .name_token = null, - .fn_token = fn_token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = ctx.extern_token, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - ctx.opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; + State.SwitchCaseCommaOrEnd => |list_state| { + switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) { + ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { + *list_state.ptr = end; continue; - } + } else { + try stack.push(State { .SwitchCaseOrEnd = list_state }); + continue; + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + }, - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = ctx.opt_ctx, - .ltoken = ctx.extern_token, - .layout = ast.Node.ContainerDecl.Layout.Extern, - }, - }) catch unreachable; - continue; - }, - State.SliceOrArrayAccess => |node| { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Ellipsis2 => { - const start = node.op.ArrayAccess; - node.op = ast.Node.SuffixOp.Op { - .Slice = ast.Node.SuffixOp.SliceRange { - .start = start, - .end = null, - } - }; + State.SwitchCaseFirstItem => |case_items| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id == Token.Id.Keyword_else) { + const else_node = try arena.construct(ast.Node.SwitchElse { + .base = ast.Node{ .id = ast.Node.Id.SwitchElse}, + .token = token_index, + }); + try case_items.push(&else_node.base); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RBracket, - .ptr = &node.rtoken, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); - continue; - }, - Token.Id.RBracket => { - node.rtoken = token; - continue; - }, - else => { - return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id)); + try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight }); + continue; + } else { + _ = tok_it.prev(); + try stack.push(State { .SwitchCaseItem = case_items }); + continue; + } + }, + State.SwitchCaseItem => |case_items| { + stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable; + try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } }); + }, + State.SwitchCaseItemCommaOrEnd => |case_items| { + switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) { + ExpectCommaOrEndResult.end_token => |t| { + if (t == null) { + stack.push(State { .SwitchCaseItem = case_items }) catch unreachable; } - } - }, - State.SliceOrArrayType => |node| { - if (self.eatToken(Token.Id.RBracket)) |_| { - node.op = ast.Node.PrefixOp.Op { - .SliceType = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - } - }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.append(State { .AddrOfModifiers = &node.op.SliceType }); continue; - } + }, + ExpectCommaOrEndResult.parse_error => |e| { + try tree.errors.push(e); + return tree; + }, + } + continue; + }, + - node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.RBracket }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + State.SuspendBody => |suspend_node| { + if (suspend_node.payload != null) { + try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } }); + } + continue; + }, + State.AsyncAllocator => |async_node| { + if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) { continue; - }, - State.AddrOfModifiers => |addr_of_info| { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_align => { - stack.append(state) catch unreachable; - if (addr_of_info.align_expr != null) { - return self.parseError(token, "multiple align qualifiers"); - } - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - continue; - }, - Token.Id.Keyword_const => { - stack.append(state) catch unreachable; - if (addr_of_info.const_token != null) { - return self.parseError(token, "duplicate qualifier: const"); - } - addr_of_info.const_token = token; - continue; - }, - Token.Id.Keyword_volatile => { - stack.append(state) catch unreachable; - if (addr_of_info.volatile_token != null) { - return self.parseError(token, "duplicate qualifier: volatile"); - } - addr_of_info.volatile_token = token; - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, + } + async_node.rangle_bracket = TokenIndex(0); + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.AngleBracketRight, + .ptr = &??async_node.rangle_bracket, + } + }); + try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } }); + continue; + }, + State.AsyncEnd => |ctx| { + const node = ctx.ctx.get() ?? continue; - State.Payload => |opt_ctx| { - const token = self.getNextToken(); - if (token.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected {}, found {}.", - @tagName(Token.Id.Pipe), - @tagName(token.id)); + switch (node.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node); + fn_proto.async_attr = ctx.attribute; + continue; + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node); + if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) { + suffix_op.op.Call.async_attr = ctx.attribute; + continue; } - self.putBackToken(token); - continue; + *(try tree.errors.addOne()) = Error { + .ExpectedCall = Error.ExpectedCall { .node = node }, + }; + return tree; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node }, + }; + return tree; } + } + }, - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload, - ast.Node.Payload { - .base = undefined, - .lpipe = token, - .error_symbol = undefined, - .rpipe = undefined - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + State.ExternType => |ctx| { + if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = ctx.comments, + .visib_token = null, + .name_token = null, + .fn_token = fn_token, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = ctx.extern_token, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + ctx.opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; continue; - }, - State.PointerPayload => |opt_ctx| { - const token = self.getNextToken(); - if (token.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected {}, found {}.", - @tagName(Token.Id.Pipe), - @tagName(token.id)); - } + } + + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = ctx.opt_ctx, + .ltoken = ctx.extern_token, + .layout = ast.Node.ContainerDecl.Layout.Extern, + }, + }) catch unreachable; + continue; + }, + State.SliceOrArrayAccess => |node| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Ellipsis2 => { + const start = node.op.ArrayAccess; + node.op = ast.Node.SuffixOp.Op { + .Slice = ast.Node.SuffixOp.Op.Slice { + .start = start, + .end = null, + } + }; - self.putBackToken(token); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RBracket, + .ptr = &node.rtoken, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } }); + continue; + }, + Token.Id.RBracket => { + node.rtoken = token_index; continue; + }, + else => { + *(try tree.errors.addOne()) = Error { + .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index }, + }; + return tree; } - - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, - ast.Node.PointerPayload { - .base = undefined, - .lpipe = token, - .ptr_token = null, - .value_symbol = undefined, - .rpipe = undefined + } + }, + State.SliceOrArrayType => |node| { + if (eatToken(&tok_it, Token.Id.RBracket)) |_| { + node.op = ast.Node.PrefixOp.Op { + .SliceType = ast.Node.PrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, } - ); + }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.push(State { .AddrOfModifiers = &node.op.SliceType }); + continue; + } - stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, + node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined }; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.RBracket }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } }); + continue; + }, + State.AddrOfModifiers => |addr_of_info| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_align => { + stack.push(state) catch unreachable; + if (addr_of_info.align_expr != null) { + *(try tree.errors.addOne()) = Error { + .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index }, + }; + return tree; } - }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + continue; + }, + Token.Id.Keyword_const => { + stack.push(state) catch unreachable; + if (addr_of_info.const_token != null) { + *(try tree.errors.addOne()) = Error { + .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index }, + }; + return tree; } - }); - continue; - }, - State.PointerIndexPayload => |opt_ctx| { - const token = self.getNextToken(); - if (token.id != Token.Id.Pipe) { - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected {}, found {}.", - @tagName(Token.Id.Pipe), - @tagName(token.id)); + addr_of_info.const_token = token_index; + continue; + }, + Token.Id.Keyword_volatile => { + stack.push(state) catch unreachable; + if (addr_of_info.volatile_token != null) { + *(try tree.errors.addOne()) = Error { + .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index }, + }; + return tree; } - - self.putBackToken(token); + addr_of_info.volatile_token = token_index; continue; - } + }, + else => { + _ = tok_it.prev(); + continue; + }, + } + }, - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, - ast.Node.PointerIndexPayload { - .base = undefined, - .lpipe = token, - .ptr_token = null, - .value_symbol = undefined, - .index_symbol = null, - .rpipe = undefined - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Pipe, - .ptr = &node.rpipe, - } - }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); - try stack.append(State { .IfToken = Token.Id.Comma }); - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Asterisk, - .ptr = &node.ptr_token, - } - }); - continue; - }, + State.Payload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; + } + _ = tok_it.prev(); + continue; + } - State.Expression => |opt_ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, - ast.Node.ControlFlowExpression { - .base = undefined, - .ltoken = token, - .kind = undefined, - .rhs = null, - } - ); - - stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; - - switch (token.id) { - Token.Id.Keyword_break => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); - try stack.append(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_continue => { - node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; - try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); - try stack.append(State { .IfToken = Token.Id.Colon }); - }, - Token.Id.Keyword_return => { - node.kind = ast.Node.ControlFlowExpression.Kind.Return; - }, - else => unreachable, - } - continue; - }, - Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = switch (token.id) { - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, - Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, - Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, - else => unreachable, - }, - .rhs = undefined, - } - ); - - stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - continue; - }, - else => { - if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) { - self.putBackToken(token); - stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; - } - continue; - } + const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload, + ast.Node.Payload { + .base = undefined, + .lpipe = token_index, + .error_symbol = undefined, + .rpipe = undefined } - }, - State.RangeExpressionBegin => |opt_ctx| { - stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .Expression = opt_ctx }); - continue; - }, - State.RangeExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + ); - if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ellipsis3, - .op = ast.Node.InfixOp.Op.Range, - .rhs = undefined, - } - ); - stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - continue; + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, } - }, - State.AssignmentExpressionBegin => |opt_ctx| { - stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .Expression = opt_ctx }); - continue; - }, - - State.AssignmentExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token = self.getNextToken(); - if (tokenIdToAssignment(token.id)) |ass_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = ass_id, - .rhs = undefined, - } - ); - stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; + }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } }); + continue; + }, + State.PointerPayload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; } - }, - State.UnwrapExpressionBegin => |opt_ctx| { - stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BoolOrExpressionBegin = opt_ctx }); + _ = tok_it.prev(); continue; - }, - - State.UnwrapExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token = self.getNextToken(); - if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = unwrap_id, - .rhs = undefined, - } - ); + } - stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }); + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload, + ast.Node.PointerPayload { + .base = undefined, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .rpipe = undefined + } + ); - if (node.op == ast.Node.InfixOp.Op.Catch) { - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); - } - continue; - } else { - self.putBackToken(token); - continue; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, + State.PointerIndexPayload => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != Token.Id.Pipe) { + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Pipe, + }, + }; + return tree; } - }, - State.BoolOrExpressionBegin => |opt_ctx| { - stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = opt_ctx }); + _ = tok_it.prev(); continue; - }, - - State.BoolOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + } - if (self.eatToken(Token.Id.Keyword_or)) |or_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = or_token, - .op = ast.Node.InfixOp.Op.BoolOr, - .rhs = undefined, - } - ); - stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload, + ast.Node.PointerIndexPayload { + .base = undefined, + .lpipe = token_index, + .ptr_token = null, + .value_symbol = undefined, + .index_symbol = null, + .rpipe = undefined } - }, + ); - State.BoolAndExpressionBegin => |opt_ctx| { - stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = opt_ctx }); - continue; - }, + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Pipe, + .ptr = &node.rpipe, + } + }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } }); + try stack.push(State { .IfToken = Token.Id.Comma }); + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Asterisk, + .ptr = &node.ptr_token, + } + }); + continue; + }, - State.BoolAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - if (self.eatToken(Token.Id.Keyword_and)) |and_token| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { + State.Expression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression, + ast.Node.ControlFlowExpression { .base = undefined, - .lhs = lhs, - .op_token = and_token, - .op = ast.Node.InfixOp.Op.BoolAnd, - .rhs = undefined, + .ltoken = token_index, + .kind = undefined, + .rhs = null, } ); - stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.ComparisonExpressionBegin => |opt_ctx| { - stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = opt_ctx }); - continue; - }, - State.ComparisonExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable; - const token = self.getNextToken(); - if (tokenIdToComparison(token.id)) |comp_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { + switch (token_ptr.id) { + Token.Id.Keyword_break => { + node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null }; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_continue => { + node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null }; + try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } }); + try stack.push(State { .IfToken = Token.Id.Colon }); + }, + Token.Id.Keyword_return => { + node.kind = ast.Node.ControlFlowExpression.Kind.Return; + }, + else => unreachable, + } + continue; + }, + Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { .base = undefined, - .lhs = lhs, - .op_token = token, - .op = comp_id, + .op_token = token_index, + .op = switch (token_ptr.id) { + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} }, + Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} }, + Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} }, + else => unreachable, + }, .rhs = undefined, } ); - stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + + stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; - } else { - self.putBackToken(token); + }, + else => { + if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { + _ = tok_it.prev(); + stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable; + } continue; } - }, + } + }, + State.RangeExpressionBegin => |opt_ctx| { + stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .Expression = opt_ctx }); + continue; + }, + State.RangeExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.BinaryOrExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ellipsis3, + .op = ast.Node.InfixOp.Op.Range, + .rhs = undefined, + } + ); + stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable; continue; - }, - - State.BinaryOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + } + }, + State.AssignmentExpressionBegin => |opt_ctx| { + stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .Expression = opt_ctx }); + continue; + }, - if (self.eatToken(Token.Id.Pipe)) |pipe| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = pipe, - .op = ast.Node.InfixOp.Op.BitOr, - .rhs = undefined, - } - ); - stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, + State.AssignmentExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.BinaryXorExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = opt_ctx }); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToAssignment(token_ptr.id)) |ass_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = ass_id, + .rhs = undefined, + } + ); + stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); continue; - }, - - State.BinaryXorExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - if (self.eatToken(Token.Id.Caret)) |caret| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = caret, - .op = ast.Node.InfixOp.Op.BitXor, - .rhs = undefined, - } - ); - stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, - - State.BinaryAndExpressionBegin => |opt_ctx| { - stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = opt_ctx }); + } else { + _ = tok_it.prev(); continue; - }, + } + }, - State.BinaryAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.UnwrapExpressionBegin => |opt_ctx| { + stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BoolOrExpressionBegin = opt_ctx }); + continue; + }, - if (self.eatToken(Token.Id.Ampersand)) |ampersand| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = ampersand, - .op = ast.Node.InfixOp.Op.BitAnd, - .rhs = undefined, - } - ); - stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, + State.UnwrapExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.BitShiftExpressionBegin => |opt_ctx| { - stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = opt_ctx }); - continue; - }, + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = unwrap_id, + .rhs = undefined, + } + ); - State.BitShiftExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }); - const token = self.getNextToken(); - if (tokenIdToBitShift(token.id)) |bitshift_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = bitshift_id, - .rhs = undefined, - } - ); - stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; + if (node.op == ast.Node.InfixOp.Op.Catch) { + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } }); } - }, - - State.AdditionExpressionBegin => |opt_ctx| { - stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = opt_ctx }); continue; - }, + } else { + _ = tok_it.prev(); + continue; + } + }, - State.AdditionExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.BoolOrExpressionBegin => |opt_ctx| { + stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BoolAndExpressionBegin = opt_ctx }); + continue; + }, - const token = self.getNextToken(); - if (tokenIdToAddition(token.id)) |add_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = add_id, - .rhs = undefined, - } - ); - stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, + State.BoolOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.MultiplyExpressionBegin => |opt_ctx| { - stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = or_token, + .op = ast.Node.InfixOp.Op.BoolOr, + .rhs = undefined, + } + ); + stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, + } + }, - State.MultiplyExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.BoolAndExpressionBegin => |opt_ctx| { + stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .ComparisonExpressionBegin = opt_ctx }); + continue; + }, - const token = self.getNextToken(); - if (tokenIdToMultiply(token.id)) |mult_id| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = mult_id, - .rhs = undefined, - } - ); - stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); - continue; - } else { - self.putBackToken(token); - continue; - } - }, + State.BoolAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.CurlySuffixExpressionBegin => |opt_ctx| { - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { .TypeExprBegin = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = and_token, + .op = ast.Node.InfixOp.Op.BoolAnd, + .rhs = undefined, + } + ); + stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, + } + }, - State.CurlySuffixExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + State.ComparisonExpressionBegin => |opt_ctx| { + stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryOrExpressionBegin = opt_ctx }); + continue; + }, - if (self.isPeekToken(Token.Id.Period)) { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .StructInitializer = ArrayList(&ast.Node).init(arena), - }, - .rtoken = undefined, - } - ); - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { - .FieldInitListItemOrEnd = ListSave(&ast.Node) { - .list = &node.op.StructInitializer, - .ptr = &node.rtoken, - } - }); - continue; - } + State.ComparisonExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToComparison(token_ptr.id)) |comp_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { .base = undefined, .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayInitializer = ArrayList(&ast.Node).init(arena), - }, - .rtoken = undefined, + .op_token = token_index, + .op = comp_id, + .rhs = undefined, } ); - stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .IfToken = Token.Id.LBrace }); - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.ArrayInitializer, - .end = Token.Id.RBrace, - .ptr = &node.rtoken, - } - }); + stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, - - State.TypeExprBegin => |opt_ctx| { - stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable; - try stack.append(State { .PrefixOpExpression = opt_ctx }); + } else { + _ = tok_it.prev(); continue; - }, - - State.TypeExprEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + } + }, - if (self.eatToken(Token.Id.Bang)) |bang| { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = bang, - .op = ast.Node.InfixOp.Op.ErrorUnion, - .rhs = undefined, - } - ); - stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); - continue; - } - }, + State.BinaryOrExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryXorExpressionBegin = opt_ctx }); + continue; + }, - State.PrefixOpExpression => |opt_ctx| { - const token = self.getNextToken(); - if (tokenIdToPrefixOp(token.id)) |prefix_id| { - var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = prefix_id, - .rhs = undefined, - } - ); + State.BinaryOrExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - // Treat '**' token as two derefs - if (token.id == Token.Id.AsteriskAsterisk) { - const child = try self.createNode(arena, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = prefix_id, - .rhs = undefined, - } - ); - node.rhs = &child.base; - node = child; + if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = pipe, + .op = ast.Node.InfixOp.Op.BitOr, + .rhs = undefined, } + ); + stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, - stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; - if (node.op == ast.Node.PrefixOp.Op.AddrOf) { - try stack.append(State { .AddrOfModifiers = &node.op.AddrOf }); - } - continue; - } else { - self.putBackToken(token); - stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; - continue; - } - }, + State.BinaryXorExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BinaryAndExpressionBegin = opt_ctx }); + continue; + }, - State.SuffixOpExpressionBegin => |opt_ctx| { - if (self.eatToken(Token.Id.Keyword_async)) |async_token| { - const async_node = try self.createNode(arena, ast.Node.AsyncAttribute, - ast.Node.AsyncAttribute { - .base = undefined, - .async_token = async_token, - .allocator_type = null, - .rangle_bracket = null, - } - ); - stack.append(State { - .AsyncEnd = AsyncEndCtx { - .ctx = opt_ctx, - .attribute = async_node, - } - }) catch unreachable; - try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); - try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() }); - try stack.append(State { .AsyncAllocator = async_node }); - continue; - } + State.BinaryXorExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; - try stack.append(State { .PrimaryExpression = opt_ctx }); + if (eatToken(&tok_it, Token.Id.Caret)) |caret| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = caret, + .op = ast.Node.InfixOp.Op.BitXor, + .rhs = undefined, + } + ); + stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, + } + }, - State.SuffixOpExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; - - const token = self.getNextToken(); - switch (token.id) { - Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .Call = ast.Node.SuffixOp.CallInfo { - .params = ArrayList(&ast.Node).init(arena), - .async_attr = null, - } - }, - .rtoken = undefined, - } - ); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.op.Call.params, - .end = Token.Id.RParen, - .ptr = &node.rtoken, - } - }); - continue; - }, - Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, - ast.Node.SuffixOp { - .base = undefined, - .lhs = lhs, - .op = ast.Node.SuffixOp.Op { - .ArrayAccess = undefined, - }, - .rtoken = undefined - } - ); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .SliceOrArrayAccess = node }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); - continue; - }, - Token.Id.Period => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, - ast.Node.InfixOp { - .base = undefined, - .lhs = lhs, - .op_token = token, - .op = ast.Node.InfixOp.Op.Period, - .rhs = undefined, - } - ); - stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; - try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); - continue; - }, - else => { - self.putBackToken(token); - continue; - }, - } - }, + State.BinaryAndExpressionBegin => |opt_ctx| { + stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .BitShiftExpressionBegin = opt_ctx }); + continue; + }, - State.PrimaryExpression => |opt_ctx| { - const token = self.getNextToken(); - switch (token.id) { - Token.Id.IntegerLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token); - continue; - }, - Token.Id.FloatLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token); - continue; - }, - Token.Id.CharLiteral => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token); - continue; - }, - Token.Id.Keyword_undefined => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token); - continue; - }, - Token.Id.Keyword_true, Token.Id.Keyword_false => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token); - continue; - }, - Token.Id.Keyword_null => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token); - continue; - }, - Token.Id.Keyword_this => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token); - continue; - }, - Token.Id.Keyword_var => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token); - continue; - }, - Token.Id.Keyword_unreachable => { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token); - continue; - }, - Token.Id.Keyword_promise => { - const node = try arena.construct(ast.Node.PromiseType { - .base = ast.Node { - .id = ast.Node.Id.PromiseType, - .same_line_comment = null, - }, - .promise_token = token, - .result = null, - }); - opt_ctx.store(&node.base); - const next_token = self.getNextToken(); - if (next_token.id != Token.Id.Arrow) { - self.putBackToken(next_token); - continue; - } - node.result = ast.Node.PromiseType.Result { - .arrow_token = next_token, - .return_type = undefined, - }; - const return_type_ptr = &((??node.result).return_type); - try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); - continue; - }, - Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { - opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable); - continue; - }, - Token.Id.LParen => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, - ast.Node.GroupedExpression { - .base = undefined, - .lparen = token, - .expr = undefined, - .rparen = undefined, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - continue; - }, - Token.Id.Builtin => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, - ast.Node.BuiltinCall { - .base = undefined, - .builtin_token = token, - .params = ArrayList(&ast.Node).init(arena), - .rparen_token = undefined, - } - ); - stack.append(State { - .ExprListItemOrEnd = ExprListCtx { - .list = &node.params, - .end = Token.Id.RParen, - .ptr = &node.rparen_token, - } - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LParen, }); - continue; - }, - Token.Id.LBracket => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, - ast.Node.PrefixOp { - .base = undefined, - .op_token = token, - .op = undefined, - .rhs = undefined, - } - ); - stack.append(State { .SliceOrArrayType = node }) catch unreachable; - continue; - }, - Token.Id.Keyword_error => { - stack.append(State { - .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { - .error_token = token, - .opt_ctx = opt_ctx - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_packed => { - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token, - .layout = ast.Node.ContainerDecl.Layout.Packed, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_extern => { - stack.append(State { - .ExternType = ExternTypeCtx { - .opt_ctx = opt_ctx, - .extern_token = token, - .comments = null, - }, - }) catch unreachable; - continue; - }, - Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { - self.putBackToken(token); - stack.append(State { - .ContainerKind = ContainerKindCtx { - .opt_ctx = opt_ctx, - .ltoken = token, - .layout = ast.Node.ContainerDecl.Layout.Auto, - }, - }) catch unreachable; - continue; - }, - Token.Id.Identifier => { - stack.append(State { - .MaybeLabeledExpression = MaybeLabeledExpressionCtx { - .label = token, - .opt_ctx = opt_ctx - } - }) catch unreachable; - continue; - }, - Token.Id.Keyword_fn => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = null, - .visib_token = null, - .name_token = null, - .fn_token = token, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = null, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - continue; - }, - Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.construct(ast.Node.FnProto { - .base = ast.Node { - .id = ast.Node.Id.FnProto, - .same_line_comment = null, - }, - .doc_comments = null, - .visib_token = null, - .name_token = null, - .fn_token = undefined, - .params = ArrayList(&ast.Node).init(arena), - .return_type = undefined, - .var_args_token = null, - .extern_export_inline_token = null, - .cc_token = token, - .async_attr = null, - .body_node = null, - .lib_name = null, - .align_expr = null, - }); - opt_ctx.store(&fn_proto.base); - stack.append(State { .FnProto = fn_proto }) catch unreachable; - try stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.Keyword_fn, - .ptr = &fn_proto.fn_token - } - }); - continue; - }, - Token.Id.Keyword_asm => { - const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm, - ast.Node.Asm { - .base = undefined, - .asm_token = token, - .volatile_token = null, - .template = undefined, - //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena), - .outputs = ArrayList(&ast.Node.AsmOutput).init(arena), - .inputs = ArrayList(&ast.Node.AsmInput).init(arena), - .cloppers = ArrayList(&ast.Node).init(arena), - .rparen = undefined, - } - ); - stack.append(State { - .ExpectTokenSave = ExpectTokenSave { - .id = Token.Id.RParen, - .ptr = &node.rparen, - } - }) catch unreachable; - try stack.append(State { .AsmClopperItems = &node.cloppers }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmInputItems = &node.inputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .AsmOutputItems = &node.outputs }); - try stack.append(State { .IfToken = Token.Id.Colon }); - try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - try stack.append(State { - .OptionalTokenSave = OptionalTokenSave { - .id = Token.Id.Keyword_volatile, - .ptr = &node.volatile_token, - } - }); - }, - Token.Id.Keyword_inline => { - stack.append(State { - .Inline = InlineCtx { - .label = null, - .inline_token = token, - .opt_ctx = opt_ctx, - } - }) catch unreachable; - continue; - }, - else => { - if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) { - self.putBackToken(token); - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)); - } - } - continue; - } - } - }, + State.BinaryAndExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = ampersand, + .op = ast.Node.InfixOp.Op.BitAnd, + .rhs = undefined, + } + ); + stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } + }, - State.ErrorTypeOrSetDecl => |ctx| { - if (self.eatToken(Token.Id.LBrace) == null) { - _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); - continue; - } + State.BitShiftExpressionBegin => |opt_ctx| { + stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .AdditionExpressionBegin = opt_ctx }); + continue; + }, - const node = try arena.construct(ast.Node.ErrorSetDecl { - .base = ast.Node { - .id = ast.Node.Id.ErrorSetDecl, - .same_line_comment = null, - }, - .error_token = ctx.error_token, - .decls = ArrayList(&ast.Node).init(arena), - .rbrace_token = undefined, - }); - ctx.opt_ctx.store(&node.base); + State.BitShiftExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - stack.append(State { - .ErrorTagListItemOrEnd = ListSave(&ast.Node) { - .list = &node.decls, - .ptr = &node.rbrace_token, + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = bitshift_id, + .rhs = undefined, } - }) catch unreachable; + ); + stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } }); continue; - }, - State.StringLiteral => |opt_ctx| { - const token = self.getNextToken(); - opt_ctx.store( - (try self.parseStringLiteral(arena, token)) ?? { - self.putBackToken(token); - if (opt_ctx != OptionalCtx.Optional) { - return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)); - } + } else { + _ = tok_it.prev(); + continue; + } + }, - continue; + State.AdditionExpressionBegin => |opt_ctx| { + stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .MultiplyExpressionBegin = opt_ctx }); + continue; + }, + + State.AdditionExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToAddition(token_ptr.id)) |add_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = add_id, + .rhs = undefined, } ); - }, + stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, - State.Identifier => |opt_ctx| { - if (self.eatToken(Token.Id.Identifier)) |ident_token| { - _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); - continue; - } + State.MultiplyExpressionBegin => |opt_ctx| { + stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx }); + continue; + }, - if (opt_ctx != OptionalCtx.Optional) { - const token = self.getNextToken(); - return self.parseError(token, "expected identifier, found {}", @tagName(token.id)); - } - }, + State.MultiplyExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - State.ErrorTag => |node_ptr| { - const comments = try self.eatDocComments(arena); - const ident_token = self.getNextToken(); - if (ident_token.id != Token.Id.Identifier) { - return self.parseError(ident_token, "expected {}, found {}", - @tagName(Token.Id.Identifier), @tagName(ident_token.id)); - } + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToMultiply(token_ptr.id)) |mult_id| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = mult_id, + .rhs = undefined, + } + ); + stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } }); + continue; + } else { + _ = tok_it.prev(); + continue; + } + }, - const node = try arena.construct(ast.Node.ErrorTag { - .base = ast.Node { - .id = ast.Node.Id.ErrorTag, - .same_line_comment = null, + State.CurlySuffixExpressionBegin => |opt_ctx| { + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { .TypeExprBegin = opt_ctx }); + continue; + }, + + State.CurlySuffixExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; + + if ((??tok_it.peek()).id == Token.Id.Period) { + const node = try arena.construct(ast.Node.SuffixOp { + .base = ast.Node { .id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), }, - .doc_comments = comments, - .name_token = ident_token, + .rtoken = undefined, }); - *node_ptr = &node.base; - continue; - }, + opt_ctx.store(&node.base); - State.ExpectToken => |token_id| { - _ = try self.expectToken(token_id); - continue; - }, - State.ExpectTokenSave => |expect_token_save| { - *expect_token_save.ptr = try self.expectToken(expect_token_save.id); + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { + .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) { + .list = &node.op.StructInitializer, + .ptr = &node.rtoken, + } + }); continue; - }, - State.IfToken => |token_id| { - if (self.eatToken(token_id)) |_| { - continue; - } + } - _ = stack.pop(); - continue; - }, - State.IfTokenSave => |if_token_save| { - if (self.eatToken(if_token_save.id)) |token| { - *if_token_save.ptr = token; - continue; + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena), + }, + .rtoken = undefined, } - - _ = stack.pop(); - continue; - }, - State.OptionalTokenSave => |optional_token_save| { - if (self.eatToken(optional_token_save.id)) |token| { - *optional_token_save.ptr = token; - continue; + ); + stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .IfToken = Token.Id.LBrace }); + try stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.ArrayInitializer, + .end = Token.Id.RBrace, + .ptr = &node.rtoken, } + }); + continue; + }, - continue; - }, - } - } - } - - fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment { - var result: ?&ast.Node.DocComment = null; - while (true) { - if (self.eatToken(Token.Id.DocComment)) |line_comment| { - const node = blk: { - if (result) |comment_node| { - break :blk comment_node; - } else { - const comment_node = try arena.construct(ast.Node.DocComment { - .base = ast.Node { - .id = ast.Node.Id.DocComment, - .same_line_comment = null, - }, - .lines = ArrayList(Token).init(arena), - }); - result = comment_node; - break :blk comment_node; - } - }; - try node.lines.append(line_comment); + State.TypeExprBegin => |opt_ctx| { + stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable; + try stack.push(State { .PrefixOpExpression = opt_ctx }); continue; - } - break; - } - return result; - } + }, + + State.TypeExprEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { - const token = self.eatToken(Token.Id.LineComment) ?? return null; - return try arena.construct(ast.Node.LineComment { - .base = ast.Node { - .id = ast.Node.Id.LineComment, - .same_line_comment = null, + if (eatToken(&tok_it, Token.Id.Bang)) |bang| { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = bang, + .op = ast.Node.InfixOp.Op.ErrorUnion, + .rhs = undefined, + } + ); + stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } }); + continue; + } }, - .token = token, - }); - } - fn requireSemiColon(node: &const ast.Node) bool { - var n = node; - while (true) { - switch (n.id) { - ast.Node.Id.Root, - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ParamDecl, - ast.Node.Id.Block, - ast.Node.Id.Payload, - ast.Node.Id.PointerPayload, - ast.Node.Id.PointerIndexPayload, - ast.Node.Id.Switch, - ast.Node.Id.SwitchCase, - ast.Node.Id.SwitchElse, - ast.Node.Id.FieldInitializer, - ast.Node.Id.DocComment, - ast.Node.Id.LineComment, - ast.Node.Id.TestDecl => return false, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", n); - if (while_node.@"else") |@"else"| { - n = @"else".base; - continue; - } + State.PrefixOpExpression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { + var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + } + ); - return while_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", n); - if (for_node.@"else") |@"else"| { - n = @"else".base; - continue; + // Treat '**' token as two derefs + if (token_ptr.id == Token.Id.AsteriskAsterisk) { + const child = try createNode(arena, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = prefix_id, + .rhs = undefined, + } + ); + node.rhs = &child.base; + node = child; } - return for_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", n); - if (if_node.@"else") |@"else"| { - n = @"else".base; - continue; + stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable; + if (node.op == ast.Node.PrefixOp.Op.AddrOf) { + try stack.push(State { .AddrOfModifiers = &node.op.AddrOf }); } + continue; + } else { + _ = tok_it.prev(); + stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable; + continue; + } + }, - return if_node.body.id != ast.Node.Id.Block; - }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", n); - n = else_node.body; + State.SuffixOpExpressionBegin => |opt_ctx| { + if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| { + const async_node = try createNode(arena, ast.Node.AsyncAttribute, + ast.Node.AsyncAttribute { + .base = undefined, + .async_token = async_token, + .allocator_type = null, + .rangle_bracket = null, + } + ); + stack.push(State { + .AsyncEnd = AsyncEndCtx { + .ctx = opt_ctx, + .attribute = async_node, + } + }) catch unreachable; + try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }); + try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() }); + try stack.push(State { .AsyncAllocator = async_node }); continue; - }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); - return defer_node.expr.id != ast.Node.Id.Block; - }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); - return comptime_node.expr.id != ast.Node.Id.Block; - }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); - if (suspend_node.body) |body| { - return body.id != ast.Node.Id.Block; - } + } - return true; - }, - else => return true, - } - } - } + stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable; + try stack.push(State { .PrimaryExpression = opt_ctx }); + continue; + }, + + State.SuffixOpExpressionEnd => |opt_ctx| { + const lhs = opt_ctx.get() ?? continue; - fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void { - const node_last_token = node.lastToken(); + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.LParen => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .Call = ast.Node.SuffixOp.Op.Call { + .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena), + .async_attr = null, + } + }, + .rtoken = undefined, + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.op.Call.params, + .end = Token.Id.RParen, + .ptr = &node.rtoken, + } + }); + continue; + }, + Token.Id.LBracket => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp, + ast.Node.SuffixOp { + .base = undefined, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op { + .ArrayAccess = undefined, + }, + .rtoken = undefined + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .SliceOrArrayAccess = node }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }}); + continue; + }, + Token.Id.Period => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp, + ast.Node.InfixOp { + .base = undefined, + .lhs = lhs, + .op_token = token_index, + .op = ast.Node.InfixOp.Op.Period, + .rhs = undefined, + } + ); + stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } }); + continue; + }, + else => { + _ = tok_it.prev(); + continue; + }, + } + }, + + State.PrimaryExpression => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.IntegerLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index); + continue; + }, + Token.Id.FloatLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index); + continue; + }, + Token.Id.CharLiteral => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index); + continue; + }, + Token.Id.Keyword_undefined => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index); + continue; + }, + Token.Id.Keyword_true, Token.Id.Keyword_false => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index); + continue; + }, + Token.Id.Keyword_null => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index); + continue; + }, + Token.Id.Keyword_this => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index); + continue; + }, + Token.Id.Keyword_var => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index); + continue; + }, + Token.Id.Keyword_unreachable => { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index); + continue; + }, + Token.Id.Keyword_promise => { + const node = try arena.construct(ast.Node.PromiseType { + .base = ast.Node { + .id = ast.Node.Id.PromiseType, + }, + .promise_token = token_index, + .result = null, + }); + opt_ctx.store(&node.base); + const next_token_index = tok_it.index; + const next_token_ptr = ??tok_it.next(); + if (next_token_ptr.id != Token.Id.Arrow) { + _ = tok_it.prev(); + continue; + } + node.result = ast.Node.PromiseType.Result { + .arrow_token = next_token_index, + .return_type = undefined, + }; + const return_type_ptr = &((??node.result).return_type); + try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); + continue; + }, + Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable); + continue; + }, + Token.Id.LParen => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression, + ast.Node.GroupedExpression { + .base = undefined, + .lparen = token_index, + .expr = undefined, + .rparen = undefined, + } + ); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + continue; + }, + Token.Id.Builtin => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall, + ast.Node.BuiltinCall { + .base = undefined, + .builtin_token = token_index, + .params = ast.Node.BuiltinCall.ParamList.init(arena), + .rparen_token = undefined, + } + ); + stack.push(State { + .ExprListItemOrEnd = ExprListCtx { + .list = &node.params, + .end = Token.Id.RParen, + .ptr = &node.rparen_token, + } + }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LParen, }); + continue; + }, + Token.Id.LBracket => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp, + ast.Node.PrefixOp { + .base = undefined, + .op_token = token_index, + .op = undefined, + .rhs = undefined, + } + ); + stack.push(State { .SliceOrArrayType = node }) catch unreachable; + continue; + }, + Token.Id.Keyword_error => { + stack.push(State { + .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx { + .error_token = token_index, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_packed => { + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token_index, + .layout = ast.Node.ContainerDecl.Layout.Packed, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_extern => { + stack.push(State { + .ExternType = ExternTypeCtx { + .opt_ctx = opt_ctx, + .extern_token = token_index, + .comments = null, + }, + }) catch unreachable; + continue; + }, + Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => { + _ = tok_it.prev(); + stack.push(State { + .ContainerKind = ContainerKindCtx { + .opt_ctx = opt_ctx, + .ltoken = token_index, + .layout = ast.Node.ContainerDecl.Layout.Auto, + }, + }) catch unreachable; + continue; + }, + Token.Id.Identifier => { + stack.push(State { + .MaybeLabeledExpression = MaybeLabeledExpressionCtx { + .label = token_index, + .opt_ctx = opt_ctx + } + }) catch unreachable; + continue; + }, + Token.Id.Keyword_fn => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = null, + .visib_token = null, + .name_token = null, + .fn_token = token_index, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = null, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + continue; + }, + Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { + const fn_proto = try arena.construct(ast.Node.FnProto { + .base = ast.Node { + .id = ast.Node.Id.FnProto, + }, + .doc_comments = null, + .visib_token = null, + .name_token = null, + .fn_token = undefined, + .params = ast.Node.FnProto.ParamList.init(arena), + .return_type = undefined, + .var_args_token = null, + .extern_export_inline_token = null, + .cc_token = token_index, + .async_attr = null, + .body_node = null, + .lib_name = null, + .align_expr = null, + }); + opt_ctx.store(&fn_proto.base); + stack.push(State { .FnProto = fn_proto }) catch unreachable; + try stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.Keyword_fn, + .ptr = &fn_proto.fn_token + } + }); + continue; + }, + Token.Id.Keyword_asm => { + const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm, + ast.Node.Asm { + .base = undefined, + .asm_token = token_index, + .volatile_token = null, + .template = undefined, + .outputs = ast.Node.Asm.OutputList.init(arena), + .inputs = ast.Node.Asm.InputList.init(arena), + .clobbers = ast.Node.Asm.ClobberList.init(arena), + .rparen = undefined, + } + ); + stack.push(State { + .ExpectTokenSave = ExpectTokenSave { + .id = Token.Id.RParen, + .ptr = &node.rparen, + } + }) catch unreachable; + try stack.push(State { .AsmClobberItems = &node.clobbers }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .AsmInputItems = &node.inputs }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .AsmOutputItems = &node.outputs }); + try stack.push(State { .IfToken = Token.Id.Colon }); + try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + try stack.push(State { + .OptionalTokenSave = OptionalTokenSave { + .id = Token.Id.Keyword_volatile, + .ptr = &node.volatile_token, + } + }); + }, + Token.Id.Keyword_inline => { + stack.push(State { + .Inline = InlineCtx { + .label = null, + .inline_token = token_index, + .opt_ctx = opt_ctx, + } + }) catch unreachable; + continue; + }, + else => { + if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) { + _ = tok_it.prev(); + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + }; + return tree; + } + } + continue; + } + } + }, - const line_comment_token = self.getNextToken(); - if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) { - self.putBackToken(line_comment_token); - return; - } - const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token); - const different_line = offset_loc.line != 0; - if (different_line) { - self.putBackToken(line_comment_token); - return; - } + State.ErrorTypeOrSetDecl => |ctx| { + if (eatToken(&tok_it, Token.Id.LBrace) == null) { + _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token); + continue; + } - node.same_line_comment = try arena.construct(line_comment_token); - } + const node = try arena.construct(ast.Node.ErrorSetDecl { + .base = ast.Node { + .id = ast.Node.Id.ErrorSetDecl, + }, + .error_token = ctx.error_token, + .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), + .rbrace_token = undefined, + }); + ctx.opt_ctx.store(&node.base); - fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node { - switch (token.id) { - Token.Id.StringLiteral => { - return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base; + stack.push(State { + .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) { + .list = &node.decls, + .ptr = &node.rbrace_token, + } + }) catch unreachable; + continue; }, - Token.Id.MultilineStringLiteralLine => { - const node = try self.createNode(arena, ast.Node.MultilineStringLiteral, - ast.Node.MultilineStringLiteral { - .base = undefined, - .tokens = ArrayList(Token).init(arena), + State.StringLiteral => |opt_ctx| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + opt_ctx.store( + (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? { + _ = tok_it.prev(); + if (opt_ctx != OptionalCtx.Optional) { + *(try tree.errors.addOne()) = Error { + .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index }, + }; + return tree; + } + + continue; } ); - try node.tokens.append(token); - while (true) { - const multiline_str = self.getNextToken(); - if (multiline_str.id != Token.Id.MultilineStringLiteralLine) { - self.putBackToken(multiline_str); - break; - } + }, - try node.tokens.append(multiline_str); + State.Identifier => |opt_ctx| { + if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| { + _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); + continue; } - return &node.base; + if (opt_ctx != OptionalCtx.Optional) { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = Token.Id.Identifier, + }, + }; + return tree; + } }, - // TODO: We shouldn't need a cast, but: - // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. - else => return (?&ast.Node)(null), - } - } - fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool { - switch (token.id) { - Token.Id.Keyword_suspend => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend, - ast.Node.Suspend { - .base = undefined, - .label = null, - .suspend_token = *token, - .payload = null, - .body = null, - } - ); - - stack.append(State { .SuspendBody = node }) catch unreachable; - try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); - return true; - }, - Token.Id.Keyword_if => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.If, - ast.Node.If { - .base = undefined, - .if_token = *token, - .condition = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - } - ); + State.ErrorTag => |node_ptr| { + const comments = try eatDocComments(arena, &tok_it); + const ident_token_index = tok_it.index; + const ident_token_ptr = ??tok_it.next(); + if (ident_token_ptr.id != Token.Id.Identifier) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = ident_token_index, + .expected_id = Token.Id.Identifier, + }, + }; + return tree; + } - stack.append(State { .Else = &node.@"else" }) catch unreachable; - try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }); - try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); - try stack.append(State { .LookForSameLineComment = &node.condition }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - return true; - }, - Token.Id.Keyword_while => { - stack.append(State { - .While = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = *token, - .opt_ctx = *ctx, - } - }) catch unreachable; - return true; - }, - Token.Id.Keyword_for => { - stack.append(State { - .For = LoopCtx { - .label = null, - .inline_token = null, - .loop_token = *token, - .opt_ctx = *ctx, - } - }) catch unreachable; - return true; - }, - Token.Id.Keyword_switch => { - const node = try arena.construct(ast.Node.Switch { + const node = try arena.construct(ast.Node.ErrorTag { .base = ast.Node { - .id = ast.Node.Id.Switch, - .same_line_comment = null, + .id = ast.Node.Id.ErrorTag, }, - .switch_token = *token, - .expr = undefined, - .cases = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, + .doc_comments = comments, + .name_token = ident_token_index, }); - ctx.store(&node.base); + *node_ptr = &node.base; + continue; + }, - stack.append(State { - .SwitchCaseOrEnd = ListSave(&ast.Node) { - .list = &node.cases, - .ptr = &node.rbrace, - }, - }) catch unreachable; - try stack.append(State { .ExpectToken = Token.Id.LBrace }); - try stack.append(State { .ExpectToken = Token.Id.RParen }); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - try stack.append(State { .ExpectToken = Token.Id.LParen }); - return true; + State.ExpectToken => |token_id| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != token_id) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = token_id, + }, + }; + return tree; + } + continue; }, - Token.Id.Keyword_comptime => { - const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime, - ast.Node.Comptime { - .base = undefined, - .comptime_token = *token, - .expr = undefined, - .doc_comments = null, - } - ); - try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); - return true; + State.ExpectTokenSave => |expect_token_save| { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id != expect_token_save.id) { + *(try tree.errors.addOne()) = Error { + .ExpectedToken = Error.ExpectedToken { + .token = token_index, + .expected_id = expect_token_save.id, + }, + }; + return tree; + } + *expect_token_save.ptr = token_index; + continue; }, - Token.Id.LBrace => { - const block = try self.createToCtxNode(arena, ctx, ast.Node.Block, - ast.Node.Block { - .base = undefined, - .label = null, - .lbrace = *token, - .statements = ArrayList(&ast.Node).init(arena), - .rbrace = undefined, - } - ); - stack.append(State { .Block = block }) catch unreachable; - return true; + State.IfToken => |token_id| { + if (eatToken(&tok_it, token_id)) |_| { + continue; + } + + _ = stack.pop(); + continue; }, - else => { - return false; - } - } - } + State.IfTokenSave => |if_token_save| { + if (eatToken(&tok_it, if_token_save.id)) |token_index| { + *if_token_save.ptr = token_index; + continue; + } - fn expectCommaOrEnd(self: &Parser, end: @TagType(Token.Id)) !?Token { - var token = self.getNextToken(); - switch (token.id) { - Token.Id.Comma => return null, - else => { - if (end == token.id) { - return token; + _ = stack.pop(); + continue; + }, + State.OptionalTokenSave => |optional_token_save| { + if (eatToken(&tok_it, optional_token_save.id)) |token_index| { + *optional_token_save.ptr = token_index; + continue; } - return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id)); + continue; }, } } +} - fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { - // TODO: We have to cast all cases because of this: - // error: expected type '?InfixOp', found '?@TagType(InfixOp)' - return switch (*id) { - Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} }, - Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} }, - Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} }, - Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} }, - Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} }, - Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} }, - Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} }, - Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} }, - Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} }, - Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} }, - Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} }, - Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} }, - Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} }, - else => null, - }; - } +const AnnotatedToken = struct { + ptr: &Token, + index: TokenIndex, +}; - fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, - else => null, - }; - } +const TopLevelDeclCtx = struct { + decls: &ast.Node.Root.DeclList, + visib_token: ?TokenIndex, + extern_export_inline_token: ?AnnotatedToken, + lib_name: ?&ast.Node, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, - Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, - Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, - Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, - Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, - Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, - else => null, - }; - } +const VarDeclCtx = struct { + mut_token: TokenIndex, + visib_token: ?TokenIndex, + comptime_token: ?TokenIndex, + extern_export_token: ?TokenIndex, + lib_name: ?&ast.Node, + list: &ast.Node.Root.DeclList, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, - Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, - else => null, - }; - } +const TopLevelExternOrFieldCtx = struct { + visib_token: TokenIndex, + container_decl: &ast.Node.ContainerDecl, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, - Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, - Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, - Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, - Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, - else => null, - }; - } +const ExternTypeCtx = struct { + opt_ctx: OptionalCtx, + extern_token: TokenIndex, + comments: ?&ast.Node.DocComment, +}; - fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { - return switch (id) { - Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, - Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, - Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, - Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, - Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, - Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, - else => null, - }; - } +const ContainerKindCtx = struct { + opt_ctx: OptionalCtx, + ltoken: TokenIndex, + layout: ast.Node.ContainerDecl.Layout, +}; - fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { - return switch (id) { - Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, - Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, - Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, - Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, - Token.Id.Ampersand => ast.Node.PrefixOp.Op { - .AddrOf = ast.Node.PrefixOp.AddrOfInfo { - .align_expr = null, - .bit_offset_start_token = null, - .bit_offset_end_token = null, - .const_token = null, - .volatile_token = null, - }, - }, - Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, - Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, - Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, - else => null, - }; - } +const ExpectTokenSave = struct { + id: @TagType(Token.Id), + ptr: &TokenIndex, +}; - fn createNode(self: &Parser, arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { - const node = try arena.create(T); - *node = *init_to; - node.base = blk: { - const id = ast.Node.typeToId(T); - break :blk ast.Node { - .id = id, - .same_line_comment = null, - }; - }; +const OptionalTokenSave = struct { + id: @TagType(Token.Id), + ptr: &?TokenIndex, +}; - return node; - } +const ExprListCtx = struct { + list: &ast.Node.SuffixOp.Op.InitList, + end: Token.Id, + ptr: &TokenIndex, +}; - fn createAttachNode(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), comptime T: type, init_to: &const T) !&T { - const node = try self.createNode(arena, T, init_to); - try list.append(&node.base); +fn ListSave(comptime List: type) type { + return struct { + list: &List, + ptr: &TokenIndex, + }; +} - return node; - } +const MaybeLabeledExpressionCtx = struct { + label: TokenIndex, + opt_ctx: OptionalCtx, +}; - fn createToCtxNode(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { - const node = try self.createNode(arena, T, init_to); - opt_ctx.store(&node.base); +const LabelCtx = struct { + label: ?TokenIndex, + opt_ctx: OptionalCtx, +}; - return node; - } +const InlineCtx = struct { + label: ?TokenIndex, + inline_token: ?TokenIndex, + opt_ctx: OptionalCtx, +}; - fn createLiteral(self: &Parser, arena: &mem.Allocator, comptime T: type, token: &const Token) !&T { - return self.createNode(arena, T, - T { - .base = undefined, - .token = *token, - } - ); - } +const LoopCtx = struct { + label: ?TokenIndex, + inline_token: ?TokenIndex, + loop_token: TokenIndex, + opt_ctx: OptionalCtx, +}; + +const AsyncEndCtx = struct { + ctx: OptionalCtx, + attribute: &ast.Node.AsyncAttribute, +}; - fn createToCtxLiteral(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token: &const Token) !&T { - const node = try self.createLiteral(arena, T, token); - opt_ctx.store(&node.base); +const ErrorTypeOrSetDeclCtx = struct { + opt_ctx: OptionalCtx, + error_token: TokenIndex, +}; + +const ParamDeclEndCtx = struct { + fn_proto: &ast.Node.FnProto, + param_decl: &ast.Node.ParamDecl, +}; + +const ComptimeStatementCtx = struct { + comptime_token: TokenIndex, + block: &ast.Node.Block, +}; + +const OptionalCtx = union(enum) { + Optional: &?&ast.Node, + RequiredNull: &?&ast.Node, + Required: &&ast.Node, - return node; + pub fn store(self: &const OptionalCtx, value: &ast.Node) void { + switch (*self) { + OptionalCtx.Optional => |ptr| *ptr = value, + OptionalCtx.RequiredNull => |ptr| *ptr = value, + OptionalCtx.Required => |ptr| *ptr = value, + } } - fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) { - const loc = self.tokenizer.getTokenLocation(0, token); - warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args); - warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]); - { - var i: usize = 0; - while (i < loc.column) : (i += 1) { - warn(" "); - } + pub fn get(self: &const OptionalCtx) ?&ast.Node { + switch (*self) { + OptionalCtx.Optional => |ptr| return *ptr, + OptionalCtx.RequiredNull => |ptr| return ??*ptr, + OptionalCtx.Required => |ptr| return *ptr, } - { - const caret_count = token.end - token.start; - var i: usize = 0; - while (i < caret_count) : (i += 1) { - warn("~"); - } + } + + pub fn toRequired(self: &const OptionalCtx) OptionalCtx { + switch (*self) { + OptionalCtx.Optional => |ptr| { + return OptionalCtx { .RequiredNull = ptr }; + }, + OptionalCtx.RequiredNull => |ptr| return *self, + OptionalCtx.Required => |ptr| return *self, } - warn("\n"); - return error.ParseError; } +}; + +const AddCommentsCtx = struct { + node_ptr: &&ast.Node, + comments: ?&ast.Node.DocComment, +}; + +const State = union(enum) { + TopLevel, + TopLevelExtern: TopLevelDeclCtx, + TopLevelLibname: TopLevelDeclCtx, + TopLevelDecl: TopLevelDeclCtx, + TopLevelExternOrField: TopLevelExternOrFieldCtx, + + ContainerKind: ContainerKindCtx, + ContainerInitArgStart: &ast.Node.ContainerDecl, + ContainerInitArg: &ast.Node.ContainerDecl, + ContainerDecl: &ast.Node.ContainerDecl, + + VarDecl: VarDeclCtx, + VarDeclAlign: &ast.Node.VarDecl, + VarDeclEq: &ast.Node.VarDecl, + + FnDef: &ast.Node.FnProto, + FnProto: &ast.Node.FnProto, + FnProtoAlign: &ast.Node.FnProto, + FnProtoReturnType: &ast.Node.FnProto, + + ParamDecl: &ast.Node.FnProto, + ParamDeclAliasOrComptime: &ast.Node.ParamDecl, + ParamDeclName: &ast.Node.ParamDecl, + ParamDeclEnd: ParamDeclEndCtx, + ParamDeclComma: &ast.Node.FnProto, + + MaybeLabeledExpression: MaybeLabeledExpressionCtx, + LabeledExpression: LabelCtx, + Inline: InlineCtx, + While: LoopCtx, + WhileContinueExpr: &?&ast.Node, + For: LoopCtx, + Else: &?&ast.Node.Else, + + Block: &ast.Node.Block, + Statement: &ast.Node.Block, + ComptimeStatement: ComptimeStatementCtx, + Semicolon: &&ast.Node, + + AsmOutputItems: &ast.Node.Asm.OutputList, + AsmOutputReturnOrType: &ast.Node.AsmOutput, + AsmInputItems: &ast.Node.Asm.InputList, + AsmClobberItems: &ast.Node.Asm.ClobberList, + + ExprListItemOrEnd: ExprListCtx, + ExprListCommaOrEnd: ExprListCtx, + FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), + FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), + FieldListCommaOrEnd: &ast.Node.ContainerDecl, + FieldInitValue: OptionalCtx, + ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), + ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), + SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList), + SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList), + SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList, + SwitchCaseItem: &ast.Node.SwitchCase.ItemList, + SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList, + + SuspendBody: &ast.Node.Suspend, + AsyncAllocator: &ast.Node.AsyncAttribute, + AsyncEnd: AsyncEndCtx, + + ExternType: ExternTypeCtx, + SliceOrArrayAccess: &ast.Node.SuffixOp, + SliceOrArrayType: &ast.Node.PrefixOp, + AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, + + Payload: OptionalCtx, + PointerPayload: OptionalCtx, + PointerIndexPayload: OptionalCtx, + + Expression: OptionalCtx, + RangeExpressionBegin: OptionalCtx, + RangeExpressionEnd: OptionalCtx, + AssignmentExpressionBegin: OptionalCtx, + AssignmentExpressionEnd: OptionalCtx, + UnwrapExpressionBegin: OptionalCtx, + UnwrapExpressionEnd: OptionalCtx, + BoolOrExpressionBegin: OptionalCtx, + BoolOrExpressionEnd: OptionalCtx, + BoolAndExpressionBegin: OptionalCtx, + BoolAndExpressionEnd: OptionalCtx, + ComparisonExpressionBegin: OptionalCtx, + ComparisonExpressionEnd: OptionalCtx, + BinaryOrExpressionBegin: OptionalCtx, + BinaryOrExpressionEnd: OptionalCtx, + BinaryXorExpressionBegin: OptionalCtx, + BinaryXorExpressionEnd: OptionalCtx, + BinaryAndExpressionBegin: OptionalCtx, + BinaryAndExpressionEnd: OptionalCtx, + BitShiftExpressionBegin: OptionalCtx, + BitShiftExpressionEnd: OptionalCtx, + AdditionExpressionBegin: OptionalCtx, + AdditionExpressionEnd: OptionalCtx, + MultiplyExpressionBegin: OptionalCtx, + MultiplyExpressionEnd: OptionalCtx, + CurlySuffixExpressionBegin: OptionalCtx, + CurlySuffixExpressionEnd: OptionalCtx, + TypeExprBegin: OptionalCtx, + TypeExprEnd: OptionalCtx, + PrefixOpExpression: OptionalCtx, + SuffixOpExpressionBegin: OptionalCtx, + SuffixOpExpressionEnd: OptionalCtx, + PrimaryExpression: OptionalCtx, + + ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, + StringLiteral: OptionalCtx, + Identifier: OptionalCtx, + ErrorTag: &&ast.Node, + + + IfToken: @TagType(Token.Id), + IfTokenSave: ExpectTokenSave, + ExpectToken: @TagType(Token.Id), + ExpectTokenSave: ExpectTokenSave, + OptionalTokenSave: OptionalTokenSave, +}; - fn expectToken(self: &Parser, id: @TagType(Token.Id)) !Token { - const token = self.getNextToken(); - if (token.id != id) { - return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id)); +fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment { + var result: ?&ast.Node.DocComment = null; + while (true) { + if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| { + const node = blk: { + if (result) |comment_node| { + break :blk comment_node; + } else { + const comment_node = try arena.construct(ast.Node.DocComment { + .base = ast.Node { + .id = ast.Node.Id.DocComment, + }, + .lines = ast.Node.DocComment.LineList.init(arena), + }); + result = comment_node; + break :blk comment_node; + } + }; + try node.lines.push(line_comment); + continue; } - return token; + break; } + return result; +} + +fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment { + const token = eatToken(tok_it, Token.Id.LineComment) ?? return null; + return try arena.construct(ast.Node.LineComment { + .base = ast.Node { + .id = ast.Node.Id.LineComment, + }, + .token = token, + }); +} + +fn requireSemiColon(node: &const ast.Node) bool { + var n = node; + while (true) { + switch (n.id) { + ast.Node.Id.Root, + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag, + ast.Node.Id.ParamDecl, + ast.Node.Id.Block, + ast.Node.Id.Payload, + ast.Node.Id.PointerPayload, + ast.Node.Id.PointerIndexPayload, + ast.Node.Id.Switch, + ast.Node.Id.SwitchCase, + ast.Node.Id.SwitchElse, + ast.Node.Id.FieldInitializer, + ast.Node.Id.DocComment, + ast.Node.Id.LineComment, + ast.Node.Id.TestDecl => return false, + ast.Node.Id.While => { + const while_node = @fieldParentPtr(ast.Node.While, "base", n); + if (while_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return while_node.body.id != ast.Node.Id.Block; + }, + ast.Node.Id.For => { + const for_node = @fieldParentPtr(ast.Node.For, "base", n); + if (for_node.@"else") |@"else"| { + n = @"else".base; + continue; + } + + return for_node.body.id != ast.Node.Id.Block; + }, + ast.Node.Id.If => { + const if_node = @fieldParentPtr(ast.Node.If, "base", n); + if (if_node.@"else") |@"else"| { + n = @"else".base; + continue; + } - fn eatToken(self: &Parser, id: @TagType(Token.Id)) ?Token { - if (self.isPeekToken(id)) { - return self.getNextToken(); + return if_node.body.id != ast.Node.Id.Block; + }, + ast.Node.Id.Else => { + const else_node = @fieldParentPtr(ast.Node.Else, "base", n); + n = else_node.body; + continue; + }, + ast.Node.Id.Defer => { + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n); + return defer_node.expr.id != ast.Node.Id.Block; + }, + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n); + return comptime_node.expr.id != ast.Node.Id.Block; + }, + ast.Node.Id.Suspend => { + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n); + if (suspend_node.body) |body| { + return body.id != ast.Node.Id.Block; + } + + return true; + }, + else => return true, } - return null; } +} + +fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, + token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node +{ + switch (token_ptr.id) { + Token.Id.StringLiteral => { + return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; + }, + Token.Id.MultilineStringLiteralLine => { + const node = try arena.construct(ast.Node.MultilineStringLiteral { + .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral }, + .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), + }); + try node.lines.push(token_index); + while (true) { + const multiline_str_index = tok_it.index; + const multiline_str_ptr = ??tok_it.next(); + if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) { + _ = tok_it.prev(); + break; + } - fn putBackToken(self: &Parser, token: &const Token) void { - self.put_back_tokens[self.put_back_count] = *token; - self.put_back_count += 1; + try node.lines.push(multiline_str_index); + } + + return &node.base; + }, + // TODO: We shouldn't need a cast, but: + // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. + else => return (?&ast.Node)(null), } +} - fn getNextToken(self: &Parser) Token { - if (self.put_back_count != 0) { - const put_back_index = self.put_back_count - 1; - const put_back_token = self.put_back_tokens[put_back_index]; - self.put_back_count = put_back_index; - return put_back_token; - } else { - return self.tokenizer.next(); +fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx, + token_ptr: &const Token, token_index: TokenIndex) !bool { + switch (token_ptr.id) { + Token.Id.Keyword_suspend => { + const node = try createToCtxNode(arena, ctx, ast.Node.Suspend, + ast.Node.Suspend { + .base = undefined, + .label = null, + .suspend_token = token_index, + .payload = null, + .body = null, + } + ); + + stack.push(State { .SuspendBody = node }) catch unreachable; + try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } }); + return true; + }, + Token.Id.Keyword_if => { + const node = try createToCtxNode(arena, ctx, ast.Node.If, + ast.Node.If { + .base = undefined, + .if_token = token_index, + .condition = undefined, + .payload = null, + .body = undefined, + .@"else" = null, + } + ); + + stack.push(State { .Else = &node.@"else" }) catch unreachable; + try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }); + try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + return true; + }, + Token.Id.Keyword_while => { + stack.push(State { + .While = LoopCtx { + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = *ctx, + } + }) catch unreachable; + return true; + }, + Token.Id.Keyword_for => { + stack.push(State { + .For = LoopCtx { + .label = null, + .inline_token = null, + .loop_token = token_index, + .opt_ctx = *ctx, + } + }) catch unreachable; + return true; + }, + Token.Id.Keyword_switch => { + const node = try arena.construct(ast.Node.Switch { + .base = ast.Node { + .id = ast.Node.Id.Switch, + }, + .switch_token = token_index, + .expr = undefined, + .cases = ast.Node.Switch.CaseList.init(arena), + .rbrace = undefined, + }); + ctx.store(&node.base); + + stack.push(State { + .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) { + .list = &node.cases, + .ptr = &node.rbrace, + }, + }) catch unreachable; + try stack.push(State { .ExpectToken = Token.Id.LBrace }); + try stack.push(State { .ExpectToken = Token.Id.RParen }); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + try stack.push(State { .ExpectToken = Token.Id.LParen }); + return true; + }, + Token.Id.Keyword_comptime => { + const node = try createToCtxNode(arena, ctx, ast.Node.Comptime, + ast.Node.Comptime { + .base = undefined, + .comptime_token = token_index, + .expr = undefined, + .doc_comments = null, + } + ); + try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } }); + return true; + }, + Token.Id.LBrace => { + const block = try arena.construct(ast.Node.Block { + .base = ast.Node {.id = ast.Node.Id.Block }, + .label = null, + .lbrace = token_index, + .statements = ast.Node.Block.StatementList.init(arena), + .rbrace = undefined, + }); + ctx.store(&block.base); + stack.push(State { .Block = block }) catch unreachable; + return true; + }, + else => { + return false; } } +} - fn isPeekToken(self: &Parser, id: @TagType(Token.Id)) bool { - const token = self.getNextToken(); - defer self.putBackToken(token); - return id == token.id; +const ExpectCommaOrEndResult = union(enum) { + end_token: ?TokenIndex, + parse_error: Error, +}; + +fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + switch (token_ptr.id) { + Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null}, + else => { + if (end == token_ptr.id) { + return ExpectCommaOrEndResult { .end_token = token_index }; + } + + return ExpectCommaOrEndResult { + .parse_error = Error { + .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd { + .token = token_index, + .end_id = end, + }, + }, + }; + }, } +} - const RenderAstFrame = struct { - node: &ast.Node, - indent: usize, +fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { + // TODO: We have to cast all cases because of this: + // error: expected type '?InfixOp', found '?@TagType(InfixOp)' + return switch (*id) { + Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} }, + Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} }, + Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} }, + Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} }, + Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} }, + Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} }, + Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} }, + Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} }, + Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} }, + Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} }, + Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} }, + Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} }, + Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} }, + Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} }, + else => null, }; +} - pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { - var stack = self.initUtilityArrayList(RenderAstFrame); - defer self.deinitUtilityArrayList(stack); +fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} }, + else => null, + }; +} - try stack.append(RenderAstFrame { - .node = &root_node.base, - .indent = 0, - }); +fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} }, + Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} }, + Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} }, + Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} }, + Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} }, + Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} }, + else => null, + }; +} - while (stack.popOrNull()) |frame| { - { - var i: usize = 0; - while (i < frame.indent) : (i += 1) { - try stream.print(" "); - } - } - try stream.print("{}\n", @tagName(frame.node.id)); - var child_i: usize = 0; - while (frame.node.iterate(child_i)) |child| : (child_i += 1) { - try stack.append(RenderAstFrame { - .node = child, - .indent = frame.indent + 2, - }); - } - } - } +fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} }, + Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} }, + else => null, + }; +} + +fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} }, + Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} }, + Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} }, + Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} }, + Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} }, + else => null, + }; +} - const RenderState = union(enum) { - TopLevelDecl: &ast.Node, - ParamDecl: &ast.Node, - Text: []const u8, - Expression: &ast.Node, - VarDecl: &ast.Node.VarDecl, - Statement: &ast.Node, - PrintIndent, - Indent: usize, - PrintSameLineComment: ?&Token, - PrintLineComment: &Token, +fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { + return switch (id) { + Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} }, + Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} }, + Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} }, + Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} }, + Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} }, + Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} }, + else => null, }; +} - pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { - var stack = self.initUtilityArrayList(RenderState); - defer self.deinitUtilityArrayList(stack); +fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { + return switch (id) { + Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} }, + Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} }, + Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} }, + Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} }, + Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} }, + Token.Id.Ampersand => ast.Node.PrefixOp.Op { + .AddrOf = ast.Node.PrefixOp.AddrOfInfo { + .align_expr = null, + .bit_offset_start_token = null, + .bit_offset_end_token = null, + .const_token = null, + .volatile_token = null, + }, + }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} }, + Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} }, + Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } }, + else => null, + }; +} - { - try stack.append(RenderState { .Text = "\n"}); - - var i = root_node.decls.len; - while (i != 0) { - i -= 1; - const decl = root_node.decls.items[i]; - try stack.append(RenderState {.TopLevelDecl = decl}); - if (i != 0) { - try stack.append(RenderState { - .Text = blk: { - const prev_node = root_node.decls.at(i - 1); - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, decl.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - } - } +fn createNode(arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T { + const node = try arena.create(T); + *node = *init_to; + node.base = blk: { + const id = ast.Node.typeToId(T); + break :blk ast.Node { + .id = id, + }; + }; + + return node; +} + +fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T { + const node = try createNode(arena, T, init_to); + opt_ctx.store(&node.base); + + return node; +} + +fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { + return createNode(arena, T, + T { + .base = undefined, + .token = token_index, } + ); +} - const indent_delta = 4; - var indent: usize = 0; - while (stack.popOrNull()) |state| { - switch (state) { - RenderState.TopLevelDecl => |decl| { - try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } ); - switch (decl.id) { - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); - try self.renderComments(stream, fn_proto, indent); - - if (fn_proto.body_node) |body_node| { - stack.append(RenderState { .Expression = body_node}) catch unreachable; - try stack.append(RenderState { .Text = " "}); - } else { - stack.append(RenderState { .Text = ";" }) catch unreachable; - } +fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { + const node = try createLiteral(arena, T, token_index); + opt_ctx.store(&node.base); - try stack.append(RenderState { .Expression = decl }); - }, - ast.Node.Id.Use => { - const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); - if (use_decl.visib_token) |visib_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); - } - try stream.print("use "); - try stack.append(RenderState { .Text = ";" }); - try stack.append(RenderState { .Expression = use_decl.expr }); - }, - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); - try self.renderComments(stream, var_decl, indent); - try stack.append(RenderState { .VarDecl = var_decl}); - }, - ast.Node.Id.TestDecl => { - const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); - try self.renderComments(stream, test_decl, indent); - try stream.print("test "); - try stack.append(RenderState { .Expression = test_decl.body_node }); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = test_decl.name }); - }, - ast.Node.Id.StructField => { - const field = @fieldParentPtr(ast.Node.StructField, "base", decl); - try self.renderComments(stream, field, indent); - if (field.visib_token) |visib_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); - } - try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token)); - try stack.append(RenderState { .Text = "," }); - try stack.append(RenderState { .Expression = field.type_expr}); - }, - ast.Node.Id.UnionTag => { - const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); - try self.renderComments(stream, tag, indent); - try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); + return node; +} + +fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex { + const token_index = tok_it.index; + const token_ptr = ??tok_it.next(); + if (token_ptr.id == id) + return token_index; + + _ = tok_it.prev(); + return null; +} - try stack.append(RenderState { .Text = "," }); +const RenderAstFrame = struct { + node: &ast.Node, + indent: usize, +}; - if (tag.value_expr) |value_expr| { - try stack.append(RenderState { .Expression = value_expr }); - try stack.append(RenderState { .Text = " = " }); - } +pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { + var stack = SegmentedList(State, 32).init(allocator); + defer stack.deinit(); - if (tag.type_expr) |type_expr| { - try stream.print(": "); - try stack.append(RenderState { .Expression = type_expr}); - } - }, - ast.Node.Id.EnumTag => { - const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); - try self.renderComments(stream, tag, indent); - try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); - - try stack.append(RenderState { .Text = "," }); - if (tag.value) |value| { - try stream.print(" = "); - try stack.append(RenderState { .Expression = value}); - } - }, - ast.Node.Id.ErrorTag => { - const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); - try self.renderComments(stream, tag, indent); - try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); - }, - ast.Node.Id.Comptime => { - if (requireSemiColon(decl)) { - try stack.append(RenderState { .Text = ";" }); - } - try stack.append(RenderState { .Expression = decl }); - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); - try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); - }, - else => unreachable, - } - }, + try stack.push(RenderAstFrame { + .node = &root_node.base, + .indent = 0, + }); - RenderState.VarDecl => |var_decl| { - try stack.append(RenderState { .Text = ";" }); - if (var_decl.init_node) |init_node| { - try stack.append(RenderState { .Expression = init_node }); - const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; - try stack.append(RenderState { .Text = text }); - } - if (var_decl.align_node) |align_node| { - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = align_node }); - try stack.append(RenderState { .Text = " align(" }); - } - if (var_decl.type_node) |type_node| { - try stack.append(RenderState { .Expression = type_node }); - try stack.append(RenderState { .Text = ": " }); - } - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.name_token) }); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.mut_token) }); + while (stack.popOrNull()) |frame| { + { + var i: usize = 0; + while (i < frame.indent) : (i += 1) { + try stream.print(" "); + } + } + try stream.print("{}\n", @tagName(frame.node.id)); + var child_i: usize = 0; + while (frame.node.iterate(child_i)) |child| : (child_i += 1) { + try stack.push(RenderAstFrame { + .node = child, + .indent = frame.indent + 2, + }); + } + } +} - if (var_decl.comptime_token) |comptime_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(comptime_token) }); - } +const RenderState = union(enum) { + TopLevelDecl: &ast.Node, + ParamDecl: &ast.Node, + Text: []const u8, + Expression: &ast.Node, + VarDecl: &ast.Node.VarDecl, + Statement: &ast.Node, + PrintIndent, + Indent: usize, +}; - if (var_decl.extern_export_token) |extern_export_token| { - if (var_decl.lib_name != null) { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = ??var_decl.lib_name }); +pub fn renderSource(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void { + var stack = SegmentedList(RenderState, 32).init(allocator); + defer stack.deinit(); + + { + try stack.push(RenderState { .Text = "\n"}); + + var i = tree.root_node.decls.len; + while (i != 0) { + i -= 1; + const decl = *tree.root_node.decls.at(i); + try stack.push(RenderState {.TopLevelDecl = decl}); + if (i != 0) { + try stack.push(RenderState { + .Text = blk: { + const prev_node = *tree.root_node.decls.at(i - 1); + const prev_node_last_token = tree.tokens.at(prev_node.lastToken()); + const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; } - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_token) }); - } + break :blk "\n"; + }, + }); + } + } + } - if (var_decl.visib_token) |visib_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); - } - }, + const indent_delta = 4; + var indent: usize = 0; + while (stack.pop()) |state| { + switch (state) { + RenderState.TopLevelDecl => |decl| { + switch (decl.id) { + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + try renderComments(tree, stream, fn_proto, indent); - RenderState.ParamDecl => |base| { - const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); - if (param_decl.comptime_token) |comptime_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token)); - } - if (param_decl.noalias_token) |noalias_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token)); - } - if (param_decl.name_token) |name_token| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token)); - } - if (param_decl.var_args_token) |var_args_token| { - try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token)); - } else { - try stack.append(RenderState { .Expression = param_decl.type_node}); - } - }, - RenderState.Text => |bytes| { - try stream.write(bytes); - }, - RenderState.Expression => |base| switch (base.id) { - ast.Node.Id.Identifier => { - const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token)); - }, - ast.Node.Id.Block => { - const block = @fieldParentPtr(ast.Node.Block, "base", base); - if (block.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); + if (fn_proto.body_node) |body_node| { + stack.push(RenderState { .Expression = body_node}) catch unreachable; + try stack.push(RenderState { .Text = " "}); + } else { + stack.push(RenderState { .Text = ";" }) catch unreachable; } - if (block.statements.len == 0) { - try stream.write("{}"); - } else { - try stream.write("{"); - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent}); - try stack.append(RenderState { .Text = "\n"}); - var i = block.statements.len; - while (i != 0) { - i -= 1; - const statement_node = block.statements.items[i]; - try stack.append(RenderState { .Statement = statement_node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = block.statements.items[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, statement_node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } + try stack.push(RenderState { .Expression = decl }); + }, + ast.Node.Id.Use => { + const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl); + if (use_decl.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); } + try stream.print("use "); + try stack.push(RenderState { .Text = ";" }); + try stack.push(RenderState { .Expression = use_decl.expr }); }, - ast.Node.Id.Defer => { - const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token)); - try stack.append(RenderState { .Expression = defer_node.expr }); + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); + try renderComments(tree, stream, var_decl, indent); + try stack.push(RenderState { .VarDecl = var_decl}); }, - ast.Node.Id.Comptime => { - const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token)); - try stack.append(RenderState { .Expression = comptime_node.expr }); + ast.Node.Id.TestDecl => { + const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl); + try renderComments(tree, stream, test_decl, indent); + try stream.print("test "); + try stack.push(RenderState { .Expression = test_decl.body_node }); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = test_decl.name }); }, - ast.Node.Id.AsyncAttribute => { - const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token)); - - if (async_attr.allocator_type) |allocator_type| { - try stack.append(RenderState { .Text = ">" }); - try stack.append(RenderState { .Expression = allocator_type }); - try stack.append(RenderState { .Text = "<" }); + ast.Node.Id.StructField => { + const field = @fieldParentPtr(ast.Node.StructField, "base", decl); + try renderComments(tree, stream, field, indent); + if (field.visib_token) |visib_token| { + try stream.print("{} ", tree.tokenSlice(visib_token)); } + try stream.print("{}: ", tree.tokenSlice(field.name_token)); + try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Expression = field.type_expr}); }, - ast.Node.Id.Suspend => { - const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); - } - try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token)); + ast.Node.Id.UnionTag => { + const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); - if (suspend_node.body) |body| { - try stack.append(RenderState { .Expression = body }); - try stack.append(RenderState { .Text = " " }); - } + try stack.push(RenderState { .Text = "," }); - if (suspend_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); + if (tag.value_expr) |value_expr| { + try stack.push(RenderState { .Expression = value_expr }); + try stack.push(RenderState { .Text = " = " }); } - }, - ast.Node.Id.InfixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - - if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { - if (prefix_op_node.op.Catch) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - try stack.append(RenderState { .Text = " catch " }); - } else { - const text = switch (prefix_op_node.op) { - ast.Node.InfixOp.Op.Add => " + ", - ast.Node.InfixOp.Op.AddWrap => " +% ", - ast.Node.InfixOp.Op.ArrayCat => " ++ ", - ast.Node.InfixOp.Op.ArrayMult => " ** ", - ast.Node.InfixOp.Op.Assign => " = ", - ast.Node.InfixOp.Op.AssignBitAnd => " &= ", - ast.Node.InfixOp.Op.AssignBitOr => " |= ", - ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", - ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", - ast.Node.InfixOp.Op.AssignBitXor => " ^= ", - ast.Node.InfixOp.Op.AssignDiv => " /= ", - ast.Node.InfixOp.Op.AssignMinus => " -= ", - ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", - ast.Node.InfixOp.Op.AssignMod => " %= ", - ast.Node.InfixOp.Op.AssignPlus => " += ", - ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", - ast.Node.InfixOp.Op.AssignTimes => " *= ", - ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", - ast.Node.InfixOp.Op.BangEqual => " != ", - ast.Node.InfixOp.Op.BitAnd => " & ", - ast.Node.InfixOp.Op.BitOr => " | ", - ast.Node.InfixOp.Op.BitShiftLeft => " << ", - ast.Node.InfixOp.Op.BitShiftRight => " >> ", - ast.Node.InfixOp.Op.BitXor => " ^ ", - ast.Node.InfixOp.Op.BoolAnd => " and ", - ast.Node.InfixOp.Op.BoolOr => " or ", - ast.Node.InfixOp.Op.Div => " / ", - ast.Node.InfixOp.Op.EqualEqual => " == ", - ast.Node.InfixOp.Op.ErrorUnion => "!", - ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", - ast.Node.InfixOp.Op.GreaterThan => " > ", - ast.Node.InfixOp.Op.LessOrEqual => " <= ", - ast.Node.InfixOp.Op.LessThan => " < ", - ast.Node.InfixOp.Op.MergeErrorSets => " || ", - ast.Node.InfixOp.Op.Mod => " % ", - ast.Node.InfixOp.Op.Mult => " * ", - ast.Node.InfixOp.Op.MultWrap => " *% ", - ast.Node.InfixOp.Op.Period => ".", - ast.Node.InfixOp.Op.Sub => " - ", - ast.Node.InfixOp.Op.SubWrap => " -% ", - ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", - ast.Node.InfixOp.Op.Range => " ... ", - ast.Node.InfixOp.Op.Catch => unreachable, - }; - try stack.append(RenderState { .Text = text }); + if (tag.type_expr) |type_expr| { + try stream.print(": "); + try stack.push(RenderState { .Expression = type_expr}); } - try stack.append(RenderState { .Expression = prefix_op_node.lhs }); }, - ast.Node.Id.PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - try stack.append(RenderState { .Expression = prefix_op_node.rhs }); - switch (prefix_op_node.op) { - ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { - try stream.write("&"); - if (addr_of_info.volatile_token != null) { - try stack.append(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.append(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { - try stream.write("[]"); - if (addr_of_info.volatile_token != null) { - try stack.append(RenderState { .Text = "volatile "}); - } - if (addr_of_info.const_token != null) { - try stack.append(RenderState { .Text = "const "}); - } - if (addr_of_info.align_expr) |align_expr| { - try stream.print("align("); - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = align_expr}); - } - }, - ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try stack.append(RenderState { .Text = "]"}); - try stack.append(RenderState { .Expression = array_index}); - try stack.append(RenderState { .Text = "["}); - }, - ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), - ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), - ast.Node.PrefixOp.Op.Deref => try stream.write("*"), - ast.Node.PrefixOp.Op.Negation => try stream.write("-"), - ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), - ast.Node.PrefixOp.Op.Try => try stream.write("try "), - ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), - ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), - ast.Node.PrefixOp.Op.Await => try stream.write("await "), - ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), - ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + ast.Node.Id.EnumTag => { + const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); + + try stack.push(RenderState { .Text = "," }); + if (tag.value) |value| { + try stream.print(" = "); + try stack.push(RenderState { .Expression = value}); } }, - ast.Node.Id.SuffixOp => { - const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); - - switch (suffix_op.op) { - ast.Node.SuffixOp.Op.Call => |call_info| { - try stack.append(RenderState { .Text = ")"}); - var i = call_info.params.len; - while (i != 0) { - i -= 1; - const param_node = call_info.params.at(i); - try stack.append(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } - } - try stack.append(RenderState { .Text = "("}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - - if (call_info.async_attr) |async_attr| { - try stack.append(RenderState { .Text = " "}); - try stack.append(RenderState { .Expression = &async_attr.base }); - } - }, - ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { - try stack.append(RenderState { .Text = "]"}); - try stack.append(RenderState { .Expression = index_expr}); - try stack.append(RenderState { .Text = "["}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.Slice => |range| { - try stack.append(RenderState { .Text = "]"}); - if (range.end) |end| { - try stack.append(RenderState { .Expression = end}); - } - try stack.append(RenderState { .Text = ".."}); - try stack.append(RenderState { .Expression = range.start}); - try stack.append(RenderState { .Text = "["}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.StructInitializer => |field_inits| { - if (field_inits.len == 0) { - try stack.append(RenderState { .Text = "{}" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (field_inits.len == 1) { - const field_init = field_inits.at(0); - - try stack.append(RenderState { .Text = " }" }); - try stack.append(RenderState { .Expression = field_init }); - try stack.append(RenderState { .Text = "{ " }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n" }); - var i = field_inits.len; - while (i != 0) { - i -= 1; - const field_init = field_inits.at(i); - if (field_init.id != ast.Node.Id.LineComment) { - try stack.append(RenderState { .Text = "," }); - } - try stack.append(RenderState { .Expression = field_init }); - try stack.append(RenderState.PrintIndent); - if (i != 0) { - try stack.append(RenderState { .Text = blk: { - const prev_node = field_inits.at(i - 1); - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }}); - } - } - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "{\n"}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| { - if (exprs.len == 0) { - try stack.append(RenderState { .Text = "{}" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - if (exprs.len == 1) { - const expr = exprs.at(0); - - try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState { .Text = "{" }); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - continue; - } - - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - var i = exprs.len; - while (i != 0) { - i -= 1; - const expr = exprs.at(i); - try stack.append(RenderState { .Text = ",\n" }); - try stack.append(RenderState { .Expression = expr }); - try stack.append(RenderState.PrintIndent); - } - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "{\n"}); - try stack.append(RenderState { .Expression = suffix_op.lhs }); - }, - } + ast.Node.Id.ErrorTag => { + const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); + try renderComments(tree, stream, tag, indent); + try stream.print("{}", tree.tokenSlice(tag.name_token)); }, - ast.Node.Id.ControlFlowExpression => { - const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); - - if (flow_expr.rhs) |rhs| { - try stack.append(RenderState { .Expression = rhs }); - try stack.append(RenderState { .Text = " " }); - } - - switch (flow_expr.kind) { - ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { - try stream.print("break"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.append(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { - try stream.print("continue"); - if (maybe_label) |label| { - try stream.print(" :"); - try stack.append(RenderState { .Expression = label }); - } - }, - ast.Node.ControlFlowExpression.Kind.Return => { - try stream.print("return"); - }, - + ast.Node.Id.Comptime => { + if (requireSemiColon(decl)) { + try stack.push(RenderState { .Text = ";" }); } + try stack.push(RenderState { .Expression = decl }); }, - ast.Node.Id.Payload => { - const payload = @fieldParentPtr(ast.Node.Payload, "base", base); - try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = payload.error_symbol }); - try stack.append(RenderState { .Text = "|"}); + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); + try stream.write(tree.tokenSlice(line_comment_node.token)); }, - ast.Node.Id.PointerPayload => { - const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); - try stack.append(RenderState { .Text = "|"}); - try stack.append(RenderState { .Expression = payload.value_symbol }); + else => unreachable, + } + }, - if (payload.ptr_token) |ptr_token| { - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) }); - } + RenderState.VarDecl => |var_decl| { + try stack.push(RenderState { .Text = ";" }); + if (var_decl.init_node) |init_node| { + try stack.push(RenderState { .Expression = init_node }); + const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; + try stack.push(RenderState { .Text = text }); + } + if (var_decl.align_node) |align_node| { + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = align_node }); + try stack.push(RenderState { .Text = " align(" }); + } + if (var_decl.type_node) |type_node| { + try stack.push(RenderState { .Expression = type_node }); + try stack.push(RenderState { .Text = ": " }); + } + try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) }); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) }); - try stack.append(RenderState { .Text = "|"}); - }, - ast.Node.Id.PointerIndexPayload => { - const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); - try stack.append(RenderState { .Text = "|"}); + if (var_decl.comptime_token) |comptime_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) }); + } - if (payload.index_symbol) |index_symbol| { - try stack.append(RenderState { .Expression = index_symbol }); - try stack.append(RenderState { .Text = ", "}); - } + if (var_decl.extern_export_token) |extern_export_token| { + if (var_decl.lib_name != null) { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = ??var_decl.lib_name }); + } + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) }); + } + + if (var_decl.visib_token) |visib_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) }); + } + }, - try stack.append(RenderState { .Expression = payload.value_symbol }); + RenderState.ParamDecl => |base| { + const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); + if (param_decl.comptime_token) |comptime_token| { + try stream.print("{} ", tree.tokenSlice(comptime_token)); + } + if (param_decl.noalias_token) |noalias_token| { + try stream.print("{} ", tree.tokenSlice(noalias_token)); + } + if (param_decl.name_token) |name_token| { + try stream.print("{}: ", tree.tokenSlice(name_token)); + } + if (param_decl.var_args_token) |var_args_token| { + try stream.print("{}", tree.tokenSlice(var_args_token)); + } else { + try stack.push(RenderState { .Expression = param_decl.type_node}); + } + }, + RenderState.Text => |bytes| { + try stream.write(bytes); + }, + RenderState.Expression => |base| switch (base.id) { + ast.Node.Id.Identifier => { + const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base); + try stream.print("{}", tree.tokenSlice(identifier.token)); + }, + ast.Node.Id.Block => { + const block = @fieldParentPtr(ast.Node.Block, "base", base); + if (block.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } - if (payload.ptr_token) |ptr_token| { - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) }); + if (block.statements.len == 0) { + try stream.write("{}"); + } else { + try stream.write("{"); + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent}); + try stack.push(RenderState { .Text = "\n"}); + var i = block.statements.len; + while (i != 0) { + i -= 1; + const statement_node = *block.statements.at(i); + try stack.push(RenderState { .Statement = statement_node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *block.statements.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); } + } + }, + ast.Node.Id.Defer => { + const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base); + try stream.print("{} ", tree.tokenSlice(defer_node.defer_token)); + try stack.push(RenderState { .Expression = defer_node.expr }); + }, + ast.Node.Id.Comptime => { + const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base); + try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token)); + try stack.push(RenderState { .Expression = comptime_node.expr }); + }, + ast.Node.Id.AsyncAttribute => { + const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base); + try stream.print("{}", tree.tokenSlice(async_attr.async_token)); + + if (async_attr.allocator_type) |allocator_type| { + try stack.push(RenderState { .Text = ">" }); + try stack.push(RenderState { .Expression = allocator_type }); + try stack.push(RenderState { .Text = "<" }); + } + }, + ast.Node.Id.Suspend => { + const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); + if (suspend_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } + try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token)); - try stack.append(RenderState { .Text = "|"}); - }, - ast.Node.Id.GroupedExpression => { - const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); - try stack.append(RenderState { .Text = ")"}); - try stack.append(RenderState { .Expression = grouped_expr.expr }); - try stack.append(RenderState { .Text = "("}); - }, - ast.Node.Id.FieldInitializer => { - const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); - try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token)); - try stack.append(RenderState { .Expression = field_init.expr }); - }, - ast.Node.Id.IntegerLiteral => { - const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token)); - }, - ast.Node.Id.FloatLiteral => { - const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token)); - }, - ast.Node.Id.StringLiteral => { - const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token)); - }, - ast.Node.Id.CharLiteral => { - const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token)); - }, - ast.Node.Id.BoolLiteral => { - const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token)); - }, - ast.Node.Id.NullLiteral => { - const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token)); - }, - ast.Node.Id.ThisLiteral => { - const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token)); - }, - ast.Node.Id.Unreachable => { - const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token)); - }, - ast.Node.Id.ErrorType => { - const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token)); - }, - ast.Node.Id.VarType => { - const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token)); - }, - ast.Node.Id.ContainerDecl => { - const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); + if (suspend_node.body) |body| { + try stack.push(RenderState { .Expression = body }); + try stack.push(RenderState { .Text = " " }); + } - switch (container_decl.layout) { - ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), - ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), - ast.Node.ContainerDecl.Layout.Auto => { }, + if (suspend_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } + }, + ast.Node.Id.InfixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); + try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + + if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) { + if (prefix_op_node.op.Catch) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); } + try stack.push(RenderState { .Text = " catch " }); + } else { + const text = switch (prefix_op_node.op) { + ast.Node.InfixOp.Op.Add => " + ", + ast.Node.InfixOp.Op.AddWrap => " +% ", + ast.Node.InfixOp.Op.ArrayCat => " ++ ", + ast.Node.InfixOp.Op.ArrayMult => " ** ", + ast.Node.InfixOp.Op.Assign => " = ", + ast.Node.InfixOp.Op.AssignBitAnd => " &= ", + ast.Node.InfixOp.Op.AssignBitOr => " |= ", + ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ", + ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ", + ast.Node.InfixOp.Op.AssignBitXor => " ^= ", + ast.Node.InfixOp.Op.AssignDiv => " /= ", + ast.Node.InfixOp.Op.AssignMinus => " -= ", + ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ", + ast.Node.InfixOp.Op.AssignMod => " %= ", + ast.Node.InfixOp.Op.AssignPlus => " += ", + ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ", + ast.Node.InfixOp.Op.AssignTimes => " *= ", + ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ", + ast.Node.InfixOp.Op.BangEqual => " != ", + ast.Node.InfixOp.Op.BitAnd => " & ", + ast.Node.InfixOp.Op.BitOr => " | ", + ast.Node.InfixOp.Op.BitShiftLeft => " << ", + ast.Node.InfixOp.Op.BitShiftRight => " >> ", + ast.Node.InfixOp.Op.BitXor => " ^ ", + ast.Node.InfixOp.Op.BoolAnd => " and ", + ast.Node.InfixOp.Op.BoolOr => " or ", + ast.Node.InfixOp.Op.Div => " / ", + ast.Node.InfixOp.Op.EqualEqual => " == ", + ast.Node.InfixOp.Op.ErrorUnion => "!", + ast.Node.InfixOp.Op.GreaterOrEqual => " >= ", + ast.Node.InfixOp.Op.GreaterThan => " > ", + ast.Node.InfixOp.Op.LessOrEqual => " <= ", + ast.Node.InfixOp.Op.LessThan => " < ", + ast.Node.InfixOp.Op.MergeErrorSets => " || ", + ast.Node.InfixOp.Op.Mod => " % ", + ast.Node.InfixOp.Op.Mult => " * ", + ast.Node.InfixOp.Op.MultWrap => " *% ", + ast.Node.InfixOp.Op.Period => ".", + ast.Node.InfixOp.Op.Sub => " - ", + ast.Node.InfixOp.Op.SubWrap => " -% ", + ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ", + ast.Node.InfixOp.Op.Range => " ... ", + ast.Node.InfixOp.Op.Catch => unreachable, + }; - switch (container_decl.kind) { - ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), - ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), - ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), - } + try stack.push(RenderState { .Text = text }); + } + try stack.push(RenderState { .Expression = prefix_op_node.lhs }); + }, + ast.Node.Id.PrefixOp => { + const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); + try stack.push(RenderState { .Expression = prefix_op_node.rhs }); + switch (prefix_op_node.op) { + ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| { + try stream.write("&"); + if (addr_of_info.volatile_token != null) { + try stack.push(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + try stack.push(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = align_expr}); + } + }, + ast.Node.PrefixOp.Op.SliceType => |addr_of_info| { + try stream.write("[]"); + if (addr_of_info.volatile_token != null) { + try stack.push(RenderState { .Text = "volatile "}); + } + if (addr_of_info.const_token != null) { + try stack.push(RenderState { .Text = "const "}); + } + if (addr_of_info.align_expr) |align_expr| { + try stream.print("align("); + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = align_expr}); + } + }, + ast.Node.PrefixOp.Op.ArrayType => |array_index| { + try stack.push(RenderState { .Text = "]"}); + try stack.push(RenderState { .Expression = array_index}); + try stack.push(RenderState { .Text = "["}); + }, + ast.Node.PrefixOp.Op.BitNot => try stream.write("~"), + ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"), + ast.Node.PrefixOp.Op.Deref => try stream.write("*"), + ast.Node.PrefixOp.Op.Negation => try stream.write("-"), + ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"), + ast.Node.PrefixOp.Op.Try => try stream.write("try "), + ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"), + ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"), + ast.Node.PrefixOp.Op.Await => try stream.write("await "), + ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "), + ast.Node.PrefixOp.Op.Resume => try stream.write("resume "), + } + }, + ast.Node.Id.SuffixOp => { + const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base); - const fields_and_decls = container_decl.fields_and_decls.toSliceConst(); - if (fields_and_decls.len == 0) { - try stack.append(RenderState { .Text = "{}"}); - } else { - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + switch (suffix_op.op) { + @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| { + try stack.push(RenderState { .Text = ")"}); + var i = call_info.params.len; + while (i != 0) { + i -= 1; + const param_node = *call_info.params.at(i); + try stack.push(RenderState { .Expression = param_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); + } + } + try stack.push(RenderState { .Text = "("}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + + if (call_info.async_attr) |async_attr| { + try stack.push(RenderState { .Text = " "}); + try stack.push(RenderState { .Expression = &async_attr.base }); + } + }, + ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| { + try stack.push(RenderState { .Text = "]"}); + try stack.push(RenderState { .Expression = index_expr}); + try stack.push(RenderState { .Text = "["}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + @TagType(ast.Node.SuffixOp.Op).Slice => |range| { + try stack.push(RenderState { .Text = "]"}); + if (range.end) |end| { + try stack.push(RenderState { .Expression = end}); + } + try stack.push(RenderState { .Text = ".."}); + try stack.push(RenderState { .Expression = range.start}); + try stack.push(RenderState { .Text = "["}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| { + if (field_inits.len == 0) { + try stack.push(RenderState { .Text = "{}" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + if (field_inits.len == 1) { + const field_init = *field_inits.at(0); - var i = fields_and_decls.len; + try stack.push(RenderState { .Text = " }" }); + try stack.push(RenderState { .Expression = field_init }); + try stack.push(RenderState { .Text = "{ " }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n" }); + var i = field_inits.len; while (i != 0) { i -= 1; - const node = fields_and_decls[i]; - try stack.append(RenderState { .TopLevelDecl = node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = fields_and_decls[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } + const field_init = *field_inits.at(i); + if (field_init.id != ast.Node.Id.LineComment) { + try stack.push(RenderState { .Text = "," }); + } + try stack.push(RenderState { .Expression = field_init }); + try stack.push(RenderState.PrintIndent); + if (i != 0) { + try stack.push(RenderState { .Text = blk: { + const prev_node = *field_inits.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; } break :blk "\n"; - }, - }); + }}); + } } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "{"}); - } + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "{\n"}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| { + if (exprs.len == 0) { + try stack.push(RenderState { .Text = "{}" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } + if (exprs.len == 1) { + const expr = *exprs.at(0); - switch (container_decl.init_arg_expr) { - ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), - ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { - if (enum_tag_type) |expr| { - try stack.append(RenderState { .Text = ")) "}); - try stack.append(RenderState { .Expression = expr}); - try stack.append(RenderState { .Text = "(enum("}); - } else { - try stack.append(RenderState { .Text = "(enum) "}); - } - }, - ast.Node.ContainerDecl.InitArg.Type => |type_expr| { - try stack.append(RenderState { .Text = ") "}); - try stack.append(RenderState { .Expression = type_expr}); - try stack.append(RenderState { .Text = "("}); - }, - } - }, - ast.Node.Id.ErrorSetDecl => { - const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); + try stack.push(RenderState { .Text = "}" }); + try stack.push(RenderState { .Expression = expr }); + try stack.push(RenderState { .Text = "{" }); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + continue; + } - const decls = err_set_decl.decls.toSliceConst(); - if (decls.len == 0) { - try stream.write("error{}"); - continue; - } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + var i = exprs.len; + while (i != 0) { + i -= 1; + const expr = *exprs.at(i); + try stack.push(RenderState { .Text = ",\n" }); + try stack.push(RenderState { .Expression = expr }); + try stack.push(RenderState.PrintIndent); + } + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "{\n"}); + try stack.push(RenderState { .Expression = suffix_op.lhs }); + }, + } + }, + ast.Node.Id.ControlFlowExpression => { + const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base); - if (decls.len == 1) blk: { - const node = decls[0]; + if (flow_expr.rhs) |rhs| { + try stack.push(RenderState { .Expression = rhs }); + try stack.push(RenderState { .Text = " " }); + } - // if there are any doc comments or same line comments - // don't try to put it all on one line - if (node.same_line_comment != null) break :blk; - if (node.cast(ast.Node.ErrorTag)) |tag| { - if (tag.doc_comments != null) break :blk; - } else { - break :blk; + switch (flow_expr.kind) { + ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| { + try stream.print("break"); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.push(RenderState { .Expression = label }); + } + }, + ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| { + try stream.print("continue"); + if (maybe_label) |label| { + try stream.print(" :"); + try stack.push(RenderState { .Expression = label }); } + }, + ast.Node.ControlFlowExpression.Kind.Return => { + try stream.print("return"); + }, + + } + }, + ast.Node.Id.Payload => { + const payload = @fieldParentPtr(ast.Node.Payload, "base", base); + try stack.push(RenderState { .Text = "|"}); + try stack.push(RenderState { .Expression = payload.error_symbol }); + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.PointerPayload => { + const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base); + try stack.push(RenderState { .Text = "|"}); + try stack.push(RenderState { .Expression = payload.value_symbol }); + + if (payload.ptr_token) |ptr_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + } + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.PointerIndexPayload => { + const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base); + try stack.push(RenderState { .Text = "|"}); + + if (payload.index_symbol) |index_symbol| { + try stack.push(RenderState { .Expression = index_symbol }); + try stack.push(RenderState { .Text = ", "}); + } + + try stack.push(RenderState { .Expression = payload.value_symbol }); + + if (payload.ptr_token) |ptr_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) }); + } + + try stack.push(RenderState { .Text = "|"}); + }, + ast.Node.Id.GroupedExpression => { + const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base); + try stack.push(RenderState { .Text = ")"}); + try stack.push(RenderState { .Expression = grouped_expr.expr }); + try stack.push(RenderState { .Text = "("}); + }, + ast.Node.Id.FieldInitializer => { + const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base); + try stream.print(".{} = ", tree.tokenSlice(field_init.name_token)); + try stack.push(RenderState { .Expression = field_init.expr }); + }, + ast.Node.Id.IntegerLiteral => { + const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(integer_literal.token)); + }, + ast.Node.Id.FloatLiteral => { + const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(float_literal.token)); + }, + ast.Node.Id.StringLiteral => { + const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(string_literal.token)); + }, + ast.Node.Id.CharLiteral => { + const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(char_literal.token)); + }, + ast.Node.Id.BoolLiteral => { + const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(bool_literal.token)); + }, + ast.Node.Id.NullLiteral => { + const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(null_literal.token)); + }, + ast.Node.Id.ThisLiteral => { + const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(this_literal.token)); + }, + ast.Node.Id.Unreachable => { + const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base); + try stream.print("{}", tree.tokenSlice(unreachable_node.token)); + }, + ast.Node.Id.ErrorType => { + const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base); + try stream.print("{}", tree.tokenSlice(error_type.token)); + }, + ast.Node.Id.VarType => { + const var_type = @fieldParentPtr(ast.Node.VarType, "base", base); + try stream.print("{}", tree.tokenSlice(var_type.token)); + }, + ast.Node.Id.ContainerDecl => { + const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base); - try stream.write("error{"); - try stack.append(RenderState { .Text = "}" }); - try stack.append(RenderState { .TopLevelDecl = node }); - continue; - } + switch (container_decl.layout) { + ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "), + ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "), + ast.Node.ContainerDecl.Layout.Auto => { }, + } - try stream.write("error{"); + switch (container_decl.kind) { + ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"), + ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"), + ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), + } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + if (container_decl.fields_and_decls.len == 0) { + try stack.push(RenderState { .Text = "{}"}); + } else { + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); - var i = decls.len; + var i = container_decl.fields_and_decls.len; while (i != 0) { i -= 1; - const node = decls[i]; - if (node.id != ast.Node.Id.LineComment) { - try stack.append(RenderState { .Text = "," }); - } - try stack.append(RenderState { .TopLevelDecl = node }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { + const node = *container_decl.fields_and_decls.at(i); + try stack.push(RenderState { .TopLevelDecl = node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = blk: { if (i != 0) { - const prev_node = decls[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); + const prev_node = *container_decl.fields_and_decls.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); if (loc.line >= 2) { break :blk "\n\n"; } @@ -4191,538 +4162,579 @@ pub const Parser = struct { }, }); } - try stack.append(RenderState { .Indent = indent + indent_delta}); - }, - ast.Node.Id.MultilineStringLiteral => { - const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); - try stream.print("\n"); - - var i : usize = 0; - while (i < multiline_str_literal.tokens.len) : (i += 1) { - const t = multiline_str_literal.tokens.at(i); - try stream.writeByteNTimes(' ', indent + indent_delta); - try stream.print("{}", self.tokenizer.getTokenSlice(t)); - } - try stream.writeByteNTimes(' ', indent); - }, - ast.Node.Id.UndefinedLiteral => { - const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token)); - }, - ast.Node.Id.BuiltinCall => { - const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); - try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token)); - try stack.append(RenderState { .Text = ")"}); - var i = builtin_call.params.len; - while (i != 0) { - i -= 1; - const param_node = builtin_call.params.at(i); - try stack.append(RenderState { .Expression = param_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "{"}); + } + + switch (container_decl.init_arg_expr) { + ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}), + ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { + if (enum_tag_type) |expr| { + try stack.push(RenderState { .Text = ")) "}); + try stack.push(RenderState { .Expression = expr}); + try stack.push(RenderState { .Text = "(enum("}); + } else { + try stack.push(RenderState { .Text = "(enum) "}); } - } - }, - ast.Node.Id.FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); + }, + ast.Node.ContainerDecl.InitArg.Type => |type_expr| { + try stack.push(RenderState { .Text = ") "}); + try stack.push(RenderState { .Expression = type_expr}); + try stack.push(RenderState { .Text = "("}); + }, + } + }, + ast.Node.Id.ErrorSetDecl => { + const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); - switch (fn_proto.return_type) { - ast.Node.FnProto.ReturnType.Explicit => |node| { - try stack.append(RenderState { .Expression = node}); - }, - ast.Node.FnProto.ReturnType.InferErrorSet => |node| { - try stack.append(RenderState { .Expression = node}); - try stack.append(RenderState { .Text = "!"}); - }, - } + if (err_set_decl.decls.len == 0) { + try stream.write("error{}"); + continue; + } - if (fn_proto.align_expr) |align_expr| { - try stack.append(RenderState { .Text = ") " }); - try stack.append(RenderState { .Expression = align_expr}); - try stack.append(RenderState { .Text = "align(" }); - } + if (err_set_decl.decls.len == 1) blk: { + const node = *err_set_decl.decls.at(0); - try stack.append(RenderState { .Text = ") " }); - var i = fn_proto.params.len; - while (i != 0) { - i -= 1; - const param_decl_node = fn_proto.params.items[i]; - try stack.append(RenderState { .ParamDecl = param_decl_node}); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } + // if there are any doc comments or same line comments + // don't try to put it all on one line + if (node.cast(ast.Node.ErrorTag)) |tag| { + if (tag.doc_comments != null) break :blk; + } else { + break :blk; } - try stack.append(RenderState { .Text = "(" }); - if (fn_proto.name_token) |name_token| { - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) }); - try stack.append(RenderState { .Text = " " }); - } - try stack.append(RenderState { .Text = "fn" }); + try stream.write("error{"); + try stack.push(RenderState { .Text = "}" }); + try stack.push(RenderState { .TopLevelDecl = node }); + continue; + } - if (fn_proto.async_attr) |async_attr| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = &async_attr.base }); - } + try stream.write("error{"); - if (fn_proto.cc_token) |cc_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) }); - } + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); - if (fn_proto.lib_name) |lib_name| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = lib_name }); + var i = err_set_decl.decls.len; + while (i != 0) { + i -= 1; + const node = *err_set_decl.decls.at(i); + if (node.id != ast.Node.Id.LineComment) { + try stack.push(RenderState { .Text = "," }); } - if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_inline_token) }); + try stack.push(RenderState { .TopLevelDecl = node }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *err_set_decl.decls.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; + }, + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + }, + ast.Node.Id.MultilineStringLiteral => { + const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); + try stream.print("\n"); + + var i : usize = 0; + while (i < multiline_str_literal.lines.len) : (i += 1) { + const t = *multiline_str_literal.lines.at(i); + try stream.writeByteNTimes(' ', indent + indent_delta); + try stream.print("{}", tree.tokenSlice(t)); + } + try stream.writeByteNTimes(' ', indent); + }, + ast.Node.Id.UndefinedLiteral => { + const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); + try stream.print("{}", tree.tokenSlice(undefined_literal.token)); + }, + ast.Node.Id.BuiltinCall => { + const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base); + try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token)); + try stack.push(RenderState { .Text = ")"}); + var i = builtin_call.params.len; + while (i != 0) { + i -= 1; + const param_node = *builtin_call.params.at(i); + try stack.push(RenderState { .Expression = param_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); } + } + }, + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base); - if (fn_proto.visib_token) |visib_token| { - assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); - } - }, - ast.Node.Id.PromiseType => { - const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); - try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token)); - if (promise_type.result) |result| { - try stream.write(self.tokenizer.getTokenSlice(result.arrow_token)); - try stack.append(RenderState { .Expression = result.return_type}); - } - }, - ast.Node.Id.LineComment => { - const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); - try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); - }, - ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes - ast.Node.Id.Switch => { - const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); - const cases = switch_node.cases.toSliceConst(); + switch (fn_proto.return_type) { + ast.Node.FnProto.ReturnType.Explicit => |node| { + try stack.push(RenderState { .Expression = node}); + }, + ast.Node.FnProto.ReturnType.InferErrorSet => |node| { + try stack.push(RenderState { .Expression = node}); + try stack.push(RenderState { .Text = "!"}); + }, + } - try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); + if (fn_proto.align_expr) |align_expr| { + try stack.push(RenderState { .Text = ") " }); + try stack.push(RenderState { .Expression = align_expr}); + try stack.push(RenderState { .Text = "align(" }); + } - if (cases.len == 0) { - try stack.append(RenderState { .Text = ") {}"}); - try stack.append(RenderState { .Expression = switch_node.expr }); - continue; + try stack.push(RenderState { .Text = ") " }); + var i = fn_proto.params.len; + while (i != 0) { + i -= 1; + const param_decl_node = *fn_proto.params.at(i); + try stack.push(RenderState { .ParamDecl = param_decl_node}); + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); } + } - try stack.append(RenderState { .Text = "}"}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = "\n"}); + try stack.push(RenderState { .Text = "(" }); + if (fn_proto.name_token) |name_token| { + try stack.push(RenderState { .Text = tree.tokenSlice(name_token) }); + try stack.push(RenderState { .Text = " " }); + } - var i = cases.len; - while (i != 0) { - i -= 1; - const node = cases[i]; - try stack.append(RenderState { .Expression = node}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - if (i != 0) { - const prev_node = cases[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - } - break :blk "\n"; - }, - }); - } - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = ") {"}); - try stack.append(RenderState { .Expression = switch_node.expr }); - }, - ast.Node.Id.SwitchCase => { - const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); - - try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment }); - try stack.append(RenderState { .Text = "," }); - try stack.append(RenderState { .Expression = switch_case.expr }); - if (switch_case.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } - try stack.append(RenderState { .Text = " => "}); + try stack.push(RenderState { .Text = "fn" }); - const items = switch_case.items.toSliceConst(); - var i = items.len; - while (i != 0) { - i -= 1; - try stack.append(RenderState { .Expression = items[i] }); + if (fn_proto.async_attr) |async_attr| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = &async_attr.base }); + } - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = ",\n" }); - } - } - }, - ast.Node.Id.SwitchElse => { - const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token)); - }, - ast.Node.Id.Else => { - const else_node = @fieldParentPtr(ast.Node.Else, "base", base); - try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token)); - - switch (else_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - try stream.print(" "); - try stack.append(RenderState { .Expression = else_node.body }); + if (fn_proto.cc_token) |cc_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) }); + } + + if (fn_proto.lib_name) |lib_name| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = lib_name }); + } + if (fn_proto.extern_export_inline_token) |extern_export_inline_token| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) }); + } + + if (fn_proto.visib_token) |visib_token_index| { + const visib_token = tree.tokens.at(visib_token_index); + assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export); + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) }); + } + }, + ast.Node.Id.PromiseType => { + const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); + try stream.write(tree.tokenSlice(promise_type.promise_token)); + if (promise_type.result) |result| { + try stream.write(tree.tokenSlice(result.arrow_token)); + try stack.push(RenderState { .Expression = result.return_type}); + } + }, + ast.Node.Id.LineComment => { + const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); + try stream.write(tree.tokenSlice(line_comment_node.token)); + }, + ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes + ast.Node.Id.Switch => { + const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); + + try stream.print("{} (", tree.tokenSlice(switch_node.switch_token)); + + if (switch_node.cases.len == 0) { + try stack.push(RenderState { .Text = ") {}"}); + try stack.push(RenderState { .Expression = switch_node.expr }); + continue; + } + + try stack.push(RenderState { .Text = "}"}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = "\n"}); + + var i = switch_node.cases.len; + while (i != 0) { + i -= 1; + const node = *switch_node.cases.at(i); + try stack.push(RenderState { .Expression = node}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + if (i != 0) { + const prev_node = *switch_node.cases.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + } + break :blk "\n"; }, - else => { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = else_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } - } + }); + } + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = ") {"}); + try stack.push(RenderState { .Expression = switch_node.expr }); + }, + ast.Node.Id.SwitchCase => { + const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); + + try stack.push(RenderState { .Text = "," }); + try stack.push(RenderState { .Expression = switch_case.expr }); + if (switch_case.payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + try stack.push(RenderState { .Text = " => "}); + + var i = switch_case.items.len; + while (i != 0) { + i -= 1; + try stack.push(RenderState { .Expression = *switch_case.items.at(i) }); - if (else_node.payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = ",\n" }); } - }, - ast.Node.Id.While => { - const while_node = @fieldParentPtr(ast.Node.While, "base", base); - if (while_node.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); + } + }, + ast.Node.Id.SwitchElse => { + const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base); + try stream.print("{}", tree.tokenSlice(switch_else.token)); + }, + ast.Node.Id.Else => { + const else_node = @fieldParentPtr(ast.Node.Else, "base", base); + try stream.print("{}", tree.tokenSlice(else_node.else_token)); + + switch (else_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => { + try stream.print(" "); + try stack.push(RenderState { .Expression = else_node.body }); + }, + else => { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = else_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); } + } - if (while_node.inline_token) |inline_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token)); - } + if (else_node.payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + }, + ast.Node.Id.While => { + const while_node = @fieldParentPtr(ast.Node.While, "base", base); + if (while_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } - try stream.print("{} ", self.tokenizer.getTokenSlice(while_node.while_token)); + if (while_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } - if (while_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); + try stream.print("{} ", tree.tokenSlice(while_node.while_token)); - if (while_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } + if (while_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); if (while_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Expression = while_node.body }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = " " }); } else { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = while_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); } + } - if (while_node.continue_expr) |continue_expr| { - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = continue_expr }); - try stack.append(RenderState { .Text = ": (" }); - try stack.append(RenderState { .Text = " " }); - } + if (while_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Expression = while_node.body }); + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = while_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } - if (while_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); - } + if (while_node.continue_expr) |continue_expr| { + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = continue_expr }); + try stack.push(RenderState { .Text = ": (" }); + try stack.push(RenderState { .Text = " " }); + } - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = while_node.condition }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.For => { - const for_node = @fieldParentPtr(ast.Node.For, "base", base); - if (for_node.label) |label| { - try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); - } + if (while_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } - if (for_node.inline_token) |inline_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token)); - } + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = while_node.condition }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.For => { + const for_node = @fieldParentPtr(ast.Node.For, "base", base); + if (for_node.label) |label| { + try stream.print("{}: ", tree.tokenSlice(label)); + } - try stream.print("{} ", self.tokenizer.getTokenSlice(for_node.for_token)); + if (for_node.inline_token) |inline_token| { + try stream.print("{} ", tree.tokenSlice(inline_token)); + } - if (for_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); + try stream.print("{} ", tree.tokenSlice(for_node.for_token)); - if (for_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } + if (for_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); if (for_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Expression = for_node.body }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = " " }); } else { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = for_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - } - - if (for_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); } + } - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = for_node.array_expr }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.If => { - const if_node = @fieldParentPtr(ast.Node.If, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token)); - - switch (if_node.body.id) { - ast.Node.Id.Block, ast.Node.Id.If, - ast.Node.Id.For, ast.Node.Id.While, - ast.Node.Id.Switch => { - if (if_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = &@"else".base }); - - if (if_node.body.id == ast.Node.Id.Block) { - try stack.append(RenderState { .Text = " " }); - } else { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Text = "\n" }); - } - } - }, - else => { - if (if_node.@"else") |@"else"| { - try stack.append(RenderState { .Expression = @"else".body }); + if (for_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Expression = for_node.body }); + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Expression = for_node.body }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + } - if (@"else".payload) |payload| { - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Expression = payload }); - } + if (for_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } - try stack.append(RenderState { .Text = " " }); - try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(@"else".else_token) }); - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = for_node.array_expr }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.If => { + const if_node = @fieldParentPtr(ast.Node.If, "base", base); + try stream.print("{} ", tree.tokenSlice(if_node.if_token)); + + switch (if_node.body.id) { + ast.Node.Id.Block, ast.Node.Id.If, + ast.Node.Id.For, ast.Node.Id.While, + ast.Node.Id.Switch => { + if (if_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = &@"else".base }); + + if (if_node.body.id == ast.Node.Id.Block) { + try stack.push(RenderState { .Text = " " }); + } else { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Text = "\n" }); } } - } + }, + else => { + if (if_node.@"else") |@"else"| { + try stack.push(RenderState { .Expression = @"else".body }); - if (if_node.condition.same_line_comment) |comment| { - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Expression = if_node.body }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - try stack.append(RenderState { .PrintLineComment = comment }); - } else { - try stack.append(RenderState { .Expression = if_node.body }); - } + if (@"else".payload) |payload| { + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = payload }); + } + try stack.push(RenderState { .Text = " " }); + try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) }); + try stack.push(RenderState { .Text = " " }); + } + } + } - try stack.append(RenderState { .Text = " " }); + try stack.push(RenderState { .Expression = if_node.body }); + try stack.push(RenderState { .Text = " " }); - if (if_node.payload) |payload| { - try stack.append(RenderState { .Expression = payload }); - try stack.append(RenderState { .Text = " " }); - } + if (if_node.payload) |payload| { + try stack.push(RenderState { .Expression = payload }); + try stack.push(RenderState { .Text = " " }); + } - try stack.append(RenderState { .Text = ")" }); - try stack.append(RenderState { .Expression = if_node.condition }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.Asm => { - const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); - try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token)); + try stack.push(RenderState { .Text = ")" }); + try stack.push(RenderState { .Expression = if_node.condition }); + try stack.push(RenderState { .Text = "(" }); + }, + ast.Node.Id.Asm => { + const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base); + try stream.print("{} ", tree.tokenSlice(asm_node.asm_token)); - if (asm_node.volatile_token) |volatile_token| { - try stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token)); - } + if (asm_node.volatile_token) |volatile_token| { + try stream.print("{} ", tree.tokenSlice(volatile_token)); + } - try stack.append(RenderState { .Indent = indent }); - try stack.append(RenderState { .Text = ")" }); - { - const cloppers = asm_node.cloppers.toSliceConst(); - var i = cloppers.len; - while (i != 0) { - i -= 1; - try stack.append(RenderState { .Expression = cloppers[i] }); + try stack.push(RenderState { .Indent = indent }); + try stack.push(RenderState { .Text = ")" }); + { + var i = asm_node.clobbers.len; + while (i != 0) { + i -= 1; + try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) }); - if (i != 0) { - try stack.append(RenderState { .Text = ", " }); - } + if (i != 0) { + try stack.push(RenderState { .Text = ", " }); } } - try stack.append(RenderState { .Text = ": " }); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta }); - try stack.append(RenderState { .Text = "\n" }); - { - const inputs = asm_node.inputs.toSliceConst(); - var i = inputs.len; - while (i != 0) { - i -= 1; - const node = inputs[i]; - try stack.append(RenderState { .Expression = &node.base}); + } + try stack.push(RenderState { .Text = ": " }); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta }); + try stack.push(RenderState { .Text = "\n" }); + { + var i = asm_node.inputs.len; + while (i != 0) { + i -= 1; + const node = *asm_node.inputs.at(i); + try stack.push(RenderState { .Expression = &node.base}); - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - const prev_node = inputs[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.append(RenderState { .Text = "," }); - } + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + const prev_node = *asm_node.inputs.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + try stack.push(RenderState { .Text = "," }); } } - try stack.append(RenderState { .Indent = indent + indent_delta + 2}); - try stack.append(RenderState { .Text = ": "}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "\n" }); - { - const outputs = asm_node.outputs.toSliceConst(); - var i = outputs.len; - while (i != 0) { - i -= 1; - const node = outputs[i]; - try stack.append(RenderState { .Expression = &node.base}); + } + try stack.push(RenderState { .Indent = indent + indent_delta + 2}); + try stack.push(RenderState { .Text = ": "}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "\n" }); + { + var i = asm_node.outputs.len; + while (i != 0) { + i -= 1; + const node = *asm_node.outputs.at(i); + try stack.push(RenderState { .Expression = &node.base}); - if (i != 0) { - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { - .Text = blk: { - const prev_node = outputs[i - 1]; - const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); - if (loc.line >= 2) { - break :blk "\n\n"; - } - break :blk "\n"; - }, - }); - try stack.append(RenderState { .Text = "," }); - } + if (i != 0) { + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { + .Text = blk: { + const prev_node = *asm_node.outputs.at(i - 1); + const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end; + const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken()); + if (loc.line >= 2) { + break :blk "\n\n"; + } + break :blk "\n"; + }, + }); + try stack.push(RenderState { .Text = "," }); } } - try stack.append(RenderState { .Indent = indent + indent_delta + 2}); - try stack.append(RenderState { .Text = ": "}); - try stack.append(RenderState.PrintIndent); - try stack.append(RenderState { .Indent = indent + indent_delta}); - try stack.append(RenderState { .Text = "\n" }); - try stack.append(RenderState { .Expression = asm_node.template }); - try stack.append(RenderState { .Text = "(" }); - }, - ast.Node.Id.AsmInput => { - const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); - - try stack.append(RenderState { .Text = ")"}); - try stack.append(RenderState { .Expression = asm_input.expr}); - try stack.append(RenderState { .Text = " ("}); - try stack.append(RenderState { .Expression = asm_input.constraint }); - try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = asm_input.symbolic_name }); - try stack.append(RenderState { .Text = "["}); - }, - ast.Node.Id.AsmOutput => { - const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); - - try stack.append(RenderState { .Text = ")"}); - switch (asm_output.kind) { - ast.Node.AsmOutput.Kind.Variable => |variable_name| { - try stack.append(RenderState { .Expression = &variable_name.base}); - }, - ast.Node.AsmOutput.Kind.Return => |return_type| { - try stack.append(RenderState { .Expression = return_type}); - try stack.append(RenderState { .Text = "-> "}); - }, - } - try stack.append(RenderState { .Text = " ("}); - try stack.append(RenderState { .Expression = asm_output.constraint }); - try stack.append(RenderState { .Text = "] "}); - try stack.append(RenderState { .Expression = asm_output.symbolic_name }); - try stack.append(RenderState { .Text = "["}); - }, - - ast.Node.Id.StructField, - ast.Node.Id.UnionTag, - ast.Node.Id.EnumTag, - ast.Node.Id.ErrorTag, - ast.Node.Id.Root, - ast.Node.Id.VarDecl, - ast.Node.Id.Use, - ast.Node.Id.TestDecl, - ast.Node.Id.ParamDecl => unreachable, + } + try stack.push(RenderState { .Indent = indent + indent_delta + 2}); + try stack.push(RenderState { .Text = ": "}); + try stack.push(RenderState.PrintIndent); + try stack.push(RenderState { .Indent = indent + indent_delta}); + try stack.push(RenderState { .Text = "\n" }); + try stack.push(RenderState { .Expression = asm_node.template }); + try stack.push(RenderState { .Text = "(" }); }, - RenderState.Statement => |base| { - try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } ); - switch (base.id) { - ast.Node.Id.VarDecl => { - const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); - try stack.append(RenderState { .VarDecl = var_decl}); + ast.Node.Id.AsmInput => { + const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base); + + try stack.push(RenderState { .Text = ")"}); + try stack.push(RenderState { .Expression = asm_input.expr}); + try stack.push(RenderState { .Text = " ("}); + try stack.push(RenderState { .Expression = asm_input.constraint }); + try stack.push(RenderState { .Text = "] "}); + try stack.push(RenderState { .Expression = asm_input.symbolic_name }); + try stack.push(RenderState { .Text = "["}); + }, + ast.Node.Id.AsmOutput => { + const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base); + + try stack.push(RenderState { .Text = ")"}); + switch (asm_output.kind) { + ast.Node.AsmOutput.Kind.Variable => |variable_name| { + try stack.push(RenderState { .Expression = &variable_name.base}); }, - else => { - if (requireSemiColon(base)) { - try stack.append(RenderState { .Text = ";" }); - } - try stack.append(RenderState { .Expression = base }); + ast.Node.AsmOutput.Kind.Return => |return_type| { + try stack.push(RenderState { .Expression = return_type}); + try stack.push(RenderState { .Text = "-> "}); }, } + try stack.push(RenderState { .Text = " ("}); + try stack.push(RenderState { .Expression = asm_output.constraint }); + try stack.push(RenderState { .Text = "] "}); + try stack.push(RenderState { .Expression = asm_output.symbolic_name }); + try stack.push(RenderState { .Text = "["}); }, - RenderState.Indent => |new_indent| indent = new_indent, - RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), - RenderState.PrintSameLineComment => |maybe_comment| blk: { - const comment_token = maybe_comment ?? break :blk; - try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); - }, - RenderState.PrintLineComment => |comment_token| { - try stream.write(self.tokenizer.getTokenSlice(comment_token)); - }, - } - } - } - fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { - const comment = node.doc_comments ?? return; - for (comment.lines.toSliceConst()) |line_token| { - try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); - try stream.writeByteNTimes(' ', indent); + ast.Node.Id.StructField, + ast.Node.Id.UnionTag, + ast.Node.Id.EnumTag, + ast.Node.Id.ErrorTag, + ast.Node.Id.Root, + ast.Node.Id.VarDecl, + ast.Node.Id.Use, + ast.Node.Id.TestDecl, + ast.Node.Id.ParamDecl => unreachable, + }, + RenderState.Statement => |base| { + switch (base.id) { + ast.Node.Id.VarDecl => { + const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); + try stack.push(RenderState { .VarDecl = var_decl}); + }, + else => { + if (requireSemiColon(base)) { + try stack.push(RenderState { .Text = ";" }); + } + try stack.push(RenderState { .Expression = base }); + }, + } + }, + RenderState.Indent => |new_indent| indent = new_indent, + RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), } } +} - fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) { - const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T); - self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count); - const typed_slice = ([]T)(self.utility_bytes); - return ArrayList(T) { - .allocator = self.util_allocator, - .items = typed_slice, - .len = 0, - }; - } - - fn deinitUtilityArrayList(self: &Parser, list: var) void { - self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items); +fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void { + const comment = node.doc_comments ?? return; + var it = comment.lines.iterator(0); + while (it.next()) |line_token_index| { + try stream.print("{}\n", tree.tokenSlice(*line_token_index)); + try stream.writeByteNTimes(' ', indent); } - -}; +} test "std.zig.parser" { _ = @import("parser_test.zig"); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index e1d75d8380..dd20a6dd8e 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,14 +1,12 @@ -test "zig fmt: same-line comment after non-block if expression" { - try testCanonical( - \\comptime { - \\ if (sr > n_uword_bits - 1) { - \\ // d > r - \\ return 0; - \\ } - \\} - \\ - ); -} +//test "zig fmt: same-line comment after non-block if expression" { +// try testCanonical( +// \\comptime { +// \\ if (sr > n_uword_bits - 1) // d > r +// \\ return 0; +// \\} +// \\ +// ); +//} test "zig fmt: switch with empty body" { try testCanonical( @@ -19,14 +17,14 @@ test "zig fmt: switch with empty body" { ); } -test "zig fmt: same-line comment on comptime expression" { - try testCanonical( - \\test "" { - \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt - \\} - \\ - ); -} +//test "zig fmt: same-line comment on comptime expression" { +// try testCanonical( +// \\test "" { +// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt +// \\} +// \\ +// ); +//} test "zig fmt: float literal with exponent" { try testCanonical( @@ -154,17 +152,17 @@ test "zig fmt: comments before switch prong" { ); } -test "zig fmt: same-line comment after switch prong" { - try testCanonical( - \\test "" { - \\ switch (err) { - \\ error.PathAlreadyExists => {}, // comment 2 - \\ else => return err, // comment 1 - \\ } - \\} - \\ - ); -} +//test "zig fmt: same-line comment after switch prong" { +// try testCanonical( +// \\test "" { +// \\ switch (err) { +// \\ error.PathAlreadyExists => {}, // comment 2 +// \\ else => return err, // comment 1 +// \\ } +// \\} +// \\ +// ); +//} test "zig fmt: comments before var decl in struct" { try testCanonical( @@ -191,27 +189,27 @@ test "zig fmt: comments before var decl in struct" { ); } -test "zig fmt: same-line comment after var decl in struct" { - try testCanonical( - \\pub const vfs_cap_data = extern struct { - \\ const Data = struct {}; // when on disk. - \\}; - \\ - ); -} - -test "zig fmt: same-line comment after field decl" { - try testCanonical( - \\pub const dirent = extern struct { - \\ d_name: u8, - \\ d_name: u8, // comment 1 - \\ d_name: u8, - \\ d_name: u8, // comment 2 - \\ d_name: u8, - \\}; - \\ - ); -} +//test "zig fmt: same-line comment after var decl in struct" { +// try testCanonical( +// \\pub const vfs_cap_data = extern struct { +// \\ const Data = struct {}; // when on disk. +// \\}; +// \\ +// ); +//} +// +//test "zig fmt: same-line comment after field decl" { +// try testCanonical( +// \\pub const dirent = extern struct { +// \\ d_name: u8, +// \\ d_name: u8, // comment 1 +// \\ d_name: u8, +// \\ d_name: u8, // comment 2 +// \\ d_name: u8, +// \\}; +// \\ +// ); +//} test "zig fmt: array literal with 1 item on 1 line" { try testCanonical( @@ -220,16 +218,16 @@ test "zig fmt: array literal with 1 item on 1 line" { ); } -test "zig fmt: same-line comment after a statement" { - try testCanonical( - \\test "" { - \\ a = b; - \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption - \\ a = b; - \\} - \\ - ); -} +//test "zig fmt: same-line comment after a statement" { +// try testCanonical( +// \\test "" { +// \\ a = b; +// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption +// \\ a = b; +// \\} +// \\ +// ); +//} test "zig fmt: comments before global variables" { try testCanonical( @@ -1094,25 +1092,48 @@ test "zig fmt: error return" { const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; -const Tokenizer = std.zig.Tokenizer; -const Parser = std.zig.Parser; const io = std.io; var fixed_buffer_mem: [100 * 1024]u8 = undefined; fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { - var tokenizer = Tokenizer.init(source); - var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); - defer parser.deinit(); + var stderr_file = try io.getStdErr(); + var stderr = &io.FileOutStream.init(&stderr_file).stream; - var tree = try parser.parse(); + var tree = try std.zig.parse(allocator, source); defer tree.deinit(); + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + const token = tree.tokens.at(parse_error.loc()); + const loc = tree.tokenLocation(0, parse_error.loc()); + try stderr.print("(memory buffer):{}:{}: error: ", loc.line + 1, loc.column + 1); + try tree.renderError(parse_error, stderr); + try stderr.print("\n{}\n", source[loc.line_start..loc.line_end]); + { + var i: usize = 0; + while (i < loc.column) : (i += 1) { + try stderr.write(" "); + } + } + { + const caret_count = token.end - token.start; + var i: usize = 0; + while (i < caret_count) : (i += 1) { + try stderr.write("~"); + } + } + try stderr.write("\n"); + } + if (tree.errors.len != 0) { + return error.ParseError; + } + var buffer = try std.Buffer.initSize(allocator, 0); errdefer buffer.deinit(); var buffer_out_stream = io.BufferOutStream.init(&buffer); - try parser.renderSource(&buffer_out_stream.stream, tree.root_node); + try std.zig.render(allocator, &buffer_out_stream.stream, &tree); return buffer.toOwnedSlice(); } @@ -1151,6 +1172,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { } }, error.ParseError => @panic("test failed"), + else => @panic("test failed"), } } } diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 31dc06b695..b0e5014a1a 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -195,37 +195,6 @@ pub const Tokenizer = struct { index: usize, pending_invalid_token: ?Token, - pub const Location = struct { - line: usize, - column: usize, - line_start: usize, - line_end: usize, - }; - - pub fn getTokenLocation(self: &Tokenizer, start_index: usize, token: &const Token) Location { - var loc = Location { - .line = 0, - .column = 0, - .line_start = start_index, - .line_end = self.buffer.len, - }; - for (self.buffer[start_index..]) |c, i| { - if (i + start_index == token.start) { - loc.line_end = i + start_index; - while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {} - return loc; - } - if (c == '\n') { - loc.line += 1; - loc.column = 0; - loc.line_start = i + 1; - } else { - loc.column += 1; - } - } - return loc; - } - /// For debugging purposes pub fn dump(self: &Tokenizer, token: &const Token) void { std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]); @@ -1047,10 +1016,6 @@ pub const Tokenizer = struct { return result; } - pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 { - return self.buffer[token.start..token.end]; - } - fn checkLiteralCharacter(self: &Tokenizer) void { if (self.pending_invalid_token != null) return; const invalid_length = self.getInvalidCharacterLength(); -- cgit v1.2.3 From 9b29c872ce1836743b64c37db5272a7d7893f474 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 9 May 2018 09:34:04 +0200 Subject: Added Slice as it's own type info in userland --- src/analyze.cpp | 6 ++-- src/analyze.hpp | 2 +- src/codegen.cpp | 4 +++ src/ir.cpp | 80 ++++++++++++++++++++++++++++-------------------- test/cases/type_info.zig | 25 +++++++++++++-- 5 files changed, 78 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 0f2fdf15de..590c946f7e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5931,8 +5931,8 @@ size_t type_id_len() { return array_length(all_type_ids); } -size_t type_id_index(TypeTableEntryId id) { - switch (id) { +size_t type_id_index(TypeTableEntry *entry) { + switch (entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: @@ -5952,6 +5952,8 @@ size_t type_id_index(TypeTableEntryId id) { case TypeTableEntryIdArray: return 7; case TypeTableEntryIdStruct: + if (entry->data.structure.is_slice) + return 25; return 8; case TypeTableEntryIdNumLitFloat: return 9; diff --git a/src/analyze.hpp b/src/analyze.hpp index aca78f4e25..56ca21a93f 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -174,7 +174,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value); const char *type_id_name(TypeTableEntryId id); TypeTableEntryId type_id_at_index(size_t index); size_t type_id_len(); -size_t type_id_index(TypeTableEntryId id); +size_t type_id_index(TypeTableEntry *entry); TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry); LinkLib *create_link_lib(Buf *name); diff --git a/src/codegen.cpp b/src/codegen.cpp index db69708e9a..4e58f86d4b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6345,6 +6345,7 @@ static void define_builtin_compile_vars(CodeGen *g) { const TypeTableEntryId id = type_id_at_index(i); buf_appendf(contents, " %s,\n", type_id_name(id)); } + buf_appendf(contents, " Slice,\n"); buf_appendf(contents, "};\n\n"); } { @@ -6357,6 +6358,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Int: Int,\n" " Float: Float,\n" " Pointer: Pointer,\n" + " Slice: Slice,\n" " Array: Array,\n" " Struct: Struct,\n" " FloatLiteral: void,\n" @@ -6392,6 +6394,8 @@ static void define_builtin_compile_vars(CodeGen *g) { " child: type,\n" " };\n" "\n" + " pub const Slice = Pointer;\n" + "\n" " pub const Array = struct {\n" " len: usize,\n" " child: type,\n" diff --git a/src/ir.cpp b/src/ir.cpp index cdf56f7fee..035e27707a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15785,11 +15785,10 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na Buf field_name = BUF_INIT; buf_init_from_str(&field_name, type_name); - auto entry = type_info_scope->decl_table.maybe_get(&field_name); + auto entry = type_info_scope->decl_table.get(&field_name); buf_deinit(&field_name); - assert(entry != nullptr); - TldVar *tld = (TldVar *)entry->value; + TldVar *tld = (TldVar *)entry; assert(tld->base.id == TldIdVar); VariableTableEntry *var = tld->var; @@ -16071,6 +16070,38 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t enum_field_val->data.x_struct.fields = inner_fields; }; + const auto create_ptr_like_type_info = [ira](const char *name, TypeTableEntry *ptr_type_entry) { + ConstExprValue *result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, name); + + ConstExprValue *fields = create_const_vals(4); + result->data.x_struct.fields = fields; + + // is_const: bool + ensure_field_index(result->type, "is_const", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_bool; + fields[0].data.x_bool = ptr_type_entry->data.pointer.is_const; + // is_volatile: bool + ensure_field_index(result->type, "is_volatile", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_bool; + fields[1].data.x_bool = ptr_type_entry->data.pointer.is_volatile; + // alignment: u32 + ensure_field_index(result->type, "alignment", 2); + fields[2].special = ConstValSpecialStatic; + fields[2].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[2].data.x_bigint, ptr_type_entry->data.pointer.alignment); + // child: type + ensure_field_index(result->type, "child", 3); + fields[3].special = ConstValSpecialStatic; + fields[3].type = ira->codegen->builtin_types.entry_type; + fields[3].data.x_type = ptr_type_entry->data.pointer.child_type; + + return result; + }; + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -16139,34 +16170,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdPointer: { - result = create_const_vals(1); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Pointer"); - - ConstExprValue *fields = create_const_vals(4); - result->data.x_struct.fields = fields; - - // is_const: bool - ensure_field_index(result->type, "is_const", 0); - fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_bool; - fields[0].data.x_bool = type_entry->data.pointer.is_const; - // is_volatile: bool - ensure_field_index(result->type, "is_volatile", 1); - fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_bool; - fields[1].data.x_bool = type_entry->data.pointer.is_volatile; - // alignment: u32 - ensure_field_index(result->type, "alignment", 2); - fields[2].special = ConstValSpecialStatic; - fields[2].type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields[2].data.x_bigint, type_entry->data.pointer.alignment); - // child: type - ensure_field_index(result->type, "child", 3); - fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_type; - fields[3].data.x_type = type_entry->data.pointer.child_type; - + result = create_ptr_like_type_info("Pointer", type_entry); break; } case TypeTableEntryIdArray: @@ -16436,6 +16440,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdStruct: { + if (type_entry->data.structure.is_slice) { + Buf ptr_field_name = BUF_INIT; + buf_init_from_str(&ptr_field_name, "ptr"); + TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; + ensure_complete_type(ira->codegen, ptr_type); + + result = create_ptr_like_type_info("Slice", ptr_type); + break; + } + result = create_const_vals(1); result->special = ConstValSpecialStatic; result->type = ir_type_info_get_type(ira, "Struct"); @@ -16622,7 +16636,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->type = result_type; - bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry->id)); + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); ConstExprValue *payload = ir_make_type_info_value(ira, type_entry); out_val->data.x_union.payload = payload; @@ -16650,7 +16664,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, TypeTableEntry *result_type = var_value->data.x_type; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry->id)); + bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry)); return result_type; } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index c9b15157e8..f10703e3ee 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -25,7 +25,7 @@ test "type info: integer, floating point type info" { } } -test "type info: pointer, array and nullable type info" { +test "type info: pointer type info" { comptime { const u32_ptr_info = @typeInfo(&u32); assert(TypeId(u32_ptr_info) == TypeId.Pointer); @@ -33,12 +33,31 @@ test "type info: pointer, array and nullable type info" { assert(u32_ptr_info.Pointer.is_volatile == false); assert(u32_ptr_info.Pointer.alignment == 4); assert(u32_ptr_info.Pointer.child == u32); + } +} + +test "type info: slice type info" { + comptime { + const u32_slice_info = @typeInfo([]u32); + assert(TypeId(u32_slice_info) == TypeId.Slice); + assert(u32_slice_info.Slice.is_const == false); + assert(u32_slice_info.Slice.is_volatile == false); + assert(u32_slice_info.Slice.alignment == 4); + assert(u32_slice_info.Slice.child == u32); + } +} +test "type info: array type info" { + comptime { const arr_info = @typeInfo([42]bool); assert(TypeId(arr_info) == TypeId.Array); assert(arr_info.Array.len == 42); assert(arr_info.Array.child == bool); + } +} +test "type info: nullable type info" { + comptime { const null_info = @typeInfo(?void); assert(TypeId(null_info) == TypeId.Nullable); assert(null_info.Nullable.child == void); @@ -100,11 +119,11 @@ test "type info: union info" { assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(typeinfo_info.Union.tag_type == TypeId); - assert(typeinfo_info.Union.fields.len == 25); + assert(typeinfo_info.Union.fields.len == 26); assert(typeinfo_info.Union.fields[4].enum_field != null); assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 20); + assert(typeinfo_info.Union.defs.len == 21); const TestNoTagUnion = union { Foo: void, -- cgit v1.2.3 From 2a74aa206781b56d3aae5c0e8a94c75f0d73ac51 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Wed, 9 May 2018 09:40:57 +0200 Subject: Freeing ptr_field_name after use --- src/ir.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 035e27707a..777e219a46 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16445,6 +16445,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t buf_init_from_str(&ptr_field_name, "ptr"); TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; ensure_complete_type(ira->codegen, ptr_type); + buf_deinit(&ptr_field_name); result = create_ptr_like_type_info("Slice", ptr_type); break; -- cgit v1.2.3 From bf21747a426887ed2ac866c9a9d317f64b22da79 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 9 May 2018 20:23:36 -0400 Subject: translate-c: fix typedef duplicate definition of variable closes #998 --- src/translate_c.cpp | 8 ++++++-- test/translate_c.zig | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/translate_c.cpp b/src/translate_c.cpp index c0a76b8969..608d717b16 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3667,6 +3667,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ if (existing_entry) { return existing_entry->value; } + QualType child_qt = typedef_decl->getUnderlyingType(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); @@ -3700,16 +3701,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ // use the name of this typedef // TODO + // trans_qual_type here might cause us to look at this typedef again so we put the item in the map first + AstNode *symbol_node = trans_create_node_symbol(c, type_name); + c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node); + AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation()); if (type_node == nullptr) { emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name)); c->decl_table.put(typedef_decl, nullptr); + // TODO add global var with type_name equal to @compileError("unable to resolve C type") return nullptr; } add_global_var(c, type_name, type_node); - AstNode *symbol_node = trans_create_node_symbol(c, type_name); - c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node); return symbol_node; } diff --git a/test/translate_c.zig b/test/translate_c.zig index a5b5f3ae2a..2cd59f6f75 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.TranslateCContext) void { + cases.add("double define struct", + \\typedef struct Bar Bar; + \\typedef struct Foo Foo; + \\ + \\struct Foo { + \\ Foo *a; + \\}; + \\ + \\struct Bar { + \\ Foo *a; + \\}; + , + \\pub const struct_Foo = extern struct { + \\ a: ?&Foo, + \\}; + \\pub const Foo = struct_Foo; + \\pub const struct_Bar = extern struct { + \\ a: ?&Foo, + \\}; + ); + cases.addAllowWarnings("simple data types", \\#include \\int foo(char a, unsigned char b, signed char c); -- cgit v1.2.3 From efa39c5343e13a13e65210f55da5df23ee3feb3e Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 10 May 2018 22:26:26 +1200 Subject: Fix bigint shift-right partial shift --- src/bigint.cpp | 2 +- test/cases/math.zig | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/bigint.cpp b/src/bigint.cpp index 64bc59e5cf..367ae79b6c 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1425,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { uint64_t digit = op1_digits[op_digit_index]; size_t dest_digit_index = op_digit_index - digit_shift_count; dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = digit << leftover_shift_count; + carry = digit << (64 - leftover_shift_count); if (dest_digit_index == 0) { break; } op_digit_index -= 1; diff --git a/test/cases/math.zig b/test/cases/math.zig index 3c33b14fbf..13704ecd4b 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -366,6 +366,14 @@ test "big number multi-limb shift and mask" { } } +test "big number multi-limb partial shift right" { + comptime { + var a = 0x1ffffffffeeeeeeee; + a >>= 16; + assert(a == 0x1ffffffffeeee); + } +} + test "xor" { test_xor(); comptime test_xor(); -- cgit v1.2.3 From 277b9cf8788f340f387e63029ad9fc12664cafff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 11 May 2018 22:41:44 -0400 Subject: fix comptime code modification of global const closes #1008 --- src/ir.cpp | 7 ++++++- test/cases/eval.zig | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 1e6a7d7b8b..c251f30320 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8686,6 +8686,10 @@ static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_ *dest = *src; if (!same_global_refs) { dest->global_refs = global_refs; + if (dest->type->id == TypeTableEntryIdStruct) { + dest->data.x_struct.fields = allocate_nonzero(dest->type->data.structure.src_field_count); + memcpy(dest->data.x_struct.fields, src->data.x_struct.fields, sizeof(ConstExprValue) * dest->type->data.structure.src_field_count); + } } } @@ -11670,7 +11674,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc if (var->mem_slot_index != SIZE_MAX) { assert(var->mem_slot_index < ira->exec_context.mem_slot_count); ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; - *mem_slot = casted_init_value->value; + copy_const_val(mem_slot, &casted_init_value->value, + !is_comptime_var || var->gen_is_const); if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { ir_build_const_from(ira, &decl_var_instruction->base); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 364db5e152..1ed30872e0 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -536,3 +536,20 @@ test "runtime 128 bit integer division" { var c = a / b; assert(c == 15231399999); } + +pub const Info = struct { + version: u8, +}; + +pub const diamond_info = Info { + .version = 0, +}; + +test "comptime modification of const struct field" { + comptime { + var res = diamond_info; + res.version = 1; + assert(diamond_info.version == 0); + assert(res.version == 1); + } +} -- cgit v1.2.3 From a6ae45145f5814963cfdff4e18c1f984729588b9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 May 2018 17:35:15 -0400 Subject: add @newStackCall builtin function See #1006 --- doc/langref.html.in | 47 ++++++++++++++++++-- src/all_types.hpp | 7 +++ src/codegen.cpp | 99 ++++++++++++++++++++++++++++++++++++++++++- src/ir.cpp | 66 +++++++++++++++++++++++++---- src/target.cpp | 62 +++++++++++++++++++++++++++ src/target.hpp | 2 + test/behavior.zig | 5 ++- test/cases/new_stack_call.zig | 26 ++++++++++++ 8 files changed, 298 insertions(+), 16 deletions(-) create mode 100644 test/cases/new_stack_call.zig (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index b867ff0b35..4ae98abbd2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4485,17 +4485,58 @@ mem.set(u8, dest, c); If no overflow or underflow occurs, returns false.

{#header_close#} + {#header_open|@newStackCall#} +
@newStackCall(new_stack: []u8, function: var, args: ...) -> var
+

+ This calls a function, in the same way that invoking an expression with parentheses does. However, + instead of using the same stack as the caller, the function uses the stack provided in the new_stack + parameter. +

+ {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +var new_stack_bytes: [1024]u8 = undefined; + +test "calling a function with a new stack" { + const arg = 1234; + + const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); + const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); + _ = targetFunction(arg); + + assert(arg == 1234); + assert(a < b); +} + +fn targetFunction(x: i32) usize { + assert(x == 1234); + + var local_variable: i32 = 42; + const ptr = &local_variable; + *ptr += 1; + + assert(local_variable == 43); + return @ptrToInt(ptr); +} + {#code_end#} + {#header_close#} {#header_open|@noInlineCall#}
@noInlineCall(function: var, args: ...) -> var

This calls a function, in the same way that invoking an expression with parentheses does:

-
const assert = @import("std").debug.assert;
+      {#code_begin|test#}
+const assert = @import("std").debug.assert;
+
 test "noinline function call" {
     assert(@noInlineCall(add, 3, 9) == 12);
 }
 
-fn add(a: i32, b: i32) -> i32 { a + b }
+fn add(a: i32, b: i32) i32 { + return a + b; +} + {#code_end#}

Unlike a normal function call, however, @noInlineCall guarantees that the call will not be inlined. If the call must be inlined, a compile error is emitted. @@ -6451,7 +6492,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index c1c6c9a1a5..dc61c8235f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1340,6 +1340,7 @@ enum BuiltinFnId { BuiltinFnIdOffsetOf, BuiltinFnIdInlineCall, BuiltinFnIdNoInlineCall, + BuiltinFnIdNewStackCall, BuiltinFnIdTypeId, BuiltinFnIdShlExact, BuiltinFnIdShrExact, @@ -1656,8 +1657,13 @@ struct CodeGen { LLVMValueRef coro_alloc_helper_fn_val; LLVMValueRef merge_err_ret_traces_fn_val; LLVMValueRef add_error_return_trace_addr_fn_val; + LLVMValueRef stacksave_fn_val; + LLVMValueRef stackrestore_fn_val; + LLVMValueRef write_register_fn_val; bool error_during_imports; + LLVMValueRef sp_md_node; + const char **clang_argv; size_t clang_argv_len; ZigList lib_dirs; @@ -2280,6 +2286,7 @@ struct IrInstructionCall { bool is_async; IrInstruction *async_allocator; + IrInstruction *new_stack; }; struct IrInstructionConst { diff --git a/src/codegen.cpp b/src/codegen.cpp index 4e58f86d4b..f1e102392a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -938,6 +938,53 @@ static LLVMValueRef get_memcpy_fn_val(CodeGen *g) { return g->memcpy_fn_val; } +static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { + if (g->stacksave_fn_val) + return g->stacksave_fn_val; + + // declare i8* @llvm.stacksave() + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMPointerType(LLVMInt8Type(), 0), nullptr, 0, false); + g->stacksave_fn_val = LLVMAddFunction(g->module, "llvm.stacksave", fn_type); + assert(LLVMGetIntrinsicID(g->stacksave_fn_val)); + + return g->stacksave_fn_val; +} + +static LLVMValueRef get_stackrestore_fn_val(CodeGen *g) { + if (g->stackrestore_fn_val) + return g->stackrestore_fn_val; + + // declare void @llvm.stackrestore(i8* %ptr) + + LLVMTypeRef param_type = LLVMPointerType(LLVMInt8Type(), 0); + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), ¶m_type, 1, false); + g->stackrestore_fn_val = LLVMAddFunction(g->module, "llvm.stackrestore", fn_type); + assert(LLVMGetIntrinsicID(g->stackrestore_fn_val)); + + return g->stackrestore_fn_val; +} + +static LLVMValueRef get_write_register_fn_val(CodeGen *g) { + if (g->write_register_fn_val) + return g->write_register_fn_val; + + // declare void @llvm.write_register.i64(metadata, i64 @value) + // !0 = !{!"sp\00"} + + LLVMTypeRef param_types[] = { + LLVMMetadataTypeInContext(LLVMGetGlobalContext()), + LLVMIntType(g->pointer_size_bytes * 8), + }; + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); + Buf *name = buf_sprintf("llvm.write_register.i%d", g->pointer_size_bytes * 8); + g->write_register_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); + assert(LLVMGetIntrinsicID(g->write_register_fn_val)); + + return g->write_register_fn_val; +} + static LLVMValueRef get_coro_destroy_fn_val(CodeGen *g) { if (g->coro_destroy_fn_val) return g->coro_destroy_fn_val; @@ -2901,6 +2948,38 @@ static size_t get_async_err_code_arg_index(CodeGen *g, FnTypeId *fn_type_id) { return 1 + get_async_allocator_arg_index(g, fn_type_id); } + +static LLVMValueRef get_new_stack_addr(CodeGen *g, LLVMValueRef new_stack) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_ptr_index, ""); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_len_index, ""); + + LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); + LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); + + LLVMValueRef ptr_addr = LLVMBuildPtrToInt(g->builder, ptr_value, LLVMTypeOf(len_value), ""); + LLVMValueRef end_addr = LLVMBuildNUWAdd(g->builder, ptr_addr, len_value, ""); + LLVMValueRef align_amt = LLVMConstInt(LLVMTypeOf(end_addr), get_abi_alignment(g, g->builtin_types.entry_usize), false); + LLVMValueRef align_adj = LLVMBuildURem(g->builder, end_addr, align_amt, ""); + return LLVMBuildNUWSub(g->builder, end_addr, align_adj, ""); +} + +static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { + LLVMValueRef write_register_fn_val = get_write_register_fn_val(g); + + if (g->sp_md_node == nullptr) { + Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(&g->zig_target.arch)); + LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1); + g->sp_md_node = LLVMMDNode(&str_node, 1); + } + + LLVMValueRef params[] = { + g->sp_md_node, + aligned_end_addr, + }; + + LLVMBuildCall(g->builder, write_register_fn_val, params, 2, ""); +} + static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { LLVMValueRef fn_val; TypeTableEntry *fn_type; @@ -2967,8 +3046,23 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc); - LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + LLVMValueRef result; + + if (instruction->new_stack == nullptr) { + result = ZigLLVMBuildCall(g->builder, fn_val, + gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + } else { + LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); + LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); + + LLVMValueRef new_stack_addr = get_new_stack_addr(g, ir_llvm_value(g, instruction->new_stack)); + LLVMValueRef old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); + gen_set_stack_pointer(g, new_stack_addr); + result = ZigLLVMBuildCall(g->builder, fn_val, + gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); + } + for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; @@ -6171,6 +6265,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); + create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); diff --git a/src/ir.cpp b/src/ir.cpp index c251f30320..7bc837d908 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1102,7 +1102,8 @@ static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstructio static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator) + bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, + IrInstruction *new_stack) { IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node); call_instruction->fn_entry = fn_entry; @@ -1113,6 +1114,7 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->arg_count = arg_count; call_instruction->is_async = is_async; call_instruction->async_allocator = async_allocator; + call_instruction->new_stack = new_stack; if (fn_ref) ir_ref_instruction(fn_ref, irb->current_basic_block); @@ -1120,16 +1122,19 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc ir_ref_instruction(args[i], irb->current_basic_block); if (async_allocator) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) + ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction, FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator) + bool is_comptime, FnInline fn_inline, bool is_async, IrInstruction *async_allocator, + IrInstruction *new_stack) { IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope, - old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline, is_async, async_allocator); + old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline, is_async, async_allocator, new_stack); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } @@ -4303,7 +4308,37 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; - IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr); + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr); + return ir_lval_wrap(irb, scope, call, lval); + } + case BuiltinFnIdNewStackCall: + { + if (node->data.fn_call_expr.params.length == 0) { + add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); + return irb->codegen->invalid_instruction; + } + + AstNode *new_stack_node = node->data.fn_call_expr.params.at(0); + IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope); + if (new_stack == irb->codegen->invalid_instruction) + return new_stack; + + AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_instruction) + return fn_ref; + + size_t arg_count = node->data.fn_call_expr.params.length - 2; + + IrInstruction **args = allocate(arg_count); + for (size_t i = 0; i < arg_count; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2); + args[i] = ir_gen_node(irb, arg_node, scope); + if (args[i] == irb->codegen->invalid_instruction) + return args[i]; + } + + IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack); return ir_lval_wrap(irb, scope, call, lval); } case BuiltinFnIdTypeId: @@ -4513,7 +4548,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -6825,7 +6860,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction **args = allocate(arg_count); args[0] = implicit_allocator_ptr; // self args[1] = mem_slice; // old_mem - ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr); + ir_build_call(irb, scope, node, nullptr, free_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume"); ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); @@ -11992,7 +12027,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c TypeTableEntry *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node, - fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst); + fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr); result->value.type = async_return_type; return result; } @@ -12362,6 +12397,19 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ir_finish_anal(ira, return_type); } + IrInstruction *casted_new_stack = nullptr; + if (call_instruction->new_stack != nullptr) { + TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); + TypeTableEntry *u8_slice = get_slice_type(ira->codegen, u8_ptr); + IrInstruction *new_stack = call_instruction->new_stack->other; + if (type_is_invalid(new_stack->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice); + if (type_is_invalid(casted_new_stack->value.type)) + return ira->codegen->builtin_types.entry_invalid; + } + if (fn_type->data.fn.is_generic) { if (!fn_entry) { ir_add_error(ira, call_instruction->fn_ref, @@ -12588,7 +12636,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal assert(async_allocator_inst == nullptr); IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, - call_instruction->is_async, nullptr); + call_instruction->is_async, nullptr, casted_new_stack); ir_add_alloca(ira, new_call_instruction, return_type); @@ -12679,7 +12727,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base, - fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr); + fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack); ir_add_alloca(ira, new_call_instruction, return_type); return ir_finish_anal(ira, return_type); diff --git a/src/target.cpp b/src/target.cpp index 5008b51a09..57970888fc 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -896,3 +896,65 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target return false; } + +const char *arch_stack_pointer_register_name(const ArchType *arch) { + switch (arch->arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_x86: + return "sp"; + case ZigLLVM_x86_64: + return "rsp"; + + case ZigLLVM_aarch64: + case ZigLLVM_arm: + case ZigLLVM_thumb: + case ZigLLVM_aarch64_be: + case ZigLLVM_amdgcn: + case ZigLLVM_amdil: + case ZigLLVM_amdil64: + case ZigLLVM_armeb: + case ZigLLVM_arc: + case ZigLLVM_avr: + case ZigLLVM_bpfeb: + case ZigLLVM_bpfel: + case ZigLLVM_hexagon: + case ZigLLVM_lanai: + case ZigLLVM_hsail: + case ZigLLVM_hsail64: + case ZigLLVM_kalimba: + case ZigLLVM_le32: + case ZigLLVM_le64: + case ZigLLVM_mips: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_mipsel: + case ZigLLVM_msp430: + case ZigLLVM_nios2: + case ZigLLVM_nvptx: + case ZigLLVM_nvptx64: + case ZigLLVM_ppc64le: + case ZigLLVM_r600: + case ZigLLVM_renderscript32: + case ZigLLVM_renderscript64: + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + case ZigLLVM_shave: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_sparcv9: + case ZigLLVM_spir: + case ZigLLVM_spir64: + case ZigLLVM_systemz: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_thumbeb: + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + case ZigLLVM_xcore: + case ZigLLVM_ppc: + case ZigLLVM_ppc64: + zig_panic("TODO populate this table with stack pointer register name for this CPU architecture"); + } + zig_unreachable(); +} diff --git a/src/target.hpp b/src/target.hpp index 614b0627d5..5a118f6d8d 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -77,6 +77,8 @@ size_t target_arch_count(void); const ArchType *get_target_arch(size_t index); void get_arch_name(char *out_str, const ArchType *arch); +const char *arch_stack_pointer_register_name(const ArchType *arch); + size_t target_vendor_count(void); ZigLLVM_VendorType get_target_vendor(size_t index); diff --git a/test/behavior.zig b/test/behavior.zig index d700faaebc..fbec60f648 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -23,6 +23,7 @@ comptime { _ = @import("cases/eval.zig"); _ = @import("cases/field_parent_ptr.zig"); _ = @import("cases/fn.zig"); + _ = @import("cases/fn_in_struct_in_comptime.zig"); _ = @import("cases/for.zig"); _ = @import("cases/generics.zig"); _ = @import("cases/if.zig"); @@ -32,11 +33,11 @@ comptime { _ = @import("cases/math.zig"); _ = @import("cases/misc.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); + _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); - _ = @import("cases/type_info.zig"); _ = @import("cases/sizeof_and_typeof.zig"); _ = @import("cases/slice.zig"); _ = @import("cases/struct.zig"); @@ -48,10 +49,10 @@ comptime { _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); + _ = @import("cases/type_info.zig"); _ = @import("cases/undefined.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); - _ = @import("cases/fn_in_struct_in_comptime.zig"); } diff --git a/test/cases/new_stack_call.zig b/test/cases/new_stack_call.zig new file mode 100644 index 0000000000..ea9f0c914f --- /dev/null +++ b/test/cases/new_stack_call.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const assert = std.debug.assert; + +var new_stack_bytes: [1024]u8 = undefined; + +test "calling a function with a new stack" { + const arg = 1234; + + const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); + const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); + _ = targetFunction(arg); + + assert(arg == 1234); + assert(a < b); +} + +fn targetFunction(x: i32) usize { + assert(x == 1234); + + var local_variable: i32 = 42; + const ptr = &local_variable; + *ptr += 1; + + assert(local_variable == 43); + return @ptrToInt(ptr); +} -- cgit v1.2.3 From 548ddd1f0c35033cd7e0d1940975bc7185bf7346 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 May 2018 23:57:15 -0400 Subject: fix AST dumping code in self hosted compiler --- src/ir.cpp | 6 +++++- std/zig/ast.zig | 54 +++++++++++++++++++++++++++++++++++++----------------- std/zig/parse.zig | 35 +---------------------------------- 3 files changed, 43 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 7bc837d908..31d22ca82a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18083,7 +18083,11 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - assert(start_value->value.type->id == TypeTableEntryIdEnum); + if (start_value->value.type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, range->start, buf_sprintf("not an enum type")); + return ira->codegen->builtin_types.entry_invalid; + } + BigInt start_index; bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag); diff --git a/std/zig/ast.zig b/std/zig/ast.zig index a92555731d..a452ed8906 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -67,6 +67,11 @@ pub const Tree = struct { pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { return self.tokenLocationPtr(start_index, self.tokens.at(token_index)); } + + pub fn dump(self: &Tree) void { + self.root_node.base.dump(0); + } + }; pub const Error = union(enum) { @@ -415,6 +420,20 @@ pub const Node = struct { } } + pub fn dump(self: &Node, indent: usize) void { + { + var i: usize = 0; + while (i < indent) : (i += 1) { + std.debug.warn(" "); + } + } + std.debug.warn("{}\n", @tagName(self.id)); + + var child_i: usize = 0; + while (self.iterate(child_i)) |child| : (child_i += 1) { + child.dump(indent + 2); + } + } pub const Root = struct { base: Node, @@ -426,7 +445,7 @@ pub const Node = struct { pub fn iterate(self: &Root, index: usize) ?&Node { if (index < self.decls.len) { - return self.decls.items[self.decls.len - index - 1]; + return *self.decls.at(index); } return null; } @@ -790,8 +809,16 @@ pub const Node = struct { pub fn iterate(self: &FnProto, index: usize) ?&Node { var i = index; - if (self.body_node) |body_node| { - if (i < 1) return body_node; + if (self.lib_name) |lib_name| { + if (i < 1) return lib_name; + i -= 1; + } + + if (i < self.params.len) return *self.params.at(self.params.len - i - 1); + i -= self.params.len; + + if (self.align_expr) |align_expr| { + if (i < 1) return align_expr; i -= 1; } @@ -807,18 +834,11 @@ pub const Node = struct { }, } - if (self.align_expr) |align_expr| { - if (i < 1) return align_expr; + if (self.body_node) |body_node| { + if (i < 1) return body_node; i -= 1; } - if (i < self.params.len) return self.params.items[self.params.len - i - 1]; - i -= self.params.len; - - if (self.lib_name) |lib_name| { - if (i < 1) return lib_name; - i -= 1; - } return null; } @@ -914,7 +934,7 @@ pub const Node = struct { pub fn iterate(self: &Block, index: usize) ?&Node { var i = index; - if (i < self.statements.len) return self.statements.items[i]; + if (i < self.statements.len) return *self.statements.at(i); i -= self.statements.len; return null; @@ -1596,7 +1616,7 @@ pub const Node = struct { i -= 1; switch (self.op) { - Op.Call => |call_info| { + @TagType(Op).Call => |*call_info| { if (i < call_info.params.len) return *call_info.params.at(i); i -= call_info.params.len; }, @@ -1604,7 +1624,7 @@ pub const Node = struct { if (i < 1) return index_expr; i -= 1; }, - Op.Slice => |range| { + @TagType(Op).Slice => |range| { if (i < 1) return range.start; i -= 1; @@ -1613,11 +1633,11 @@ pub const Node = struct { i -= 1; } }, - Op.ArrayInitializer => |exprs| { + Op.ArrayInitializer => |*exprs| { if (i < exprs.len) return *exprs.at(i); i -= exprs.len; }, - Op.StructInitializer => |fields| { + Op.StructInitializer => |*fields| { if (i < fields.len) return *fields.at(i); i -= fields.len; }, diff --git a/std/zig/parse.zig b/std/zig/parse.zig index c96893fd96..f88fdfab62 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -7,9 +7,8 @@ const Token = std.zig.Token; const TokenIndex = ast.TokenIndex; const Error = ast.Error; -/// Returns an AST tree, allocated with the parser's allocator. /// Result should be freed with tree.deinit() when there are -/// no more references to any AST nodes of the tree. +/// no more references to any of the tokens or nodes. pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { var tree_arena = std.heap.ArenaAllocator.init(allocator); errdefer tree_arena.deinit(); @@ -3466,38 +3465,6 @@ fn putBackToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void { } } -const RenderAstFrame = struct { - node: &ast.Node, - indent: usize, -}; - -pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void { - var stack = std.ArrayList(State).init(allocator); - defer stack.deinit(); - - try stack.append(RenderAstFrame { - .node = &root_node.base, - .indent = 0, - }); - - while (stack.popOrNull()) |frame| { - { - var i: usize = 0; - while (i < frame.indent) : (i += 1) { - try stream.print(" "); - } - } - try stream.print("{}\n", @tagName(frame.node.id)); - var child_i: usize = 0; - while (frame.node.iterate(child_i)) |child| : (child_i += 1) { - try stack.append(RenderAstFrame { - .node = child, - .indent = frame.indent + 2, - }); - } - } -} - test "std.zig.parser" { _ = @import("parser_test.zig"); } -- cgit v1.2.3 From 74b10c08d1bd0b64aa8175dff6dec178c7c3bbee Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 15 May 2018 14:11:41 -0400 Subject: fix @typeInfo not setting a field to comptime --- src/ir.cpp | 1 + test/cases/type_info.zig | 319 ++++++++++++++++++++++++++--------------------- 2 files changed, 178 insertions(+), 142 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 31d22ca82a..cade660651 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16549,6 +16549,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); inner_fields[1].data.x_maybe = create_const_vals(1); + inner_fields[1].data.x_maybe->special = ConstValSpecialStatic; inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize; bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset); } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index f10703e3ee..7bf1e68180 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -4,167 +4,199 @@ const TypeInfo = @import("builtin").TypeInfo; const TypeId = @import("builtin").TypeId; test "type info: tag type, void info" { - comptime { - assert(@TagType(TypeInfo) == TypeId); - const void_info = @typeInfo(void); - assert(TypeId(void_info) == TypeId.Void); - assert(void_info.Void == {}); - } + testBasic(); + comptime testBasic(); +} + +fn testBasic() void { + assert(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assert(TypeId(void_info) == TypeId.Void); + assert(void_info.Void == {}); } test "type info: integer, floating point type info" { - comptime { - const u8_info = @typeInfo(u8); - assert(TypeId(u8_info) == TypeId.Int); - assert(!u8_info.Int.is_signed); - assert(u8_info.Int.bits == 8); + testIntFloat(); + comptime testIntFloat(); +} - const f64_info = @typeInfo(f64); - assert(TypeId(f64_info) == TypeId.Float); - assert(f64_info.Float.bits == 64); - } +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + assert(TypeId(u8_info) == TypeId.Int); + assert(!u8_info.Int.is_signed); + assert(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assert(TypeId(f64_info) == TypeId.Float); + assert(f64_info.Float.bits == 64); } test "type info: pointer type info" { - comptime { - const u32_ptr_info = @typeInfo(&u32); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.is_const == false); - assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == 4); - assert(u32_ptr_info.Pointer.child == u32); - } + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(&u32); + assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.is_const == false); + assert(u32_ptr_info.Pointer.is_volatile == false); + assert(u32_ptr_info.Pointer.alignment == 4); + assert(u32_ptr_info.Pointer.child == u32); } test "type info: slice type info" { - comptime { - const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Slice); - assert(u32_slice_info.Slice.is_const == false); - assert(u32_slice_info.Slice.is_volatile == false); - assert(u32_slice_info.Slice.alignment == 4); - assert(u32_slice_info.Slice.child == u32); - } + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + assert(TypeId(u32_slice_info) == TypeId.Slice); + assert(u32_slice_info.Slice.is_const == false); + assert(u32_slice_info.Slice.is_volatile == false); + assert(u32_slice_info.Slice.alignment == 4); + assert(u32_slice_info.Slice.child == u32); } test "type info: array type info" { - comptime { - const arr_info = @typeInfo([42]bool); - assert(TypeId(arr_info) == TypeId.Array); - assert(arr_info.Array.len == 42); - assert(arr_info.Array.child == bool); - } + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + assert(TypeId(arr_info) == TypeId.Array); + assert(arr_info.Array.len == 42); + assert(arr_info.Array.child == bool); } test "type info: nullable type info" { - comptime { - const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Nullable); - assert(null_info.Nullable.child == void); - } + testNullable(); + comptime testNullable(); +} + +fn testNullable() void { + const null_info = @typeInfo(?void); + assert(TypeId(null_info) == TypeId.Nullable); + assert(null_info.Nullable.child == void); } test "type info: promise info" { - comptime { - const null_promise_info = @typeInfo(promise); - assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == @typeOf(undefined)); + testPromise(); + comptime testPromise(); +} - const promise_info = @typeInfo(promise->usize); - assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child == usize); - } +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + assert(TypeId(null_promise_info) == TypeId.Promise); + assert(null_promise_info.Promise.child == @typeOf(undefined)); + const promise_info = @typeInfo(promise->usize); + assert(TypeId(promise_info) == TypeId.Promise); + assert(promise_info.Promise.child == usize); } test "type info: error set, error union info" { - comptime { - const TestErrorSet = error { - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - assert(TypeId(error_set_info) == TypeId.ErrorSet); - assert(error_set_info.ErrorSet.errors.len == 3); - assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); - - const error_union_info = @typeInfo(TestErrorSet!usize); - assert(TypeId(error_union_info) == TypeId.ErrorUnion); - assert(error_union_info.ErrorUnion.error_set == TestErrorSet); - assert(error_union_info.ErrorUnion.payload == usize); - } + testErrorSet(); + comptime testErrorSet(); +} + +fn testErrorSet() void { + const TestErrorSet = error { + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + assert(TypeId(error_set_info) == TypeId.ErrorSet); + assert(error_set_info.ErrorSet.errors.len == 3); + assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assert(TypeId(error_union_info) == TypeId.ErrorUnion); + assert(error_union_info.ErrorUnion.error_set == TestErrorSet); + assert(error_union_info.ErrorUnion.payload == usize); } test "type info: enum info" { - comptime { - const Os = @import("builtin").Os; + testEnum(); + comptime testEnum(); +} - const os_info = @typeInfo(Os); - assert(TypeId(os_info) == TypeId.Enum); - assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 32); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); - assert(os_info.Enum.fields[10].value == 10); - assert(os_info.Enum.tag_type == u5); - assert(os_info.Enum.defs.len == 0); - } +fn testEnum() void { + const Os = @import("builtin").Os; + + const os_info = @typeInfo(Os); + assert(TypeId(os_info) == TypeId.Enum); + assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assert(os_info.Enum.fields.len == 32); + assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas")); + assert(os_info.Enum.fields[10].value == 10); + assert(os_info.Enum.tag_type == u5); + assert(os_info.Enum.defs.len == 0); } test "type info: union info" { - comptime { - const typeinfo_info = @typeInfo(TypeInfo); - assert(TypeId(typeinfo_info) == TypeId.Union); - assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type == TypeId); - assert(typeinfo_info.Union.fields.len == 26); - assert(typeinfo_info.Union.fields[4].enum_field != null); - assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 21); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == @typeOf(undefined)); - assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(notag_union_info.Union.fields.len == 2); - assert(notag_union_info.Union.fields[0].enum_field == null); - assert(notag_union_info.Union.fields[1].field_type == u32); - - const TestExternUnion = extern union { - foo: &c_void, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == @typeOf(undefined)); - assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == &c_void); - } + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + assert(TypeId(typeinfo_info) == TypeId.Union); + assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assert(typeinfo_info.Union.tag_type == TypeId); + assert(typeinfo_info.Union.fields.len == 26); + assert(typeinfo_info.Union.fields[4].enum_field != null); + assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); + assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); + assert(typeinfo_info.Union.defs.len == 21); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assert(TypeId(notag_union_info) == TypeId.Union); + assert(notag_union_info.Union.tag_type == @typeOf(undefined)); + assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assert(notag_union_info.Union.fields.len == 2); + assert(notag_union_info.Union.fields[0].enum_field == null); + assert(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: &c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assert(extern_union_info.Union.tag_type == @typeOf(undefined)); + assert(extern_union_info.Union.fields[0].enum_field == null); + assert(extern_union_info.Union.fields[0].field_type == &c_void); } test "type info: struct info" { - comptime { - const struct_info = @typeInfo(TestStruct); - assert(TypeId(struct_info) == TypeId.Struct); - assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - assert(struct_info.Struct.fields.len == 3); - assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == &TestStruct); - assert(struct_info.Struct.defs.len == 2); - assert(struct_info.Struct.defs[0].is_pub); - assert(!struct_info.Struct.defs[0].data.Fn.is_extern); - assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); - assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void); - } + testStruct(); + comptime testStruct(); +} + +fn testStruct() void { + const struct_info = @typeInfo(TestStruct); + assert(TypeId(struct_info) == TypeId.Struct); + assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assert(struct_info.Struct.fields.len == 3); + assert(struct_info.Struct.fields[1].offset == null); + assert(struct_info.Struct.fields[2].field_type == &TestStruct); + assert(struct_info.Struct.defs.len == 2); + assert(struct_info.Struct.defs[0].is_pub); + assert(!struct_info.Struct.defs[0].data.Fn.is_extern); + assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assert(struct_info.Struct.defs[0].data.Fn.return_type == void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void); } const TestStruct = packed struct { @@ -178,21 +210,24 @@ const TestStruct = packed struct { }; test "type info: function type info" { - comptime { - const fn_info = @typeInfo(@typeOf(foo)); - assert(TypeId(fn_info) == TypeId.Fn); - assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); - assert(fn_info.Fn.is_generic); - assert(fn_info.Fn.args.len == 2); - assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == @typeOf(undefined)); - assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); - } + testFunction(); + comptime testFunction(); +} + +fn testFunction() void { + const fn_info = @typeInfo(@typeOf(foo)); + assert(TypeId(fn_info) == TypeId.Fn); + assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assert(fn_info.Fn.is_generic); + assert(fn_info.Fn.args.len == 2); + assert(fn_info.Fn.is_var_args); + assert(fn_info.Fn.return_type == @typeOf(undefined)); + assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assert(TypeId(bound_fn_info) == TypeId.BoundFn); + assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); } fn foo(comptime a: usize, b: bool, args: ...) usize { -- cgit v1.2.3 From 967bad43a053bf7d9d54dd352fcd8416c767785e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 16 May 2018 20:18:38 -0400 Subject: OpenBSD has the same C integer sizes as Linux Thanks Jan S for this information closes #1016 --- src/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/target.cpp b/src/target.cpp index 57970888fc..c53ed74d14 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -702,6 +702,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsLinux: case OsMacOSX: case OsZen: + case OsOpenBSD: switch (id) { case CIntTypeShort: case CIntTypeUShort: @@ -742,7 +743,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsKFreeBSD: case OsLv2: case OsNetBSD: - case OsOpenBSD: case OsSolaris: case OsHaiku: case OsMinix: -- cgit v1.2.3 From 43085417bec447ab31f3454e180213f102885cc8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 May 2018 21:27:44 -0400 Subject: update github.com/zig-lang to github.com/ziglang --- README.md | 4 ++-- doc/langref.html.in | 8 ++++---- example/hello_world/hello_libc.zig | 2 +- src/analyze.cpp | 4 ++-- src/codegen.cpp | 6 +++--- src/ir.cpp | 8 ++++---- std/atomic/queue.zig | 2 +- std/event.zig | 8 ++++---- std/fmt/index.zig | 2 +- std/mem.zig | 2 +- std/os/index.zig | 6 +++--- std/os/test.zig | 2 +- std/os/time.zig | 2 +- std/special/compiler_rt/comparetf2.zig | 4 ++-- std/zig/ast.zig | 4 ++-- test/build_examples.zig | 2 +- test/cases/coroutines.zig | 2 +- test/cases/misc.zig | 2 +- test/compare_output.zig | 4 ++-- 19 files changed, 37 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/README.md b/README.md index cf4d8179c7..b5bf13f095 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ libc. Create demo games using Zig. ## Building -[![Build Status](https://travis-ci.org/zig-lang/zig.svg?branch=master)](https://travis-ci.org/zig-lang/zig) +[![Build Status](https://travis-ci.org/ziglang/zig.svg?branch=master)](https://travis-ci.org/ziglang/zig) [![Build status](https://ci.appveyor.com/api/projects/status/4t80mk2dmucrc38i/branch/master?svg=true)](https://ci.appveyor.com/project/andrewrk/zig-d3l86/branch/master) ### Stage 1: Build Zig from C++ Source Code @@ -161,7 +161,7 @@ bin/zig build --build-file ../build.zig test ##### Windows -See https://github.com/zig-lang/zig/wiki/Building-Zig-on-Windows +See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows ### Stage 2: Build Self-Hosted Zig from Zig Source Code diff --git a/doc/langref.html.in b/doc/langref.html.in index c3c50b117b..d63c38d0fe 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -96,7 +96,7 @@

If you search for something specific in this documentation and do not find it, - please file an issue or say something on IRC. + please file an issue or say something on IRC.

The code samples in this document are compiled and tested as part of the main test suite of Zig. @@ -2827,7 +2827,7 @@ test "fn reflection" {

The number of unique error values across the entire compilation should determine the size of the error set type. - However right now it is hard coded to be a u16. See #768. + However right now it is hard coded to be a u16. See #768.

You can implicitly cast an error from a subset to its superset: @@ -5958,7 +5958,7 @@ pub fn main() void { {#code_begin|exe#} {#link_libc#} const c = @cImport({ - // See https://github.com/zig-lang/zig/issues/515 + // See https://github.com/ziglang/zig/issues/515 @cDefine("_NO_CRT_STDIO_INLINE", "1"); @cInclude("stdio.h"); }); @@ -6301,7 +6301,7 @@ fn readU32Be() u32 {}

  • Non-Ascii Unicode line endings: U+0085 (NEL), U+2028 (LS), U+2029 (PS).
  • The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possbly the last line of the file).

    -

    For some discussion on the rationale behind these design decisions, see issue #663

    +

    For some discussion on the rationale behind these design decisions, see issue #663

    {#header_close#} {#header_open|Grammar#}
    Root = many(TopLevelItem) EOF
    diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig
    index 60123c6fd8..4a35e47b15 100644
    --- a/example/hello_world/hello_libc.zig
    +++ b/example/hello_world/hello_libc.zig
    @@ -1,5 +1,5 @@
     const c = @cImport({
    -    // See https://github.com/zig-lang/zig/issues/515
    +    // See https://github.com/ziglang/zig/issues/515
         @cDefine("_NO_CRT_STDIO_INLINE", "1");
         @cInclude("stdio.h");
         @cInclude("string.h");
    diff --git a/src/analyze.cpp b/src/analyze.cpp
    index d6137a4286..c59fde8ef6 100644
    --- a/src/analyze.cpp
    +++ b/src/analyze.cpp
    @@ -1007,7 +1007,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
         if (fn_type_id->return_type != nullptr) {
             ensure_complete_type(g, fn_type_id->return_type);
         } else {
    -        zig_panic("TODO implement inferred return types https://github.com/zig-lang/zig/issues/447");
    +        zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447");
         }
     
         TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
    @@ -1556,7 +1556,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
                 return g->builtin_types.entry_invalid;
             }
             add_node_error(g, proto_node,
    -            buf_sprintf("TODO implement inferred return types https://github.com/zig-lang/zig/issues/447"));
    +            buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
             return g->builtin_types.entry_invalid;
             //return get_generic_fn_type(g, &fn_type_id);
         }
    diff --git a/src/codegen.cpp b/src/codegen.cpp
    index f1e102392a..69542b3e67 100644
    --- a/src/codegen.cpp
    +++ b/src/codegen.cpp
    @@ -582,7 +582,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
                 addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull");
             }
             // Note: byval is disabled on windows due to an LLVM bug:
    -        // https://github.com/zig-lang/zig/issues/536
    +        // https://github.com/ziglang/zig/issues/536
             if (is_byval && g->zig_target.os != OsWindows) {
                 addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval");
             }
    @@ -3067,7 +3067,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
             FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
             // Note: byval is disabled on windows due to an LLVM bug:
    -        // https://github.com/zig-lang/zig/issues/536
    +        // https://github.com/ziglang/zig/issues/536
             if (gen_info->is_byval && g->zig_target.os != OsWindows) {
                 addLLVMCallsiteAttr(result, (unsigned)gen_info->gen_index, "byval");
             }
    @@ -6730,7 +6730,7 @@ static void init(CodeGen *g) {
         const char *target_specific_features;
         if (g->is_native_target) {
             // LLVM creates invalid binaries on Windows sometimes.
    -        // See https://github.com/zig-lang/zig/issues/508
    +        // See https://github.com/ziglang/zig/issues/508
             // As a workaround we do not use target native features on Windows.
             if (g->zig_target.os == OsWindows) {
                 target_specific_cpu_args = "";
    diff --git a/src/ir.cpp b/src/ir.cpp
    index e2cbba48a7..440063d58d 100644
    --- a/src/ir.cpp
    +++ b/src/ir.cpp
    @@ -12130,7 +12130,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
                 casted_arg->value.type->id == TypeTableEntryIdNumLitFloat)
         {
             ir_add_error(ira, casted_arg,
    -            buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/zig-lang/zig/issues/557"));
    +            buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557"));
             return false;
         }
     
    @@ -12331,7 +12331,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
     
             if (fn_proto_node->data.fn_proto.is_var_args) {
                 ir_add_error(ira, &call_instruction->base,
    -                    buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/zig-lang/zig/issues/313"));
    +                    buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313"));
                 return ira->codegen->builtin_types.entry_invalid;
             }
     
    @@ -12424,7 +12424,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
             }
             if (call_instruction->is_async && fn_type_id->is_var_args) {
                 ir_add_error(ira, call_instruction->fn_ref,
    -                buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/zig-lang/zig/issues/557"));
    +                buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/ziglang/zig/issues/557"));
                 return ira->codegen->builtin_types.entry_invalid;
             }
     
    @@ -12507,7 +12507,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
                         VariableTableEntry *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i);
                         if (arg_var == nullptr) {
                             ir_add_error(ira, arg,
    -                            buf_sprintf("compiler bug: var args can't handle void. https://github.com/zig-lang/zig/issues/557"));
    +                            buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557"));
                             return ira->codegen->builtin_types.entry_invalid;
                         }
                         IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var, true, false);
    diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
    index 288a2b3b48..35180da8d1 100644
    --- a/std/atomic/queue.zig
    +++ b/std/atomic/queue.zig
    @@ -16,7 +16,7 @@ pub fn Queue(comptime T: type) type {
                 data: T,
             };
     
    -        // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
    +        // TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287
             pub fn init(self: &Self) void {
                 self.root.next = null;
                 self.head = &self.root;
    diff --git a/std/event.zig b/std/event.zig
    index b2e7e3ae38..558bd2a188 100644
    --- a/std/event.zig
    +++ b/std/event.zig
    @@ -148,7 +148,7 @@ pub const Loop = struct {
     };
     
     pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
    -    var address = _address.*; // TODO https://github.com/zig-lang/zig/issues/733
    +    var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733
     
         const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp);
         errdefer std.os.close(sockfd);
    @@ -172,7 +172,7 @@ test "listen on a port, send bytes, receive bytes" {
     
             async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, _socket: &const std.os.File) void {
                 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
    -            var socket = _socket.*; // TODO https://github.com/zig-lang/zig/issues/733
    +            var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
                 defer socket.close();
                 const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
                     error.OutOfMemory => @panic("unable to handle connection: out of memory"),
    @@ -186,8 +186,8 @@ test "listen on a port, send bytes, receive bytes" {
             }
     
             async fn errorableHandler(self: &Self, _addr: &const std.net.Address, _socket: &const std.os.File) !void {
    -            const addr = _addr.*; // TODO https://github.com/zig-lang/zig/issues/733
    -            var socket = _socket.*; // TODO https://github.com/zig-lang/zig/issues/733
    +            const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
    +            var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
     
                 var adapter = std.io.FileOutStream.init(&socket);
                 var stream = &adapter.stream;
    diff --git a/std/fmt/index.zig b/std/fmt/index.zig
    index 0af772b7dc..624751822a 100644
    --- a/std/fmt/index.zig
    +++ b/std/fmt/index.zig
    @@ -824,7 +824,7 @@ test "fmt.format" {
         try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
         try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
         {
    -        // Dummy field because of https://github.com/zig-lang/zig/issues/557.
    +        // Dummy field because of https://github.com/ziglang/zig/issues/557.
             const Struct = struct {
                 unused: u8,
             };
    diff --git a/std/mem.zig b/std/mem.zig
    index 3ca87b35d3..617c1de2f5 100644
    --- a/std/mem.zig
    +++ b/std/mem.zig
    @@ -702,7 +702,7 @@ test "std.mem.rotate" {
         }));
     }
     
    -// TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by
    +// TODO: When https://github.com/ziglang/zig/issues/649 is solved these can be done by
     // endian-casting the pointer and then dereferencing
     
     pub fn endianSwapIfLe(comptime T: type, x: T) T {
    diff --git a/std/os/index.zig b/std/os/index.zig
    index 7d19cd82c6..01e2092e1c 100644
    --- a/std/os/index.zig
    +++ b/std/os/index.zig
    @@ -239,7 +239,7 @@ pub fn close(handle: FileHandle) void {
     /// Calls POSIX read, and keeps trying if it gets interrupted.
     pub fn posixRead(fd: i32, buf: []u8) !void {
         // Linux can return EINVAL when read amount is > 0x7ffff000
    -    // See https://github.com/zig-lang/zig/pull/743#issuecomment-363158274
    +    // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274
         const max_buf_len = 0x7ffff000;
     
         var index: usize = 0;
    @@ -281,7 +281,7 @@ pub const PosixWriteError = error{
     /// Calls POSIX write, and keeps trying if it gets interrupted.
     pub fn posixWrite(fd: i32, bytes: []const u8) !void {
         // Linux can return EINVAL when write amount is > 0x7ffff000
    -    // See https://github.com/zig-lang/zig/pull/743#issuecomment-363165856
    +    // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856
         const max_bytes_len = 0x7ffff000;
     
         var index: usize = 0;
    @@ -2513,7 +2513,7 @@ pub const SpawnThreadError = error{
     /// caller must call wait on the returned thread
     pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
         // TODO compile-time call graph analysis to determine stack upper bound
    -    // https://github.com/zig-lang/zig/issues/157
    +    // https://github.com/ziglang/zig/issues/157
         const default_stack_size = 8 * 1024 * 1024;
     
         const Context = @typeOf(context);
    diff --git a/std/os/test.zig b/std/os/test.zig
    index 56d6e8b309..4dfe76224a 100644
    --- a/std/os/test.zig
    +++ b/std/os/test.zig
    @@ -12,7 +12,7 @@ const AtomicOrder = builtin.AtomicOrder;
     test "makePath, put some files in it, deleteTree" {
         if (builtin.os == builtin.Os.windows) {
             // TODO implement os.Dir for windows
    -        // https://github.com/zig-lang/zig/issues/709
    +        // https://github.com/ziglang/zig/issues/709
             return;
         }
         try os.makePath(a, "os_test_tmp/b/c");
    diff --git a/std/os/time.zig b/std/os/time.zig
    index 4fd2c4e924..3af150ab6a 100644
    --- a/std/os/time.zig
    +++ b/std/os/time.zig
    @@ -135,7 +135,7 @@ pub const Timer = struct {
         
         //At some point we may change our minds on RAW, but for now we're
         //  sticking with posix standard MONOTONIC. For more information, see: 
    -    //  https://github.com/zig-lang/zig/pull/933
    +    //  https://github.com/ziglang/zig/pull/933
         //
         //const monotonic_clock_id = switch(builtin.os) {
         //    Os.linux => linux.CLOCK_MONOTONIC_RAW,
    diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig
    index c189e5803b..760c3689c0 100644
    --- a/std/special/compiler_rt/comparetf2.zig
    +++ b/std/special/compiler_rt/comparetf2.zig
    @@ -1,4 +1,4 @@
    -// TODO https://github.com/zig-lang/zig/issues/305
    +// TODO https://github.com/ziglang/zig/issues/305
     // and then make the return types of some of these functions the enum instead of c_int
     const LE_LESS = c_int(-1);
     const LE_EQUAL = c_int(0);
    @@ -59,7 +59,7 @@ pub extern fn __letf2(a: f128, b: f128) c_int {
         ;
     }
     
    -// TODO https://github.com/zig-lang/zig/issues/305
    +// TODO https://github.com/ziglang/zig/issues/305
     // and then make the return types of some of these functions the enum instead of c_int
     const GE_LESS = c_int(-1);
     const GE_EQUAL = c_int(0);
    diff --git a/std/zig/ast.zig b/std/zig/ast.zig
    index c1552b0220..1f15046a79 100644
    --- a/std/zig/ast.zig
    +++ b/std/zig/ast.zig
    @@ -113,7 +113,7 @@ pub const Error = union(enum) {
     
         pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void {
             switch (self.*) {
    -            // TODO https://github.com/zig-lang/zig/issues/683
    +            // TODO https://github.com/ziglang/zig/issues/683
                 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),
                 @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
                 @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream),
    @@ -137,7 +137,7 @@ pub const Error = union(enum) {
     
         pub fn loc(self: &Error) TokenIndex {
             switch (self.*) {
    -            // TODO https://github.com/zig-lang/zig/issues/683
    +            // TODO https://github.com/ziglang/zig/issues/683
                 @TagType(Error).InvalidToken => |x| return x.token,
                 @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token,
                 @TagType(Error).ExpectedAggregateKw => |x| return x.token,
    diff --git a/test/build_examples.zig b/test/build_examples.zig
    index a3b44b9136..7a4c0f35d9 100644
    --- a/test/build_examples.zig
    +++ b/test/build_examples.zig
    @@ -9,7 +9,7 @@ pub fn addCases(cases: &tests.BuildExamplesContext) void {
         cases.add("example/guess_number/main.zig");
         if (!is_windows) {
             // TODO get this test passing on windows
    -        // See https://github.com/zig-lang/zig/issues/538
    +        // See https://github.com/ziglang/zig/issues/538
             cases.addBuildFile("example/shared_library/build.zig");
             cases.addBuildFile("example/mix_o_files/build.zig");
         }
    diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
    index 4aa97861ac..e983947a4c 100644
    --- a/test/cases/coroutines.zig
    +++ b/test/cases/coroutines.zig
    @@ -204,7 +204,7 @@ test "error return trace across suspend points - async return" {
         cancel p2;
     }
     
    -// TODO https://github.com/zig-lang/zig/issues/760
    +// TODO https://github.com/ziglang/zig/issues/760
     fn nonFailing() (promise->error!void) {
         return async suspendThenFail() catch unreachable;
     }
    diff --git a/test/cases/misc.zig b/test/cases/misc.zig
    index 66487a4946..deeeca8c3a 100644
    --- a/test/cases/misc.zig
    +++ b/test/cases/misc.zig
    @@ -543,7 +543,7 @@ test "@typeName" {
         comptime {
             assert(mem.eql(u8, @typeName(i64), "i64"));
             assert(mem.eql(u8, @typeName(&usize), "&usize"));
    -        // https://github.com/zig-lang/zig/issues/675
    +        // https://github.com/ziglang/zig/issues/675
             assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)"));
             assert(mem.eql(u8, @typeName(Struct), "Struct"));
             assert(mem.eql(u8, @typeName(Union), "Union"));
    diff --git a/test/compare_output.zig b/test/compare_output.zig
    index b01e87d4eb..905ffd37a9 100644
    --- a/test/compare_output.zig
    +++ b/test/compare_output.zig
    @@ -131,7 +131,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
             \\const is_windows = builtin.os == builtin.Os.windows;
             \\const c = @cImport({
             \\    if (is_windows) {
    -        \\        // See https://github.com/zig-lang/zig/issues/515
    +        \\        // See https://github.com/ziglang/zig/issues/515
             \\        @cDefine("_NO_CRT_STDIO_INLINE", "1");
             \\        @cInclude("io.h");
             \\        @cInclude("fcntl.h");
    @@ -316,7 +316,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
             \\const is_windows = builtin.os == builtin.Os.windows;
             \\const c = @cImport({
             \\    if (is_windows) {
    -        \\        // See https://github.com/zig-lang/zig/issues/515
    +        \\        // See https://github.com/ziglang/zig/issues/515
             \\        @cDefine("_NO_CRT_STDIO_INLINE", "1");
             \\        @cInclude("io.h");
             \\        @cInclude("fcntl.h");
    -- 
    cgit v1.2.3
    
    
    From 2b3af4ef6b79b8fe178656b069f837eff82ae8c3 Mon Sep 17 00:00:00 2001
    From: Jimmi HC 
    Date: Wed, 30 May 2018 10:30:09 +0200
    Subject: fixed #1009 ir_make_type_info_defs already calls
     resolve_top_level_decl on all Tld when building the def array. This means,
     that there is no reason that analyze_fn_body is nessesary, as the fn type
     should have already been resolved completly. The only thing analyze_fn_body
     does here, is cause problems with generic functions.
    
    ---
     src/ir.cpp               | 4 ----
     test/cases/type_info.zig | 7 +++++++
     2 files changed, 7 insertions(+), 4 deletions(-)
    
    (limited to 'src')
    
    diff --git a/src/ir.cpp b/src/ir.cpp
    index 440063d58d..5d182fe9b0 100644
    --- a/src/ir.cpp
    +++ b/src/ir.cpp
    @@ -15982,10 +15982,6 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
                         FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
                         assert(!fn_entry->is_test);
     
    -                    analyze_fn_body(ira->codegen, fn_entry);
    -                    if (fn_entry->anal_state == FnAnalStateInvalid)
    -                        return;
    -
                         AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node);
     
                         ConstExprValue *fn_def_val = create_const_vals(1);
    diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig
    index eee5d1f2ca..05266feb9c 100644
    --- a/test/cases/type_info.zig
    +++ b/test/cases/type_info.zig
    @@ -233,3 +233,10 @@ fn testFunction() void {
     fn foo(comptime a: usize, b: bool, args: ...) usize {
         return 0;
     }
    +
    +test "typeInfo with comptime parameter in struct fn def" {
    +    const S = struct {
    +        pub fn func(comptime x: f32) void {}
    +    };
    +    comptime var info = @typeInfo(S);
    +}
    -- 
    cgit v1.2.3
    
    
    From 1b3aaacba260e4c8d89ac98ab856ff9b3c77dac4 Mon Sep 17 00:00:00 2001
    From: Jimmi HC 
    Date: Wed, 30 May 2018 10:34:20 +0200
    Subject: Removed copy-pasted resolve_inferred_error_set both ir.cpp and
     analyze.cpp have a function resolve_inferred_error_set, which is a nearly
     exact copy-paste. This commit removes the one in ir.cpp and exposes then one
     in analyze.cpp. This also allows us to make analyze_fn_body local to
     analyze.cpp, as it is not used anywhere in ir.cpp after this change
    
    ---
     src/analyze.cpp |  7 ++++---
     src/analyze.hpp |  2 +-
     src/ir.cpp      | 62 +++++++++++++++++++--------------------------------------
     3 files changed, 25 insertions(+), 46 deletions(-)
    
    (limited to 'src')
    
    diff --git a/src/analyze.cpp b/src/analyze.cpp
    index c59fde8ef6..b00e18a9a1 100644
    --- a/src/analyze.cpp
    +++ b/src/analyze.cpp
    @@ -25,6 +25,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);
     static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);
     static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);
     static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
    +static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
     
     ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
         if (node->owner->c_import_node != nullptr) {
    @@ -3880,7 +3881,7 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr
         }
     }
     
    -static bool analyze_resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) {
    +bool resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) {
         FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn;
         if (infer_fn != nullptr) {
             if (infer_fn->anal_state == FnAnalStateInvalid) {
    @@ -3932,7 +3933,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
                 }
     
                 if (inferred_err_set_type->data.error_set.infer_fn != nullptr) {
    -                if (!analyze_resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
    +                if (!resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) {
                         fn_table_entry->anal_state = FnAnalStateInvalid;
                         return;
                     }
    @@ -3962,7 +3963,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
         fn_table_entry->anal_state = FnAnalStateComplete;
     }
     
    -void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
    +static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
         assert(fn_table_entry->anal_state != FnAnalStateProbing);
         if (fn_table_entry->anal_state != FnAnalStateReady)
             return;
    diff --git a/src/analyze.hpp b/src/analyze.hpp
    index 56ca21a93f..d538f042ce 100644
    --- a/src/analyze.hpp
    +++ b/src/analyze.hpp
    @@ -191,7 +191,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
     
     ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
     TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
    -void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
    +bool resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node);
     
     TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry);
     
    diff --git a/src/ir.cpp b/src/ir.cpp
    index 5d182fe9b0..8d32a81e25 100644
    --- a/src/ir.cpp
    +++ b/src/ir.cpp
    @@ -7633,38 +7633,16 @@ static bool slice_is_const(TypeTableEntry *type) {
         return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
     }
     
    -static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) {
    -    assert(err_set_type->id == TypeTableEntryIdErrorSet);
    -    FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn;
    -    if (infer_fn != nullptr) {
    -        if (infer_fn->anal_state == FnAnalStateInvalid) {
    -            return false;
    -        } else if (infer_fn->anal_state == FnAnalStateReady) {
    -            analyze_fn_body(ira->codegen, infer_fn);
    -            if (err_set_type->data.error_set.infer_fn != nullptr) {
    -                assert(ira->codegen->errors.length != 0);
    -                return false;
    -            }
    -        } else {
    -            ir_add_error_node(ira, source_node,
    -                buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet",
    -                    buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name)));
    -            return false;
    -        }
    -    }
    -    return true;
    -}
    -
     static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry *set1, TypeTableEntry *set2,
             AstNode *source_node)
     {
         assert(set1->id == TypeTableEntryIdErrorSet);
         assert(set2->id == TypeTableEntryIdErrorSet);
     
    -    if (!resolve_inferred_error_set(ira, set1, source_node)) {
    +    if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) {
             return ira->codegen->builtin_types.entry_invalid;
         }
    -    if (!resolve_inferred_error_set(ira, set2, source_node)) {
    +    if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) {
             return ira->codegen->builtin_types.entry_invalid;
         }
         if (type_is_global_error_set(set1)) {
    @@ -7803,7 +7781,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
                 return result;
             }
     
    -        if (!resolve_inferred_error_set(ira, contained_set, source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) {
                 result.id = ConstCastResultIdUnresolvedInferredErrSet;
                 return result;
             }
    @@ -8192,7 +8170,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
             } else {
                 err_set_type = prev_inst->value.type;
    -            if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) {
    +            if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) {
                     return ira->codegen->builtin_types.entry_invalid;
                 }
                 update_errors_helper(ira->codegen, &errors, &errors_count);
    @@ -8231,7 +8209,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     if (type_is_global_error_set(err_set_type)) {
                         continue;
                     }
    -                if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
    +                if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
                         return ira->codegen->builtin_types.entry_invalid;
                     }
                     if (type_is_global_error_set(cur_type)) {
    @@ -8297,7 +8275,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                         continue;
                     }
                     TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
    -                if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
    +                if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
                         return ira->codegen->builtin_types.entry_invalid;
                     }
                     if (type_is_global_error_set(cur_err_set_type)) {
    @@ -8360,7 +8338,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                 if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) {
                     continue;
                 }
    -            if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) {
    +            if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
                     return ira->codegen->builtin_types.entry_invalid;
                 }
     
    @@ -8417,11 +8395,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
                     TypeTableEntry *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type;
                     TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
     
    -                if (!resolve_inferred_error_set(ira, prev_err_set_type, cur_inst->source_node)) {
    +                if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) {
                         return ira->codegen->builtin_types.entry_invalid;
                     }
     
    -                if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
    +                if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
                         return ira->codegen->builtin_types.entry_invalid;
                     }
     
    @@ -8531,7 +8509,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
             {
                 if (err_set_type != nullptr) {
                     TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
    -                if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) {
    +                if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
                         return ira->codegen->builtin_types.entry_invalid;
                     }
                     if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) {
    @@ -9213,7 +9191,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou
             if (!val)
                 return ira->codegen->invalid_instruction;
     
    -        if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) {
                 return ira->codegen->invalid_instruction;
             }
             if (!type_is_global_error_set(wanted_type)) {
    @@ -9654,7 +9632,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc
             IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
                     source_instr->source_node, wanted_type);
     
    -        if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) {
                 return ira->codegen->invalid_instruction;
             }
     
    @@ -9752,7 +9730,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc
             zig_unreachable();
         }
         if (!type_is_global_error_set(err_set_type)) {
    -        if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) {
                 return ira->codegen->invalid_instruction;
             }
             if (err_set_type->data.error_set.err_count == 0) {
    @@ -10647,7 +10625,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
                 return ira->codegen->builtin_types.entry_invalid;
             }
     
    -        if (!resolve_inferred_error_set(ira, intersect_type, source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) {
                 return ira->codegen->builtin_types.entry_invalid;
             }
     
    @@ -11503,11 +11481,11 @@ static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstruction
             return ira->codegen->builtin_types.entry_type;
         }
     
    -    if (!resolve_inferred_error_set(ira, op1_type, instruction->op1->other->source_node)) {
    +    if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->other->source_node)) {
             return ira->codegen->builtin_types.entry_invalid;
         }
     
    -    if (!resolve_inferred_error_set(ira, op2_type, instruction->op2->other->source_node)) {
    +    if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->other->source_node)) {
             return ira->codegen->builtin_types.entry_invalid;
         }
     
    @@ -13851,7 +13829,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
                     }
                     err_set_type = err_entry->set_with_only_this_in_it;
                 } else {
    -                if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) {
    +                if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) {
                         return ira->codegen->builtin_types.entry_invalid;
                     }
                     err_entry = find_err_table_entry(child_type, field_name);
    @@ -17559,7 +17537,7 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
         } else if (container_type->id == TypeTableEntryIdUnion) {
             result = container_type->data.unionation.src_field_count;
         } else if (container_type->id == TypeTableEntryIdErrorSet) {
    -        if (!resolve_inferred_error_set(ira, container_type, instruction->base.source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, container_type, instruction->base.source_node)) {
                 return ira->codegen->builtin_types.entry_invalid;
             }
             if (type_is_global_error_set(container_type)) {
    @@ -17863,7 +17841,7 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc
             }
     
             TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type;
    -        if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
                 return ira->codegen->builtin_types.entry_invalid;
             }
             if (!type_is_global_error_set(err_set_type) &&
    @@ -18131,7 +18109,7 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
                 }
             }
         } else if (switch_type->id == TypeTableEntryIdErrorSet) {
    -        if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) {
    +        if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->source_node)) {
                 return ira->codegen->builtin_types.entry_invalid;
             }
     
    -- 
    cgit v1.2.3
    
    
    From 15302e84a45a04cfe94a8842318f02a608055962 Mon Sep 17 00:00:00 2001
    From: Jimmi HC 
    Date: Wed, 30 May 2018 11:51:46 +0200
    Subject: Adding workaround for when the user tries to unwrap 'type' closes
     #1011
    
    ---
     src/ir.cpp | 9 +++++++++
     1 file changed, 9 insertions(+)
    
    (limited to 'src')
    
    diff --git a/src/ir.cpp b/src/ir.cpp
    index 8d32a81e25..6e944a8976 100644
    --- a/src/ir.cpp
    +++ b/src/ir.cpp
    @@ -17914,6 +17914,15 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
             return ira->codegen->builtin_types.entry_invalid;
         TypeTableEntry *ptr_type = value->value.type;
     
    +    // Because we don't have Pointer Reform yet, we can't have a pointer to a 'type'.
    +    // Therefor, we have to check for type 'type' here, so we can output a correct error
    +    // without asserting the assert below.
    +    if (ptr_type->id == TypeTableEntryIdMetaType) {
    +        ir_add_error(ira, value,
    +            buf_sprintf("expected error union type, found '%s'", buf_ptr(&ptr_type->name)));
    +        return ira->codegen->builtin_types.entry_invalid;
    +    }
    +
         // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
         assert(ptr_type->id == TypeTableEntryIdPointer);
     
    -- 
    cgit v1.2.3
    
    
    From fcbb7426faac5e693ef195defe2d8d2a2eddadb1 Mon Sep 17 00:00:00 2001
    From: Andrew Kelley 
    Date: Thu, 31 May 2018 10:56:59 -0400
    Subject: use * for pointer type instead of &
    
    See #770
    
    To help automatically translate code, see the
    zig-fmt-pointer-reform-2 branch.
    
    This will convert all & into *. Due to the syntax
    ambiguity (which is why we are making this change),
    even address-of & will turn into *, so you'll have
    to manually fix thes instances. You will be guaranteed
    to get compile errors for them - expected 'type', found 'foo'
    ---
     build.zig                                      |  14 +-
     doc/docgen.zig                                 |  22 +-
     doc/langref.html.in                            | 212 ++++-----
     example/cat/main.zig                           |   2 +-
     example/hello_world/hello_libc.zig             |   2 +-
     example/mix_o_files/base64.zig                 |   2 +-
     example/mix_o_files/build.zig                  |   2 +-
     example/shared_library/build.zig               |   2 +-
     src-self-hosted/arg.zig                        |  12 +-
     src-self-hosted/errmsg.zig                     |  14 +-
     src-self-hosted/introspect.zig                 |   6 +-
     src-self-hosted/ir.zig                         |   2 +-
     src-self-hosted/main.zig                       |  36 +-
     src-self-hosted/module.zig                     |  30 +-
     src-self-hosted/scope.zig                      |   2 +-
     src-self-hosted/target.zig                     |  10 +-
     src/all_types.hpp                              |  31 +-
     src/analyze.cpp                                |   8 +-
     src/ast_render.cpp                             |  31 +-
     src/codegen.cpp                                |   2 +-
     src/ir.cpp                                     |  89 ++--
     src/ir_print.cpp                               |   6 +-
     src/parser.cpp                                 |  41 +-
     src/translate_c.cpp                            |  33 +-
     std/array_list.zig                             |  46 +-
     std/atomic/queue.zig                           |  32 +-
     std/atomic/stack.zig                           |  36 +-
     std/base64.zig                                 |  12 +-
     std/buf_map.zig                                |  18 +-
     std/buf_set.zig                                |  18 +-
     std/buffer.zig                                 |  40 +-
     std/build.zig                                  | 278 ++++++------
     std/c/darwin.zig                               |   8 +-
     std/c/index.zig                                |  72 ++--
     std/c/linux.zig                                |   4 +-
     std/c/windows.zig                              |   2 +-
     std/crypto/blake2.zig                          |  16 +-
     std/crypto/md5.zig                             |   8 +-
     std/crypto/sha1.zig                            |   8 +-
     std/crypto/sha2.zig                            |  16 +-
     std/crypto/sha3.zig                            |   6 +-
     std/crypto/throughput_test.zig                 |   4 +-
     std/cstr.zig                                   |  26 +-
     std/debug/failing_allocator.zig                |  10 +-
     std/debug/index.zig                            | 106 ++---
     std/elf.zig                                    |  18 +-
     std/event.zig                                  |  34 +-
     std/fmt/errol/index.zig                        |  14 +-
     std/fmt/index.zig                              |   8 +-
     std/hash/adler.zig                             |   4 +-
     std/hash/crc.zig                               |   8 +-
     std/hash/fnv.zig                               |   4 +-
     std/hash/siphash.zig                           |   8 +-
     std/hash_map.zig                               |  36 +-
     std/heap.zig                                   |  82 ++--
     std/io.zig                                     |  80 ++--
     std/json.zig                                   |  36 +-
     std/linked_list.zig                            |  32 +-
     std/macho.zig                                  |  16 +-
     std/math/complex/atan.zig                      |   4 +-
     std/math/complex/cosh.zig                      |   4 +-
     std/math/complex/exp.zig                       |   4 +-
     std/math/complex/index.zig                     |  14 +-
     std/math/complex/ldexp.zig                     |   8 +-
     std/math/complex/pow.zig                       |   2 +-
     std/math/complex/sinh.zig                      |   4 +-
     std/math/complex/sqrt.zig                      |   4 +-
     std/math/complex/tanh.zig                      |   4 +-
     std/math/hypot.zig                             |   2 +-
     std/math/index.zig                             |   4 +-
     std/mem.zig                                    |  48 +--
     std/net.zig                                    |   8 +-
     std/os/child_process.zig                       |  62 +--
     std/os/darwin.zig                              |  64 +--
     std/os/file.zig                                |  32 +-
     std/os/get_user_id.zig                         |   8 +-
     std/os/index.zig                               | 164 +++----
     std/os/linux/index.zig                         | 174 ++++----
     std/os/linux/vdso.zig                          |  36 +-
     std/os/linux/x86_64.zig                        |   8 +-
     std/os/path.zig                                |  22 +-
     std/os/test.zig                                |   2 +-
     std/os/time.zig                                |   6 +-
     std/os/windows/index.zig                       |  96 ++---
     std/os/windows/util.zig                        |  12 +-
     std/os/zen.zig                                 |  20 +-
     std/rand/index.zig                             |  46 +-
     std/rand/ziggurat.zig                          |  10 +-
     std/segmented_list.zig                         |  54 +--
     std/sort.zig                                   |  54 +--
     std/special/bootstrap.zig                      |  20 +-
     std/special/build_file_template.zig            |   4 +-
     std/special/build_runner.zig                   |   6 +-
     std/special/builtin.zig                        |   8 +-
     std/special/compiler_rt/index.zig              |   4 +-
     std/special/compiler_rt/udivmod.zig            |  18 +-
     std/special/compiler_rt/udivmoddi4.zig         |   2 +-
     std/special/compiler_rt/udivmodti4.zig         |   4 +-
     std/special/compiler_rt/udivti3.zig            |   2 +-
     std/special/compiler_rt/umodti3.zig            |   2 +-
     std/special/panic.zig                          |   2 +-
     std/unicode.zig                                |   6 +-
     std/zig/ast.zig                                | 570 ++++++++++++-------------
     std/zig/bench.zig                              |   6 +-
     std/zig/parse.zig                              | 156 +++----
     std/zig/parser_test.zig                        |   2 +-
     std/zig/render.zig                             |  56 +--
     std/zig/tokenizer.zig                          |   8 +-
     test/assemble_and_link.zig                     |   2 +-
     test/build_examples.zig                        |   2 +-
     test/cases/align.zig                           |  56 +--
     test/cases/atomics.zig                         |  12 +-
     test/cases/bugs/655.zig                        |   4 +-
     test/cases/bugs/828.zig                        |   6 +-
     test/cases/bugs/920.zig                        |   6 +-
     test/cases/cast.zig                            |  42 +-
     test/cases/const_slice_child.zig               |   6 +-
     test/cases/coroutines.zig                      |   6 +-
     test/cases/enum.zig                            |  10 +-
     test/cases/enum_with_members.zig               |   2 +-
     test/cases/eval.zig                            |  12 +-
     test/cases/field_parent_ptr.zig                |   4 +-
     test/cases/fn_in_struct_in_comptime.zig        |   6 +-
     test/cases/generics.zig                        |   8 +-
     test/cases/incomplete_struct_param_tld.zig     |   4 +-
     test/cases/math.zig                            |  18 +-
     test/cases/misc.zig                            |  48 +--
     test/cases/null.zig                            |   2 +-
     test/cases/reflection.zig                      |   2 +-
     test/cases/slice.zig                           |   2 +-
     test/cases/struct.zig                          |  28 +-
     test/cases/struct_contains_null_ptr_itself.zig |   4 +-
     test/cases/switch.zig                          |   2 +-
     test/cases/this.zig                            |   2 +-
     test/cases/type_info.zig                       |  16 +-
     test/cases/undefined.zig                       |   4 +-
     test/cases/union.zig                           |  16 +-
     test/compare_output.zig                        |  20 +-
     test/compile_errors.zig                        | 122 +++---
     test/gen_h.zig                                 |   2 +-
     test/runtime_safety.zig                        |   2 +-
     test/standalone/brace_expansion/build.zig      |   2 +-
     test/standalone/brace_expansion/main.zig       |   8 +-
     test/standalone/issue_339/build.zig            |   2 +-
     test/standalone/issue_339/test.zig             |   2 +-
     test/standalone/issue_794/build.zig            |   2 +-
     test/standalone/pkg_import/build.zig           |   2 +-
     test/standalone/use_alias/build.zig            |   2 +-
     test/tests.zig                                 | 136 +++---
     test/translate_c.zig                           |  58 +--
     150 files changed, 2162 insertions(+), 2143 deletions(-)
    
    (limited to 'src')
    
    diff --git a/build.zig b/build.zig
    index a4e3dbcdfa..109a799ac9 100644
    --- a/build.zig
    +++ b/build.zig
    @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList;
     const Buffer = std.Buffer;
     const io = std.io;
     
    -pub fn build(b: &Builder) !void {
    +pub fn build(b: *Builder) !void {
         const mode = b.standardReleaseOptions();
     
         var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
    @@ -132,7 +132,7 @@ pub fn build(b: &Builder) !void {
         test_step.dependOn(tests.addGenHTests(b, test_filter));
     }
     
    -fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) void {
    +fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void {
         for (dep.libdirs.toSliceConst()) |lib_dir| {
             lib_exe_obj.addLibPath(lib_dir);
         }
    @@ -147,7 +147,7 @@ fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) vo
         }
     }
     
    -fn addCppLib(b: &Builder, lib_exe_obj: &std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void {
    +fn addCppLib(b: *Builder, lib_exe_obj: *std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void {
         const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
         lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);
     }
    @@ -159,7 +159,7 @@ const LibraryDep = struct {
         includes: ArrayList([]const u8),
     };
     
    -fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep {
    +fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
         const libs_output = try b.exec([][]const u8{
             llvm_config_exe,
             "--libs",
    @@ -217,7 +217,7 @@ fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep {
         return result;
     }
     
    -pub fn installStdLib(b: &Builder, stdlib_files: []const u8) void {
    +pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
         var it = mem.split(stdlib_files, ";");
         while (it.next()) |stdlib_file| {
             const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable;
    @@ -226,7 +226,7 @@ pub fn installStdLib(b: &Builder, stdlib_files: []const u8) void {
         }
     }
     
    -pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void {
    +pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
         var it = mem.split(c_header_files, ";");
         while (it.next()) |c_header_file| {
             const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable;
    @@ -235,7 +235,7 @@ pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void {
         }
     }
     
    -fn nextValue(index: &usize, build_info: []const u8) []const u8 {
    +fn nextValue(index: *usize, build_info: []const u8) []const u8 {
         const start = index.*;
         while (true) : (index.* += 1) {
             switch (build_info[index.*]) {
    diff --git a/doc/docgen.zig b/doc/docgen.zig
    index 7dc444f127..fed4bb8eba 100644
    --- a/doc/docgen.zig
    +++ b/doc/docgen.zig
    @@ -104,7 +104,7 @@ const Tokenizer = struct {
             };
         }
     
    -    fn next(self: &Tokenizer) Token {
    +    fn next(self: *Tokenizer) Token {
             var result = Token{
                 .id = Token.Id.Eof,
                 .start = self.index,
    @@ -196,7 +196,7 @@ const Tokenizer = struct {
             line_end: usize,
         };
     
    -    fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
    +    fn getTokenLocation(self: *Tokenizer, token: *const Token) Location {
             var loc = Location{
                 .line = 0,
                 .column = 0,
    @@ -221,7 +221,7 @@ const Tokenizer = struct {
         }
     };
     
    -fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error {
    +fn parseError(tokenizer: *Tokenizer, token: *const Token, comptime fmt: []const u8, args: ...) error {
         const loc = tokenizer.getTokenLocation(token);
         warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args);
         if (loc.line_start <= loc.line_end) {
    @@ -244,13 +244,13 @@ fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const
         return error.ParseError;
     }
     
    -fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void {
    +fn assertToken(tokenizer: *Tokenizer, token: *const Token, id: Token.Id) !void {
         if (token.id != id) {
             return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
         }
     }
     
    -fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token {
    +fn eatToken(tokenizer: *Tokenizer, id: Token.Id) !Token {
         const token = tokenizer.next();
         try assertToken(tokenizer, token, id);
         return token;
    @@ -317,7 +317,7 @@ const Action = enum {
         Close,
     };
     
    -fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
    +fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
         var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator);
         errdefer urls.deinit();
     
    @@ -546,7 +546,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
         };
     }
     
    -fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 {
    +fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
         var buf = try std.Buffer.initSize(allocator, 0);
         defer buf.deinit();
     
    @@ -566,7 +566,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 {
         return buf.toOwnedSlice();
     }
     
    -fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 {
    +fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
         var buf = try std.Buffer.initSize(allocator, 0);
         defer buf.deinit();
     
    @@ -608,7 +608,7 @@ test "term color" {
         assert(mem.eql(u8, result, "AgreenB"));
     }
     
    -fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 {
    +fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
         var buf = try std.Buffer.initSize(allocator, 0);
         defer buf.deinit();
     
    @@ -688,7 +688,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 {
         return buf.toOwnedSlice();
     }
     
    -fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var, zig_exe: []const u8) !void {
    +fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {
         var code_progress_index: usize = 0;
         for (toc.nodes) |node| {
             switch (node) {
    @@ -1036,7 +1036,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
         }
     }
     
    -fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {
    +fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {
         const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
         switch (result.term) {
             os.ChildProcess.Term.Exited => |exit_code| {
    diff --git a/doc/langref.html.in b/doc/langref.html.in
    index d63c38d0fe..3bd1124e00 100644
    --- a/doc/langref.html.in
    +++ b/doc/langref.html.in
    @@ -458,7 +458,7 @@ test "string literals" {
     
         // A C string literal is a null terminated pointer.
         const null_terminated_bytes = c"hello";
    -    assert(@typeOf(null_terminated_bytes) == &const u8);
    +    assert(@typeOf(null_terminated_bytes) == *const u8);
         assert(null_terminated_bytes[5] == 0);
     }
           {#code_end#}
    @@ -547,7 +547,7 @@ const c_string_literal =
     ;
           {#code_end#}
           

    - In this example the variable c_string_literal has type &const char and + In this example the variable c_string_literal has type *const char and has a terminating null byte.

    {#see_also|@embedFile#} @@ -1403,12 +1403,12 @@ test "address of syntax" { assert(x_ptr.* == 1234); // When you get the address of a const variable, you get a const pointer. - assert(@typeOf(x_ptr) == &const i32); + assert(@typeOf(x_ptr) == *const i32); // If you want to mutate the value, you'd need an address of a mutable variable: var y: i32 = 5678; const y_ptr = &y; - assert(@typeOf(y_ptr) == &i32); + assert(@typeOf(y_ptr) == *i32); y_ptr.* += 1; assert(y_ptr.* == 5679); } @@ -1455,7 +1455,7 @@ comptime { test "@ptrToInt and @intToPtr" { // To convert an integer address into a pointer, use @intToPtr: - const ptr = @intToPtr(&i32, 0xdeadbeef); + const ptr = @intToPtr(*i32, 0xdeadbeef); // To convert a pointer to an integer, use @ptrToInt: const addr = @ptrToInt(ptr); @@ -1467,7 +1467,7 @@ test "@ptrToInt and @intToPtr" { comptime { // Zig is able to do this at compile-time, as long as // ptr is never dereferenced. - const ptr = @intToPtr(&i32, 0xdeadbeef); + const ptr = @intToPtr(*i32, 0xdeadbeef); const addr = @ptrToInt(ptr); assert(@typeOf(addr) == usize); assert(addr == 0xdeadbeef); @@ -1477,17 +1477,17 @@ test "volatile" { // In Zig, loads and stores are assumed to not have side effects. // If a given load or store should have side effects, such as // Memory Mapped Input/Output (MMIO), use `volatile`: - const mmio_ptr = @intToPtr(&volatile u8, 0x12345678); + const mmio_ptr = @intToPtr(*volatile u8, 0x12345678); // Now loads and stores with mmio_ptr are guaranteed to all happen // and in the same order as in source code. - assert(@typeOf(mmio_ptr) == &volatile u8); + assert(@typeOf(mmio_ptr) == *volatile u8); } test "nullable pointers" { // Pointers cannot be null. If you want a null pointer, use the nullable // prefix `?` to make the pointer type nullable. - var ptr: ?&i32 = null; + var ptr: ?*i32 = null; var x: i32 = 1; ptr = &x; @@ -1496,7 +1496,7 @@ test "nullable pointers" { // Nullable pointers are the same size as normal pointers, because pointer // value 0 is used as the null value. - assert(@sizeOf(?&i32) == @sizeOf(&i32)); + assert(@sizeOf(?*i32) == @sizeOf(*i32)); } test "pointer casting" { @@ -1504,7 +1504,7 @@ test "pointer casting" { // operation that Zig cannot protect you against. Use @ptrCast only when other // conversions are not possible. const bytes align(@alignOf(u32)) = []u8{0x12, 0x12, 0x12, 0x12}; - const u32_ptr = @ptrCast(&const u32, &bytes[0]); + const u32_ptr = @ptrCast(*const u32, &bytes[0]); assert(u32_ptr.* == 0x12121212); // Even this example is contrived - there are better ways to do the above than @@ -1518,7 +1518,7 @@ test "pointer casting" { test "pointer child type" { // pointer types have a `child` field which tells you the type they point to. - assert((&u32).Child == u32); + assert((*u32).Child == u32); } {#code_end#} {#header_open|Alignment#} @@ -1543,15 +1543,15 @@ const builtin = @import("builtin"); test "variable alignment" { var x: i32 = 1234; const align_of_i32 = @alignOf(@typeOf(x)); - assert(@typeOf(&x) == &i32); - assert(&i32 == &align(align_of_i32) i32); + assert(@typeOf(&x) == *i32); + assert(*i32 == *align(align_of_i32) i32); if (builtin.arch == builtin.Arch.x86_64) { - assert((&i32).alignment == 4); + assert((*i32).alignment == 4); } } {#code_end#} -

    In the same way that a &i32 can be implicitly cast to a - &const i32, a pointer with a larger alignment can be implicitly +

    In the same way that a *i32 can be implicitly cast to a + *const i32, a pointer with a larger alignment can be implicitly cast to a pointer with a smaller alignment, but not vice versa.

    @@ -1565,7 +1565,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == &align(4) u8); + assert(@typeOf(&foo) == *align(4) u8); const slice = (&foo)[0..1]; assert(@typeOf(slice) == []align(4) u8); } @@ -1610,7 +1610,7 @@ fn foo(bytes: []u8) u32 { u8 can alias any memory.

    As an example, this code produces undefined behavior:

    -
    @ptrCast(&u32, f32(12.34)).*
    +
    @ptrCast(*u32, f32(12.34)).*

    Instead, use {#link|@bitCast#}:

    @bitCast(u32, f32(12.34))

    As an added benefit, the @bitcast version works at compile-time.

    @@ -1736,7 +1736,7 @@ const Vec3 = struct { }; } - pub fn dot(self: &const Vec3, other: &const Vec3) f32 { + pub fn dot(self: *const Vec3, other: *const Vec3) f32 { return self.x * other.x + self.y * other.y + self.z * other.z; } }; @@ -1768,7 +1768,7 @@ test "struct namespaced variable" { // struct field order is determined by the compiler for optimal performance. // however, you can still calculate a struct base pointer given a field pointer: -fn setYBasedOnX(x: &f32, y: f32) void { +fn setYBasedOnX(x: *f32, y: f32) void { const point = @fieldParentPtr(Point, "x", x); point.y = y; } @@ -1786,13 +1786,13 @@ test "field parent pointer" { fn LinkedList(comptime T: type) type { return struct { pub const Node = struct { - prev: ?&Node, - next: ?&Node, + prev: ?*Node, + next: ?*Node, data: T, }; - first: ?&Node, - last: ?&Node, + first: ?*Node, + last: ?*Node, len: usize, }; } @@ -2039,7 +2039,7 @@ const Variant = union(enum) { Int: i32, Bool: bool, - fn truthy(self: &const Variant) bool { + fn truthy(self: *const Variant) bool { return switch (self.*) { Variant.Int => |x_int| x_int != 0, Variant.Bool => |x_bool| x_bool, @@ -2786,7 +2786,7 @@ test "pass aggregate type by value to function" { } {#code_end#}

    - Instead, one must use &const. Zig allows implicitly casting something + Instead, one must use *const. Zig allows implicitly casting something to a const pointer to it:

    {#code_begin|test#} @@ -2794,7 +2794,7 @@ const Foo = struct { x: i32, }; -fn bar(foo: &const Foo) void {} +fn bar(foo: *const Foo) void {} test "implicitly cast to const pointer" { bar(Foo {.x = 12,}); @@ -3208,16 +3208,16 @@ struct Foo *do_a_thing(void) {

    Zig code

    {#code_begin|syntax#} // malloc prototype included for reference -extern fn malloc(size: size_t) ?&u8; +extern fn malloc(size: size_t) ?*u8; -fn doAThing() ?&Foo { +fn doAThing() ?*Foo { const ptr = malloc(1234) ?? return null; // ... } {#code_end#}

    Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" - is &u8 not ?&u8. The ?? operator + is *u8 not ?*u8. The ?? operator unwrapped the nullable type and therefore ptr is guaranteed to be non-null everywhere it is used in the function.

    @@ -3237,7 +3237,7 @@ fn doAThing() ?&Foo { In Zig you can accomplish the same thing:

    {#code_begin|syntax#} -fn doAThing(nullable_foo: ?&Foo) void { +fn doAThing(nullable_foo: ?*Foo) void { // do some stuff if (nullable_foo) |foo| { @@ -3713,7 +3713,7 @@ fn List(comptime T: type) type {

    {#code_begin|syntax#} const Node = struct { - next: &Node, + next: *Node, name: []u8, }; {#code_end#} @@ -3745,7 +3745,7 @@ pub fn main() void { {#code_begin|syntax#} /// Calls print and then flushes the buffer. -pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) error!void { +pub fn printf(self: *OutStream, comptime format: []const u8, args: ...) error!void { const State = enum { Start, OpenBrace, @@ -3817,7 +3817,7 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) error!vo and emits a function that actually looks like this:

    {#code_begin|syntax#} -pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void { +pub fn printf(self: *OutStream, arg0: i32, arg1: []const u8) !void { try self.write("here is a string: '"); try self.printValue(arg0); try self.write("' here is a number: "); @@ -3831,7 +3831,7 @@ pub fn printf(self: &OutStream, arg0: i32, arg1: []const u8) !void { on the type:

    {#code_begin|syntax#} -pub fn printValue(self: &OutStream, value: var) !void { +pub fn printValue(self: *OutStream, value: var) !void { const T = @typeOf(value); if (@isInteger(T)) { return self.printInt(T, value); @@ -3911,7 +3911,7 @@ pub fn main() void { at compile time.

    {#header_open|@addWithOverflow#} -
    @addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
    +
    @addWithOverflow(comptime T: type, a: T, b: T, result: *T) bool

    Performs result.* = a + b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -3919,7 +3919,7 @@ pub fn main() void {

    {#header_close#} {#header_open|@ArgType#} -
    @ArgType(comptime T: type, comptime n: usize) -> type
    +
    @ArgType(comptime T: type, comptime n: usize) type

    This builtin function takes a function type and returns the type of the parameter at index n.

    @@ -3931,7 +3931,7 @@ pub fn main() void {

    {#header_close#} {#header_open|@atomicLoad#} -
    @atomicLoad(comptime T: type, ptr: &const T, comptime ordering: builtin.AtomicOrder) -> T
    +
    @atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T

    This builtin function atomically dereferences a pointer and returns the value.

    @@ -3950,7 +3950,7 @@ pub fn main() void {

    {#header_close#} {#header_open|@atomicRmw#} -
    @atomicRmw(comptime T: type, ptr: &T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -> T
    +
    @atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T

    This builtin function atomically modifies memory and then returns the previous value.

    @@ -3969,7 +3969,7 @@ pub fn main() void {

    {#header_close#} {#header_open|@bitCast#} -
    @bitCast(comptime DestType: type, value: var) -> DestType
    +
    @bitCast(comptime DestType: type, value: var) DestType

    Converts a value of one type to another type.

    @@ -4002,9 +4002,9 @@ pub fn main() void { {#header_close#} {#header_open|@alignCast#} -
    @alignCast(comptime alignment: u29, ptr: var) -> var
    +
    @alignCast(comptime alignment: u29, ptr: var) var

    - ptr can be &T, fn(), ?&T, + ptr can be *T, fn(), ?*T, ?fn(), or []T. It returns the same type as ptr except with the alignment adjusted to the new value.

    @@ -4013,7 +4013,7 @@ pub fn main() void { {#header_close#} {#header_open|@alignOf#} -
    @alignOf(comptime T: type) -> (number literal)
    +
    @alignOf(comptime T: type) (number literal)

    This function returns the number of bytes that this type should be aligned to for the current target to match the C ABI. When the child type of a pointer has @@ -4021,7 +4021,7 @@ pub fn main() void {

    const assert = @import("std").debug.assert;
     comptime {
    -    assert(&u32 == &align(@alignOf(u32)) u32);
    +    assert(*u32 == *align(@alignOf(u32)) u32);
     }

    The result is a target-specific compile time constant. It is guaranteed to be @@ -4049,7 +4049,7 @@ comptime { {#see_also|Import from C Header File|@cInclude|@cImport|@cUndef|void#} {#header_close#} {#header_open|@cImport#} -

    @cImport(expression) -> (namespace)
    +
    @cImport(expression) (namespace)

    This function parses C code and imports the functions, types, variables, and compatible macro definitions into the result namespace. @@ -4095,13 +4095,13 @@ comptime { {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} {#header_close#} {#header_open|@canImplicitCast#} -

    @canImplicitCast(comptime T: type, value) -> bool
    +
    @canImplicitCast(comptime T: type, value) bool

    Returns whether a value can be implicitly casted to a given type.

    {#header_close#} {#header_open|@clz#} -
    @clz(x: T) -> U
    +
    @clz(x: T) U

    This function counts the number of leading zeroes in x which is an integer type T. @@ -4116,13 +4116,13 @@ comptime { {#header_close#} {#header_open|@cmpxchgStrong#} -

    @cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
    +
    @cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T

    This function performs a strong atomic compare exchange operation. It's the equivalent of this code, except atomic:

    {#code_begin|syntax#} -fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { +fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { const old_value = ptr.*; if (old_value == expected_value) { ptr.* = new_value; @@ -4143,13 +4143,13 @@ fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_v {#see_also|Compile Variables|cmpxchgWeak#} {#header_close#} {#header_open|@cmpxchgWeak#} -
    @cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
    +
    @cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T

    This function performs a weak atomic compare exchange operation. It's the equivalent of this code, except atomic:

    {#code_begin|syntax#} -fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { +fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T { const old_value = ptr.*; if (old_value == expected_value and usuallyTrueButSometimesFalse()) { ptr.* = new_value; @@ -4237,7 +4237,7 @@ test "main" { {#code_end#} {#header_close#} {#header_open|@ctz#} -
    @ctz(x: T) -> U
    +
    @ctz(x: T) U

    This function counts the number of trailing zeroes in x which is an integer type T. @@ -4251,7 +4251,7 @@ test "main" {

    {#header_close#} {#header_open|@divExact#} -
    @divExact(numerator: T, denominator: T) -> T
    +
    @divExact(numerator: T, denominator: T) T

    Exact division. Caller guarantees denominator != 0 and @divTrunc(numerator, denominator) * denominator == numerator. @@ -4264,7 +4264,7 @@ test "main" { {#see_also|@divTrunc|@divFloor#} {#header_close#} {#header_open|@divFloor#} -

    @divFloor(numerator: T, denominator: T) -> T
    +
    @divFloor(numerator: T, denominator: T) T

    Floored division. Rounds toward negative infinity. For unsigned integers it is the same as numerator / denominator. Caller guarantees denominator != 0 and @@ -4278,7 +4278,7 @@ test "main" { {#see_also|@divTrunc|@divExact#} {#header_close#} {#header_open|@divTrunc#} -

    @divTrunc(numerator: T, denominator: T) -> T
    +
    @divTrunc(numerator: T, denominator: T) T

    Truncated division. Rounds toward zero. For unsigned integers it is the same as numerator / denominator. Caller guarantees denominator != 0 and @@ -4292,7 +4292,7 @@ test "main" { {#see_also|@divFloor|@divExact#} {#header_close#} {#header_open|@embedFile#} -

    @embedFile(comptime path: []const u8) -> [X]u8
    +
    @embedFile(comptime path: []const u8) [X]u8

    This function returns a compile time constant fixed-size array with length equal to the byte count of the file given by path. The contents of the array @@ -4304,19 +4304,19 @@ test "main" { {#see_also|@import#} {#header_close#} {#header_open|@export#} -

    @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) -> []const u8
    +
    @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8

    Creates a symbol in the output object file.

    {#header_close#} {#header_open|@tagName#} -
    @tagName(value: var) -> []const u8
    +
    @tagName(value: var) []const u8

    Converts an enum value or union value to a slice of bytes representing the name.

    {#header_close#} {#header_open|@TagType#} -
    @TagType(T: type) -> type
    +
    @TagType(T: type) type

    For an enum, returns the integer type that is used to store the enumeration value.

    @@ -4325,7 +4325,7 @@ test "main" {

    {#header_close#} {#header_open|@errorName#} -
    @errorName(err: error) -> []u8
    +
    @errorName(err: error) []u8

    This function returns the string representation of an error. If an error declaration is: @@ -4341,7 +4341,7 @@ test "main" {

    {#header_close#} {#header_open|@errorReturnTrace#} -
    @errorReturnTrace() -> ?&builtin.StackTrace
    +
    @errorReturnTrace() ?*builtin.StackTrace

    If the binary is built with error return tracing, and this function is invoked in a function that calls a function with an error or error union return type, returns a @@ -4360,7 +4360,7 @@ test "main" { {#header_close#} {#header_open|@fieldParentPtr#}

    @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
    -    field_ptr: &T) -> &ParentType
    + field_ptr: *T) *ParentType

    Given a pointer to a field, returns the base pointer of a struct.

    @@ -4380,7 +4380,7 @@ test "main" {

    {#header_close#} {#header_open|@import#} -
    @import(comptime path: []u8) -> (namespace)
    +
    @import(comptime path: []u8) (namespace)

    This function finds a zig file corresponding to path and imports all the public top level declarations into the resulting namespace. @@ -4400,7 +4400,7 @@ test "main" { {#see_also|Compile Variables|@embedFile#} {#header_close#} {#header_open|@inlineCall#} -

    @inlineCall(function: X, args: ...) -> Y
    +
    @inlineCall(function: X, args: ...) Y

    This calls a function, in the same way that invoking an expression with parentheses does:

    @@ -4420,19 +4420,19 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#see_also|@noInlineCall#} {#header_close#} {#header_open|@intToPtr#} -
    @intToPtr(comptime DestType: type, int: usize) -> DestType
    +
    @intToPtr(comptime DestType: type, int: usize) DestType

    Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}.

    {#header_close#} {#header_open|@IntType#} -
    @IntType(comptime is_signed: bool, comptime bit_count: u8) -> type
    +
    @IntType(comptime is_signed: bool, comptime bit_count: u8) type

    This function returns an integer type with the given signness and bit count.

    {#header_close#} {#header_open|@maxValue#} -
    @maxValue(comptime T: type) -> (number literal)
    +
    @maxValue(comptime T: type) (number literal)

    This function returns the maximum value of the integer type T.

    @@ -4441,7 +4441,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }

    {#header_close#} {#header_open|@memberCount#} -
    @memberCount(comptime T: type) -> (number literal)
    +
    @memberCount(comptime T: type) (number literal)

    This function returns the number of members in a struct, enum, or union type.

    @@ -4453,7 +4453,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }

    {#header_close#} {#header_open|@memberName#} -
    @memberName(comptime T: type, comptime index: usize) -> [N]u8
    +
    @memberName(comptime T: type, comptime index: usize) [N]u8

    Returns the field name of a struct, union, or enum.

    The result is a compile time constant. @@ -4463,15 +4463,15 @@ fn add(a: i32, b: i32) i32 { return a + b; }

    {#header_close#} {#header_open|@field#} -
    @field(lhs: var, comptime field_name: []const u8) -> (field)
    +
    @field(lhs: var, comptime field_name: []const u8) (field)

    Preforms field access equivalent to lhs.->field_name-<.

    {#header_close#} {#header_open|@memberType#} -
    @memberType(comptime T: type, comptime index: usize) -> type
    +
    @memberType(comptime T: type, comptime index: usize) type

    Returns the field type of a struct or union.

    {#header_close#} {#header_open|@memcpy#} -
    @memcpy(noalias dest: &u8, noalias source: &const u8, byte_count: usize)
    +
    @memcpy(noalias dest: *u8, noalias source: *const u8, byte_count: usize)

    This function copies bytes from one region of memory to another. dest and source are both pointers and must not overlap. @@ -4489,7 +4489,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } mem.copy(u8, dest[0...byte_count], source[0...byte_count]); {#header_close#} {#header_open|@memset#} -

    @memset(dest: &u8, c: u8, byte_count: usize)
    +
    @memset(dest: *u8, c: u8, byte_count: usize)

    This function sets a region of memory to c. dest is a pointer.

    @@ -4506,7 +4506,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]); mem.set(u8, dest, c); {#header_close#} {#header_open|@minValue#} -
    @minValue(comptime T: type) -> (number literal)
    +
    @minValue(comptime T: type) (number literal)

    This function returns the minimum value of the integer type T.

    @@ -4515,7 +4515,7 @@ mem.set(u8, dest, c);

    {#header_close#} {#header_open|@mod#} -
    @mod(numerator: T, denominator: T) -> T
    +
    @mod(numerator: T, denominator: T) T

    Modulus division. For unsigned integers this is the same as numerator % denominator. Caller guarantees denominator > 0. @@ -4528,7 +4528,7 @@ mem.set(u8, dest, c); {#see_also|@rem#} {#header_close#} {#header_open|@mulWithOverflow#} -

    @mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
    +
    @mulWithOverflow(comptime T: type, a: T, b: T, result: *T) bool

    Performs result.* = a * b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -4536,7 +4536,7 @@ mem.set(u8, dest, c);

    {#header_close#} {#header_open|@newStackCall#} -
    @newStackCall(new_stack: []u8, function: var, args: ...) -> var
    +
    @newStackCall(new_stack: []u8, function: var, args: ...) var

    This calls a function, in the same way that invoking an expression with parentheses does. However, instead of using the same stack as the caller, the function uses the stack provided in the new_stack @@ -4572,7 +4572,7 @@ fn targetFunction(x: i32) usize { {#code_end#} {#header_close#} {#header_open|@noInlineCall#} -

    @noInlineCall(function: var, args: ...) -> var
    +
    @noInlineCall(function: var, args: ...) var

    This calls a function, in the same way that invoking an expression with parentheses does:

    @@ -4594,13 +4594,13 @@ fn add(a: i32, b: i32) i32 { {#see_also|@inlineCall#} {#header_close#} {#header_open|@offsetOf#} -
    @offsetOf(comptime T: type, comptime field_name: [] const u8) -> (number literal)
    +
    @offsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)

    This function returns the byte offset of a field relative to its containing struct.

    {#header_close#} {#header_open|@OpaqueType#} -
    @OpaqueType() -> type
    +
    @OpaqueType() type

    Creates a new type with an unknown size and alignment.

    @@ -4608,12 +4608,12 @@ fn add(a: i32, b: i32) i32 { This is typically used for type safety when interacting with C code that does not expose struct details. Example:

    - {#code_begin|test_err|expected type '&Derp', found '&Wat'#} + {#code_begin|test_err|expected type '*Derp', found '*Wat'#} const Derp = @OpaqueType(); const Wat = @OpaqueType(); -extern fn bar(d: &Derp) void; -export fn foo(w: &Wat) void { +extern fn bar(d: *Derp) void; +export fn foo(w: *Wat) void { bar(w); } @@ -4623,7 +4623,7 @@ test "call foo" { {#code_end#} {#header_close#} {#header_open|@panic#} -
    @panic(message: []const u8) -> noreturn
    +
    @panic(message: []const u8) noreturn

    Invokes the panic handler function. By default the panic handler function calls the public panic function exposed in the root source file, or @@ -4639,19 +4639,19 @@ test "call foo" { {#see_also|Root Source File#} {#header_close#} {#header_open|@ptrCast#} -

    @ptrCast(comptime DestType: type, value: var) -> DestType
    +
    @ptrCast(comptime DestType: type, value: var) DestType

    Converts a pointer of one type to a pointer of another type.

    {#header_close#} {#header_open|@ptrToInt#} -
    @ptrToInt(value: var) -> usize
    +
    @ptrToInt(value: var) usize

    Converts value to a usize which is the address of the pointer. value can be one of these types:

      -
    • &T
    • -
    • ?&T
    • +
    • *T
    • +
    • ?*T
    • fn()
    • ?fn()
    @@ -4659,7 +4659,7 @@ test "call foo" { {#header_close#} {#header_open|@rem#} -
    @rem(numerator: T, denominator: T) -> T
    +
    @rem(numerator: T, denominator: T) T

    Remainder division. For unsigned integers this is the same as numerator % denominator. Caller guarantees denominator > 0. @@ -4776,13 +4776,13 @@ pub const FloatMode = enum { {#see_also|Compile Variables#} {#header_close#} {#header_open|@setGlobalSection#} -

    @setGlobalSection(global_variable_name, comptime section_name: []const u8) -> bool
    +
    @setGlobalSection(global_variable_name, comptime section_name: []const u8) bool

    Puts the global variable in the specified section.

    {#header_close#} {#header_open|@shlExact#} -
    @shlExact(value: T, shift_amt: Log2T) -> T
    +
    @shlExact(value: T, shift_amt: Log2T) T

    Performs the left shift operation (<<). Caller guarantees that the shift will not shift any 1 bits out. @@ -4794,7 +4794,7 @@ pub const FloatMode = enum { {#see_also|@shrExact|@shlWithOverflow#} {#header_close#} {#header_open|@shlWithOverflow#} -

    @shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: &T) -> bool
    +
    @shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: *T) bool

    Performs result.* = a << b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -4807,7 +4807,7 @@ pub const FloatMode = enum { {#see_also|@shlExact|@shrExact#} {#header_close#} {#header_open|@shrExact#} -

    @shrExact(value: T, shift_amt: Log2T) -> T
    +
    @shrExact(value: T, shift_amt: Log2T) T

    Performs the right shift operation (>>). Caller guarantees that the shift will not shift any 1 bits out. @@ -4819,7 +4819,7 @@ pub const FloatMode = enum { {#see_also|@shlExact|@shlWithOverflow#} {#header_close#} {#header_open|@sizeOf#} -

    @sizeOf(comptime T: type) -> (number literal)
    +
    @sizeOf(comptime T: type) (number literal)

    This function returns the number of bytes it takes to store T in memory.

    @@ -4828,7 +4828,7 @@ pub const FloatMode = enum {

    {#header_close#} {#header_open|@sqrt#} -
    @sqrt(comptime T: type, value: T) -> T
    +
    @sqrt(comptime T: type, value: T) T

    Performs the square root of a floating point number. Uses a dedicated hardware instruction when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO. @@ -4838,7 +4838,7 @@ pub const FloatMode = enum {

    {#header_close#} {#header_open|@subWithOverflow#} -
    @subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
    +
    @subWithOverflow(comptime T: type, a: T, b: T, result: *T) bool

    Performs result.* = a - b. If overflow or underflow occurs, stores the overflowed bits in result and returns true. @@ -4846,7 +4846,7 @@ pub const FloatMode = enum {

    {#header_close#} {#header_open|@truncate#} -
    @truncate(comptime T: type, integer) -> T
    +
    @truncate(comptime T: type, integer) T

    This function truncates bits from an integer type, resulting in a smaller integer type. @@ -4870,7 +4870,7 @@ const b: u8 = @truncate(u8, a); {#header_close#} {#header_open|@typeId#} -

    @typeId(comptime T: type) -> @import("builtin").TypeId
    +
    @typeId(comptime T: type) @import("builtin").TypeId

    Returns which kind of type something is. Possible values:

    @@ -4904,7 +4904,7 @@ pub const TypeId = enum { {#code_end#} {#header_close#} {#header_open|@typeInfo#} -
    @typeInfo(comptime T: type) -> @import("builtin").TypeInfo
    +
    @typeInfo(comptime T: type) @import("builtin").TypeInfo

    Returns information on the type. Returns a value of the following union:

    @@ -5080,14 +5080,14 @@ pub const TypeInfo = union(TypeId) { {#code_end#} {#header_close#} {#header_open|@typeName#} -
    @typeName(T: type) -> []u8
    +
    @typeName(T: type) []u8

    This function returns the string representation of a type.

    {#header_close#} {#header_open|@typeOf#} -
    @typeOf(expression) -> type
    +
    @typeOf(expression) type

    This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated. @@ -5937,7 +5937,7 @@ pub const __zig_test_fn_slice = {}; // overwritten later {#header_open|C String Literals#} {#code_begin|exe#} {#link_libc#} -extern fn puts(&const u8) void; +extern fn puts(*const u8) void; pub fn main() void { puts(c"this has a null terminator"); @@ -5996,8 +5996,8 @@ const c = @cImport({ {#code_begin|syntax#} const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: &u8, dest_len: usize, - source_ptr: &const u8, source_len: usize) usize +export fn decode_base_64(dest_ptr: *u8, dest_len: usize, + source_ptr: *const u8, source_len: usize) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; @@ -6028,7 +6028,7 @@ int main(int argc, char **argv) { {#code_begin|syntax#} const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/cat/main.zig b/example/cat/main.zig index de0d323bed..1b34cb22eb 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -41,7 +41,7 @@ fn usage(exe: []const u8) !void { return error.Invalid; } -fn cat_file(stdout: &os.File, file: &os.File) !void { +fn cat_file(stdout: *os.File, file: *os.File) !void { var buf: [1024 * 4]u8 = undefined; while (true) { diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 1df8f04ce4..f64beda40f 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -7,7 +7,7 @@ const c = @cImport({ const msg = c"Hello, world!\n"; -export fn main(argc: c_int, argv: &&u8) c_int { +export fn main(argc: c_int, argv: **u8) c_int { if (c.printf(msg) != c_int(c.strlen(msg))) return -1; return 0; diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig index e682a97055..35b090825b 100644 --- a/example/mix_o_files/base64.zig +++ b/example/mix_o_files/base64.zig @@ -1,6 +1,6 @@ const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) usize { +export fn decode_base_64(dest_ptr: *u8, dest_len: usize, source_ptr: *const u8, source_len: usize) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard_decoder_unsafe; diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index e5d2e6a446..a4e7fbbf8f 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 30c714c6c6..05648cf9eb 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addCExecutable("test"); diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index fa2166e3a5..df2c04ef1f 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -30,7 +30,7 @@ fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool { } // Modifies the current argument index during iteration -fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize, allowed_set: ?[]const []const u8, index: &usize) !FlagArg { +fn readFlagArguments(allocator: *Allocator, args: []const []const u8, required: usize, allowed_set: ?[]const []const u8, index: *usize) !FlagArg { switch (required) { 0 => return FlagArg{ .None = undefined }, // TODO: Required to force non-tag but value? 1 => { @@ -79,7 +79,7 @@ pub const Args = struct { flags: HashMapFlags, positionals: ArrayList([]const u8), - pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args { + pub fn parse(allocator: *Allocator, comptime spec: []const Flag, args: []const []const u8) !Args { var parsed = Args{ .flags = HashMapFlags.init(allocator), .positionals = ArrayList([]const u8).init(allocator), @@ -143,18 +143,18 @@ pub const Args = struct { return parsed; } - pub fn deinit(self: &Args) void { + pub fn deinit(self: *Args) void { self.flags.deinit(); self.positionals.deinit(); } // e.g. --help - pub fn present(self: &Args, name: []const u8) bool { + pub fn present(self: *Args, name: []const u8) bool { return self.flags.contains(name); } // e.g. --name value - pub fn single(self: &Args, name: []const u8) ?[]const u8 { + pub fn single(self: *Args, name: []const u8) ?[]const u8 { if (self.flags.get(name)) |entry| { switch (entry.value) { FlagArg.Single => |inner| { @@ -168,7 +168,7 @@ pub const Args = struct { } // e.g. --names value1 value2 value3 - pub fn many(self: &Args, name: []const u8) ?[]const []const u8 { + pub fn many(self: *Args, name: []const u8) ?[]const []const u8 { if (self.flags.get(name)) |entry| { switch (entry.value) { FlagArg.Many => |inner| { diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 9905b8e3a6..32d2450aac 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -16,18 +16,18 @@ pub const Msg = struct { text: []u8, first_token: TokenIndex, last_token: TokenIndex, - tree: &ast.Tree, + tree: *ast.Tree, }; /// `path` must outlive the returned Msg /// `tree` must outlive the returned Msg /// Caller owns returned Msg and must free with `allocator` pub fn createFromParseError( - allocator: &mem.Allocator, - parse_error: &const ast.Error, - tree: &ast.Tree, + allocator: *mem.Allocator, + parse_error: *const ast.Error, + tree: *ast.Tree, path: []const u8, -) !&Msg { +) !*Msg { const loc_token = parse_error.loc(); var text_buf = try std.Buffer.initSize(allocator, 0); defer text_buf.deinit(); @@ -47,7 +47,7 @@ pub fn createFromParseError( return msg; } -pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void { +pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { const first_token = msg.tree.tokens.at(msg.first_token); const last_token = msg.tree.tokens.at(msg.last_token); const start_loc = msg.tree.tokenLocationPtr(0, first_token); @@ -76,7 +76,7 @@ pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void { try stream.write("\n"); } -pub fn printToFile(file: &os.File, msg: &const Msg, color: Color) !void { +pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void { const color_on = switch (color) { Color.Auto => file.isTty(), Color.On => true, diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index adab00286b..56b56c0c78 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -7,7 +7,7 @@ const os = std.os; const warn = std.debug.warn; /// Caller must free result -pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 { +pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig"); errdefer allocator.free(test_zig_dir); @@ -21,7 +21,7 @@ pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![ } /// Caller must free result -pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 { +pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 { const self_exe_path = try os.selfExeDirPath(allocator); defer allocator.free(self_exe_path); @@ -42,7 +42,7 @@ pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 { return error.FileNotFound; } -pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 { +pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { return findZigLibDir(allocator) catch |err| { warn( \\Unable to find zig lib directory: {}. diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c4550b5179..3334d9511b 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -2,7 +2,7 @@ const Scope = @import("scope.zig").Scope; pub const Instruction = struct { id: Id, - scope: &Scope, + scope: *Scope, pub const Id = enum { Br, diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 71838503b7..80b1c3889a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -18,8 +18,8 @@ const Target = @import("target.zig").Target; const errmsg = @import("errmsg.zig"); var stderr_file: os.File = undefined; -var stderr: &io.OutStream(io.FileOutStream.Error) = undefined; -var stdout: &io.OutStream(io.FileOutStream.Error) = undefined; +var stderr: *io.OutStream(io.FileOutStream.Error) = undefined; +var stdout: *io.OutStream(io.FileOutStream.Error) = undefined; const usage = \\usage: zig [command] [options] @@ -43,7 +43,7 @@ const usage = const Command = struct { name: []const u8, - exec: fn (&Allocator, []const []const u8) error!void, + exec: fn (*Allocator, []const []const u8) error!void, }; pub fn main() !void { @@ -191,7 +191,7 @@ const missing_build_file = \\ ; -fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_build_spec, args); defer flags.deinit(); @@ -426,7 +426,7 @@ const args_build_generic = []Flag{ Flag.Arg1("--ver-patch"), }; -fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void { +fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Module.Kind) !void { var flags = try Args.parse(allocator, args_build_generic, args); defer flags.deinit(); @@ -661,19 +661,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo try stderr.print("building {}: {}\n", @tagName(out_type), in_file); } -fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void { try buildOutputType(allocator, args, Module.Kind.Exe); } // cmd:build-lib /////////////////////////////////////////////////////////////////////////////////// -fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void { try buildOutputType(allocator, args, Module.Kind.Lib); } // cmd:build-obj /////////////////////////////////////////////////////////////////////////////////// -fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void { +fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { try buildOutputType(allocator, args, Module.Kind.Obj); } @@ -700,7 +700,7 @@ const args_fmt_spec = []Flag{ }), }; -fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { +fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_fmt_spec, args); defer flags.deinit(); @@ -768,7 +768,7 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { // cmd:targets ///////////////////////////////////////////////////////////////////////////////////// -fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { +fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { try stdout.write("Architectures:\n"); { comptime var i: usize = 0; @@ -810,7 +810,7 @@ fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { // cmd:version ///////////////////////////////////////////////////////////////////////////////////// -fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void { +fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING)); } @@ -827,7 +827,7 @@ const usage_test = const args_test_spec = []Flag{Flag.Bool("--help")}; -fn cmdTest(allocator: &Allocator, args: []const []const u8) !void { +fn cmdTest(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_build_spec, args); defer flags.deinit(); @@ -862,7 +862,7 @@ const usage_run = const args_run_spec = []Flag{Flag.Bool("--help")}; -fn cmdRun(allocator: &Allocator, args: []const []const u8) !void { +fn cmdRun(allocator: *Allocator, args: []const []const u8) !void { var compile_args = args; var runtime_args: []const []const u8 = []const []const u8{}; @@ -912,7 +912,7 @@ const args_translate_c_spec = []Flag{ Flag.Arg1("--output"), }; -fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void { +fn cmdTranslateC(allocator: *Allocator, args: []const []const u8) !void { var flags = try Args.parse(allocator, args_translate_c_spec, args); defer flags.deinit(); @@ -958,7 +958,7 @@ fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void { // cmd:help //////////////////////////////////////////////////////////////////////////////////////// -fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void { +fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { try stderr.write(usage); } @@ -981,7 +981,7 @@ const info_zen = \\ ; -fn cmdZen(allocator: &Allocator, args: []const []const u8) !void { +fn cmdZen(allocator: *Allocator, args: []const []const u8) !void { try stdout.write(info_zen); } @@ -996,7 +996,7 @@ const usage_internal = \\ ; -fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void { +fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { if (args.len == 0) { try stderr.write(usage_internal); os.exit(1); @@ -1018,7 +1018,7 @@ fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void { try stderr.write(usage_internal); } -fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void { +fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { try stdout.print( \\ZIG_CMAKE_BINARY_DIR {} \\ZIG_CXX_COMPILER {} diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 61834eab66..a7ddf3f9e9 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -13,7 +13,7 @@ const ArrayList = std.ArrayList; const errmsg = @import("errmsg.zig"); pub const Module = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, name: Buffer, root_src_path: ?[]const u8, module: llvm.ModuleRef, @@ -53,8 +53,8 @@ pub const Module = struct { windows_subsystem_windows: bool, windows_subsystem_console: bool, - link_libs_list: ArrayList(&LinkLib), - libc_link_lib: ?&LinkLib, + link_libs_list: ArrayList(*LinkLib), + libc_link_lib: ?*LinkLib, err_color: errmsg.Color, @@ -106,19 +106,19 @@ pub const Module = struct { pub const CliPkg = struct { name: []const u8, path: []const u8, - children: ArrayList(&CliPkg), - parent: ?&CliPkg, + children: ArrayList(*CliPkg), + parent: ?*CliPkg, - pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg { + pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { var pkg = try allocator.create(CliPkg); pkg.name = name; pkg.path = path; - pkg.children = ArrayList(&CliPkg).init(allocator); + pkg.children = ArrayList(*CliPkg).init(allocator); pkg.parent = parent; return pkg; } - pub fn deinit(self: &CliPkg) void { + pub fn deinit(self: *CliPkg) void { for (self.children.toSliceConst()) |child| { child.deinit(); } @@ -126,7 +126,7 @@ pub const Module = struct { } }; - pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target, kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module { + pub fn create(allocator: *mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: *const Target, kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !*Module { var name_buffer = try Buffer.init(allocator, name); errdefer name_buffer.deinit(); @@ -188,7 +188,7 @@ pub const Module = struct { .link_objects = [][]const u8{}, .windows_subsystem_windows = false, .windows_subsystem_console = false, - .link_libs_list = ArrayList(&LinkLib).init(allocator), + .link_libs_list = ArrayList(*LinkLib).init(allocator), .libc_link_lib = null, .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, @@ -200,11 +200,11 @@ pub const Module = struct { return module_ptr; } - fn dump(self: &Module) void { + fn dump(self: *Module) void { c.LLVMDumpModule(self.module); } - pub fn destroy(self: &Module) void { + pub fn destroy(self: *Module) void { c.LLVMDisposeBuilder(self.builder); c.LLVMDisposeModule(self.module); c.LLVMContextDispose(self.context); @@ -213,7 +213,7 @@ pub const Module = struct { self.allocator.destroy(self); } - pub fn build(self: &Module) !void { + pub fn build(self: *Module) !void { if (self.llvm_argv.len != 0) { var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator, [][]const []const u8{ [][]const u8{"zig (LLVM option parsing)"}, @@ -259,12 +259,12 @@ pub const Module = struct { self.dump(); } - pub fn link(self: &Module, out_file: ?[]const u8) !void { + pub fn link(self: *Module, out_file: ?[]const u8) !void { warn("TODO link"); return error.Todo; } - pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib { + pub fn addLinkLib(self: *Module, name: []const u8, provided_explicitly: bool) !*LinkLib { const is_libc = mem.eql(u8, name, "c"); if (is_libc) { diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 05e586daae..b73dcb4ed3 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,6 +1,6 @@ pub const Scope = struct { id: Id, - parent: &Scope, + parent: *Scope, pub const Id = enum { Decls, diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 7983a3ddec..724d99ea23 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -11,7 +11,7 @@ pub const Target = union(enum) { Native, Cross: CrossTarget, - pub fn oFileExt(self: &const Target) []const u8 { + pub fn oFileExt(self: *const Target) []const u8 { const environ = switch (self.*) { Target.Native => builtin.environ, Target.Cross => |t| t.environ, @@ -22,28 +22,28 @@ pub const Target = union(enum) { }; } - pub fn exeFileExt(self: &const Target) []const u8 { + pub fn exeFileExt(self: *const Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn getOs(self: &const Target) builtin.Os { + pub fn getOs(self: *const Target) builtin.Os { return switch (self.*) { Target.Native => builtin.os, Target.Cross => |t| t.os, }; } - pub fn isDarwin(self: &const Target) bool { + pub fn isDarwin(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: &const Target) bool { + pub fn isWindows(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, diff --git a/src/all_types.hpp b/src/all_types.hpp index 9c156fb58b..b9199c2757 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -374,7 +374,7 @@ enum NodeType { NodeTypeCharLiteral, NodeTypeSymbol, NodeTypePrefixOpExpr, - NodeTypeAddrOfExpr, + NodeTypePointerType, NodeTypeFnCallExpr, NodeTypeArrayAccessExpr, NodeTypeSliceExpr, @@ -616,6 +616,7 @@ enum PrefixOp { PrefixOpNegationWrap, PrefixOpMaybe, PrefixOpUnwrapMaybe, + PrefixOpAddrOf, }; struct AstNodePrefixOpExpr { @@ -623,7 +624,7 @@ struct AstNodePrefixOpExpr { AstNode *primary_expr; }; -struct AstNodeAddrOfExpr { +struct AstNodePointerType { AstNode *align_expr; BigInt *bit_offset_start; BigInt *bit_offset_end; @@ -899,7 +900,7 @@ struct AstNode { AstNodeBinOpExpr bin_op_expr; AstNodeCatchExpr unwrap_err_expr; AstNodePrefixOpExpr prefix_op_expr; - AstNodeAddrOfExpr addr_of_expr; + AstNodePointerType pointer_type; AstNodeFnCallExpr fn_call_expr; AstNodeArrayAccessExpr array_access_expr; AstNodeSliceExpr slice_expr; @@ -2053,7 +2054,7 @@ enum IrInstructionId { IrInstructionIdTypeInfo, IrInstructionIdTypeId, IrInstructionIdSetEvalBranchQuota, - IrInstructionIdPtrTypeOf, + IrInstructionIdPtrType, IrInstructionIdAlignCast, IrInstructionIdOpaqueType, IrInstructionIdSetAlignStack, @@ -2412,6 +2413,17 @@ struct IrInstructionArrayType { IrInstruction *child_type; }; +struct IrInstructionPtrType { + IrInstruction base; + + IrInstruction *align_value; + IrInstruction *child_type; + uint32_t bit_offset_start; + uint32_t bit_offset_end; + bool is_const; + bool is_volatile; +}; + struct IrInstructionPromiseType { IrInstruction base; @@ -2891,17 +2903,6 @@ struct IrInstructionSetEvalBranchQuota { IrInstruction *new_quota; }; -struct IrInstructionPtrTypeOf { - IrInstruction base; - - IrInstruction *align_value; - IrInstruction *child_type; - uint32_t bit_offset_start; - uint32_t bit_offset_end; - bool is_const; - bool is_volatile; -}; - struct IrInstructionAlignCast { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index b00e18a9a1..a5011035c5 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -418,12 +418,12 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { - buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "*%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); } else if (unaligned_bit_count == 0) { - buf_appendf(&entry->name, "&align(%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "*align(%" PRIu32 ") %s%s%s", byte_alignment, const_str, volatile_str, buf_ptr(&child_type->name)); } else { - buf_appendf(&entry->name, "&align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "*align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment, bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } @@ -3270,7 +3270,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeThisLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: - case NodeTypeAddrOfExpr: + case NodeTypePointerType: case NodeTypeIfBoolExpr: case NodeTypeWhileExpr: case NodeTypeForExpr: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 5a1e81b36d..f356f406b0 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -68,6 +68,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpBinNot: return "~"; case PrefixOpMaybe: return "?"; case PrefixOpUnwrapMaybe: return "??"; + case PrefixOpAddrOf: return "&"; } zig_unreachable(); } @@ -185,8 +186,6 @@ static const char *node_type_str(NodeType node_type) { return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; - case NodeTypeAddrOfExpr: - return "AddrOfExpr"; case NodeTypeUse: return "Use"; case NodeTypeBoolLiteral: @@ -251,6 +250,8 @@ static const char *node_type_str(NodeType node_type) { return "Suspend"; case NodeTypePromiseType: return "PromiseType"; + case NodeTypePointerType: + return "PointerType"; } zig_unreachable(); } @@ -616,41 +617,41 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, "%s", prefix_op_str(op)); AstNode *child_node = node->data.prefix_op_expr.primary_expr; - bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypeAddrOfExpr; + bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType; render_node_extra(ar, child_node, new_grouped); if (!grouped) fprintf(ar->f, ")"); break; } - case NodeTypeAddrOfExpr: + case NodeTypePointerType: { if (!grouped) fprintf(ar->f, "("); - fprintf(ar->f, "&"); - if (node->data.addr_of_expr.align_expr != nullptr) { + fprintf(ar->f, "*"); + if (node->data.pointer_type.align_expr != nullptr) { fprintf(ar->f, "align("); - render_node_grouped(ar, node->data.addr_of_expr.align_expr); - if (node->data.addr_of_expr.bit_offset_start != nullptr) { - assert(node->data.addr_of_expr.bit_offset_end != nullptr); + render_node_grouped(ar, node->data.pointer_type.align_expr); + if (node->data.pointer_type.bit_offset_start != nullptr) { + assert(node->data.pointer_type.bit_offset_end != nullptr); Buf offset_start_buf = BUF_INIT; buf_resize(&offset_start_buf, 0); - bigint_append_buf(&offset_start_buf, node->data.addr_of_expr.bit_offset_start, 10); + bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10); Buf offset_end_buf = BUF_INIT; buf_resize(&offset_end_buf, 0); - bigint_append_buf(&offset_end_buf, node->data.addr_of_expr.bit_offset_end, 10); + bigint_append_buf(&offset_end_buf, node->data.pointer_type.bit_offset_end, 10); fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf)); } fprintf(ar->f, ") "); } - if (node->data.addr_of_expr.is_const) { + if (node->data.pointer_type.is_const) { fprintf(ar->f, "const "); } - if (node->data.addr_of_expr.is_volatile) { + if (node->data.pointer_type.is_volatile) { fprintf(ar->f, "volatile "); } - render_node_ungrouped(ar, node->data.addr_of_expr.op_expr); + render_node_ungrouped(ar, node->data.pointer_type.op_expr); if (!grouped) fprintf(ar->f, ")"); break; } @@ -669,7 +670,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, " "); } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr); + bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType); render_node_extra(ar, fn_ref_node, grouped); fprintf(ar->f, "("); for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 69542b3e67..d07d427729 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4600,7 +4600,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdTypeInfo: case IrInstructionIdTypeId: case IrInstructionIdSetEvalBranchQuota: - case IrInstructionIdPtrTypeOf: + case IrInstructionIdPtrType: case IrInstructionIdOpaqueType: case IrInstructionIdSetAlignStack: case IrInstructionIdArgType: diff --git a/src/ir.cpp b/src/ir.cpp index 6e944a8976..b1fac9f485 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -41,10 +41,6 @@ struct IrAnalyze { static const LVal LVAL_NONE = { false, false, false }; static const LVal LVAL_PTR = { true, false, false }; -static LVal make_lval_addr(bool is_const, bool is_volatile) { - return { true, is_const, is_volatile }; -} - enum ConstCastResultId { ConstCastResultIdOk, ConstCastResultIdErrSet, @@ -629,8 +625,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetEvalBranchQuo return IrInstructionIdSetEvalBranchQuota; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeOf *) { - return IrInstructionIdPtrTypeOf; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrType *) { + return IrInstructionIdPtrType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) { @@ -1196,11 +1192,11 @@ static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instru return new_instruction; } -static IrInstruction *ir_build_ptr_type_of(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, uint32_t bit_offset_start, uint32_t bit_offset_end) { - IrInstructionPtrTypeOf *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; @@ -4609,14 +4605,8 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { - AstNode *expr_node; - if (node->type == NodeTypePrefixOpExpr) { - expr_node = node->data.prefix_op_expr.primary_expr; - } else if (node->type == NodeTypePtrDeref) { - expr_node = node->data.ptr_deref_expr.target; - } else { - zig_unreachable(); - } + assert(node->type == NodeTypePrefixOpExpr); + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) @@ -4640,16 +4630,12 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * return ir_build_ref(irb, scope, value->source_node, value, lval.is_const, lval.is_volatile); } -static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeAddrOfExpr); - bool is_const = node->data.addr_of_expr.is_const; - bool is_volatile = node->data.addr_of_expr.is_volatile; - AstNode *expr_node = node->data.addr_of_expr.op_expr; - AstNode *align_expr = node->data.addr_of_expr.align_expr; - - if (align_expr == nullptr && !is_const && !is_volatile) { - return ir_gen_node_extra(irb, expr_node, scope, make_lval_addr(is_const, is_volatile)); - } +static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypePointerType); + bool is_const = node->data.pointer_type.is_const; + bool is_volatile = node->data.pointer_type.is_volatile; + AstNode *expr_node = node->data.pointer_type.op_expr; + AstNode *align_expr = node->data.pointer_type.align_expr; IrInstruction *align_value; if (align_expr != nullptr) { @@ -4665,27 +4651,27 @@ static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *n return child_type; uint32_t bit_offset_start = 0; - if (node->data.addr_of_expr.bit_offset_start != nullptr) { - if (!bigint_fits_in_bits(node->data.addr_of_expr.bit_offset_start, 32, false)) { + if (node->data.pointer_type.bit_offset_start != nullptr) { + if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, node->data.addr_of_expr.bit_offset_start, 10); + bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); return irb->codegen->invalid_instruction; } - bit_offset_start = bigint_as_unsigned(node->data.addr_of_expr.bit_offset_start); + bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start); } uint32_t bit_offset_end = 0; - if (node->data.addr_of_expr.bit_offset_end != nullptr) { - if (!bigint_fits_in_bits(node->data.addr_of_expr.bit_offset_end, 32, false)) { + if (node->data.pointer_type.bit_offset_end != nullptr) { + if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, node->data.addr_of_expr.bit_offset_end, 10); + bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10); exec_add_error_node(irb->codegen, irb->exec, node, buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); return irb->codegen->invalid_instruction; } - bit_offset_end = bigint_as_unsigned(node->data.addr_of_expr.bit_offset_end); + bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end); } if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) { @@ -4694,7 +4680,7 @@ static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *n return irb->codegen->invalid_instruction; } - return ir_build_ptr_type_of(irb, scope, node, child_type, is_const, is_volatile, + return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, align_value, bit_offset_start, bit_offset_end); } @@ -4761,6 +4747,10 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); case PrefixOpUnwrapMaybe: return ir_gen_maybe_assert_ok(irb, scope, node, lval); + case PrefixOpAddrOf: { + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval); + } } zig_unreachable(); } @@ -6568,8 +6558,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); case NodeTypePrefixOpExpr: return ir_gen_prefix_op_expr(irb, scope, node, lval); - case NodeTypeAddrOfExpr: - return ir_lval_wrap(irb, scope, ir_gen_address_of(irb, scope, node), lval); case NodeTypeContainerInitExpr: return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval); case NodeTypeVariableDeclaration: @@ -6592,14 +6580,23 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, ptr_instruction); } - case NodeTypePtrDeref: - return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); + case NodeTypePtrDeref: { + assert(node->type == NodeTypePtrDeref); + AstNode *expr_node = node->data.ptr_deref_expr.target; + IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); + if (value == irb->codegen->invalid_instruction) + return value; + + return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); + } case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval); case NodeTypeArrayType: return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval); + case NodeTypePointerType: + return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval); case NodeTypePromiseType: return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval); case NodeTypeStringLiteral: @@ -8961,6 +8958,7 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstExprValue *pointee, TypeTableEntry *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { + // TODO remove this special case for types if (pointee_type->id == TypeTableEntryIdMetaType) { TypeTableEntry *type_entry = pointee->data.x_type; if (type_entry->id == TypeTableEntryIdUnreachable) { @@ -18778,11 +18776,16 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr return usize; } -static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInstructionPtrTypeOf *instruction) { +static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { TypeTableEntry *child_type = ir_resolve_type(ira, instruction->child_type->other); if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; + if (child_type->id == TypeTableEntryIdUnreachable) { + ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); + return ira->codegen->builtin_types.entry_invalid; + } + uint32_t align_bytes; if (instruction->align_value != nullptr) { if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) @@ -19606,8 +19609,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); case IrInstructionIdSetEvalBranchQuota: return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction); - case IrInstructionIdPtrTypeOf: - return ir_analyze_instruction_ptr_type_of(ira, (IrInstructionPtrTypeOf *)instruction); + case IrInstructionIdPtrType: + return ir_analyze_instruction_ptr_type(ira, (IrInstructionPtrType *)instruction); case IrInstructionIdAlignCast: return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction); case IrInstructionIdOpaqueType: @@ -19783,7 +19786,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdPanic: case IrInstructionIdSetEvalBranchQuota: - case IrInstructionIdPtrTypeOf: + case IrInstructionIdPtrType: case IrInstructionIdSetAlignStack: case IrInstructionIdExport: case IrInstructionIdCancel: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 9678120f1d..3c177a8bbf 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -921,7 +921,7 @@ static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCas fprintf(irp->f, ")"); } -static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instruction) { +static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { fprintf(irp->f, "&"); if (instruction->align_value != nullptr) { fprintf(irp->f, "align("); @@ -1527,8 +1527,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCanImplicitCast: ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); break; - case IrInstructionIdPtrTypeOf: - ir_print_ptr_type_of(irp, (IrInstructionPtrTypeOf *)instruction); + case IrInstructionIdPtrType: + ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction); break; case IrInstructionIdDeclRef: ir_print_decl_ref(irp, (IrInstructionDeclRef *)instruction); diff --git a/src/parser.cpp b/src/parser.cpp index 4763d3b987..ef390a3a2e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1167,20 +1167,19 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdTilde: return PrefixOpBinNot; case TokenIdMaybe: return PrefixOpMaybe; case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; + case TokenIdAmpersand: return PrefixOpAddrOf; default: return PrefixOpInvalid; } } -static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { - Token *ampersand_tok = ast_eat_token(pc, token_index, TokenIdAmpersand); - - AstNode *node = ast_create_node(pc, NodeTypeAddrOfExpr, ampersand_tok); +static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) { + AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok); Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdKeywordAlign) { *token_index += 1; ast_eat_token(pc, token_index, TokenIdLParen); - node->data.addr_of_expr.align_expr = ast_parse_expression(pc, token_index, true); + node->data.pointer_type.align_expr = ast_parse_expression(pc, token_index, true); token = &pc->tokens->at(*token_index); if (token->id == TokenIdColon) { @@ -1189,24 +1188,24 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) { ast_eat_token(pc, token_index, TokenIdColon); Token *bit_offset_end_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral); - node->data.addr_of_expr.bit_offset_start = token_bigint(bit_offset_start_tok); - node->data.addr_of_expr.bit_offset_end = token_bigint(bit_offset_end_tok); + node->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start_tok); + node->data.pointer_type.bit_offset_end = token_bigint(bit_offset_end_tok); } ast_eat_token(pc, token_index, TokenIdRParen); token = &pc->tokens->at(*token_index); } if (token->id == TokenIdKeywordConst) { *token_index += 1; - node->data.addr_of_expr.is_const = true; + node->data.pointer_type.is_const = true; token = &pc->tokens->at(*token_index); } if (token->id == TokenIdKeywordVolatile) { *token_index += 1; - node->data.addr_of_expr.is_volatile = true; + node->data.pointer_type.is_volatile = true; } - node->data.addr_of_expr.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); + node->data.pointer_type.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); return node; } @@ -1216,8 +1215,17 @@ PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integ */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdAmpersand) { - return ast_parse_addr_of(pc, token_index); + if (token->id == TokenIdStar) { + *token_index += 1; + return ast_parse_pointer_type(pc, token_index, token); + } + if (token->id == TokenIdStarStar) { + *token_index += 1; + AstNode *child_node = ast_parse_pointer_type(pc, token_index, token); + child_node->column += 1; + AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token); + parent_node->data.pointer_type.op_expr = child_node; + return parent_node; } if (token->id == TokenIdKeywordTry) { return ast_parse_try_expr(pc, token_index); @@ -1234,13 +1242,12 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); - AstNode *parent_node = node; AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true); node->data.prefix_op_expr.primary_expr = prefix_op_expr; node->data.prefix_op_expr.prefix_op = prefix_op; - return parent_node; + return node; } @@ -3121,9 +3128,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeErrorType: // none break; - case NodeTypeAddrOfExpr: - visit_field(&node->data.addr_of_expr.align_expr, visit, context); - visit_field(&node->data.addr_of_expr.op_expr, visit, context); + case NodeTypePointerType: + visit_field(&node->data.pointer_type.align_expr, visit, context); + visit_field(&node->data.pointer_type.op_expr, visit, context); break; case NodeTypeErrorSetDecl: visit_node_list(&node->data.err_set_decl.decls, visit, context); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 50ff073008..db541d34f3 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -276,11 +276,18 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod node); } -static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { - AstNode *node = trans_create_node(c, NodeTypeAddrOfExpr); - node->data.addr_of_expr.is_const = is_const; - node->data.addr_of_expr.is_volatile = is_volatile; - node->data.addr_of_expr.op_expr = child_node; +static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypePointerType); + node->data.pointer_type.is_const = is_const; + node->data.pointer_type.is_volatile = is_volatile; + node->data.pointer_type.op_expr = child_node; + return node; +} + +static AstNode *trans_create_node_addr_of(Context *c, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); + node->data.prefix_op_expr.prefix_op = PrefixOpAddrOf; + node->data.prefix_op_expr.primary_expr = child_node; return node; } @@ -848,7 +855,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); } - AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(), + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), child_qt.isVolatileQualified(), child_node); return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); } @@ -1033,7 +1040,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou emit_warning(c, source_loc, "unresolved array element type"); return nullptr; } - AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(), + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), child_qt.isVolatileQualified(), child_type_node); return pointer_node; } @@ -1402,7 +1409,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result // const _ref = &lhs; AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue); if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); + AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs); // TODO: avoid name collisions with generated variable names Buf* tmp_var_name = buf_create_from_str("_ref"); AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); @@ -1476,7 +1483,7 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, // const _ref = &lhs; AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue); if (lhs == nullptr) return nullptr; - AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs); + AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs); // TODO: avoid name collisions with generated variable names Buf* tmp_var_name = buf_create_from_str("_ref"); AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs); @@ -1813,7 +1820,7 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr // const _ref = &expr; AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue); if (expr == nullptr) return nullptr; - AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); + AstNode *addr_of_expr = trans_create_node_addr_of(c, expr); // TODO: avoid name collisions with generated variable names Buf* ref_var_name = buf_create_from_str("_ref"); AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); @@ -1868,7 +1875,7 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra // const _ref = &expr; AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue); if (expr == nullptr) return nullptr; - AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr); + AstNode *addr_of_expr = trans_create_node_addr_of(c, expr); // TODO: avoid name collisions with generated variable names Buf* ref_var_name = buf_create_from_str("_ref"); AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr); @@ -1917,7 +1924,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue); if (value_node == nullptr) return value_node; - return trans_create_node_addr_of(c, false, false, value_node); + return trans_create_node_addr_of(c, value_node); } case UO_Deref: { @@ -4441,7 +4448,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } else if (first_tok->id == CTokIdAsterisk) { *tok_i += 1; - node = trans_create_node_addr_of(c, false, false, node); + node = trans_create_node_ptr_type(c, false, false, node); } else { return node; } diff --git a/std/array_list.zig b/std/array_list.zig index b315194c33..07a1db6451 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -17,10 +17,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { /// you uninitialized memory. items: []align(A) T, len: usize, - allocator: &Allocator, + allocator: *Allocator, /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn init(allocator: &Allocator) Self { + pub fn init(allocator: *Allocator) Self { return Self{ .items = []align(A) T{}, .len = 0, @@ -28,30 +28,30 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } - pub fn deinit(l: &const Self) void { + pub fn deinit(l: *const Self) void { l.allocator.free(l.items); } - pub fn toSlice(l: &const Self) []align(A) T { + pub fn toSlice(l: *const Self) []align(A) T { return l.items[0..l.len]; } - pub fn toSliceConst(l: &const Self) []align(A) const T { + pub fn toSliceConst(l: *const Self) []align(A) const T { return l.items[0..l.len]; } - pub fn at(l: &const Self, n: usize) T { + pub fn at(l: *const Self, n: usize) T { return l.toSliceConst()[n]; } - pub fn count(self: &const Self) usize { + pub fn count(self: *const Self) usize { return self.len; } /// ArrayList takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Deinitialize with `deinit` or use `toOwnedSlice`. - pub fn fromOwnedSlice(allocator: &Allocator, slice: []align(A) T) Self { + pub fn fromOwnedSlice(allocator: *Allocator, slice: []align(A) T) Self { return Self{ .items = slice, .len = slice.len, @@ -60,14 +60,14 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { } /// The caller owns the returned memory. ArrayList becomes empty. - pub fn toOwnedSlice(self: &Self) []align(A) T { + pub fn toOwnedSlice(self: *Self) []align(A) T { const allocator = self.allocator; const result = allocator.alignedShrink(T, A, self.items, self.len); self.* = init(allocator); return result; } - pub fn insert(l: &Self, n: usize, item: &const T) !void { + pub fn insert(l: *Self, n: usize, item: *const T) !void { try l.ensureCapacity(l.len + 1); l.len += 1; @@ -75,7 +75,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { l.items[n] = item.*; } - pub fn insertSlice(l: &Self, n: usize, items: []align(A) const T) !void { + pub fn insertSlice(l: *Self, n: usize, items: []align(A) const T) !void { try l.ensureCapacity(l.len + items.len); l.len += items.len; @@ -83,28 +83,28 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { mem.copy(T, l.items[n .. n + items.len], items); } - pub fn append(l: &Self, item: &const T) !void { + pub fn append(l: *Self, item: *const T) !void { const new_item_ptr = try l.addOne(); new_item_ptr.* = item.*; } - pub fn appendSlice(l: &Self, items: []align(A) const T) !void { + pub fn appendSlice(l: *Self, items: []align(A) const T) !void { try l.ensureCapacity(l.len + items.len); mem.copy(T, l.items[l.len..], items); l.len += items.len; } - pub fn resize(l: &Self, new_len: usize) !void { + pub fn resize(l: *Self, new_len: usize) !void { try l.ensureCapacity(new_len); l.len = new_len; } - pub fn shrink(l: &Self, new_len: usize) void { + pub fn shrink(l: *Self, new_len: usize) void { assert(new_len <= l.len); l.len = new_len; } - pub fn ensureCapacity(l: &Self, new_capacity: usize) !void { + pub fn ensureCapacity(l: *Self, new_capacity: usize) !void { var better_capacity = l.items.len; if (better_capacity >= new_capacity) return; while (true) { @@ -114,7 +114,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity); } - pub fn addOne(l: &Self) !&T { + pub fn addOne(l: *Self) !*T { const new_length = l.len + 1; try l.ensureCapacity(new_length); const result = &l.items[l.len]; @@ -122,34 +122,34 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return result; } - pub fn pop(self: &Self) T { + pub fn pop(self: *Self) T { self.len -= 1; return self.items[self.len]; } - pub fn popOrNull(self: &Self) ?T { + pub fn popOrNull(self: *Self) ?T { if (self.len == 0) return null; return self.pop(); } pub const Iterator = struct { - list: &const Self, + list: *const Self, // how many items have we returned count: usize, - pub fn next(it: &Iterator) ?T { + pub fn next(it: *Iterator) ?T { if (it.count >= it.list.len) return null; const val = it.list.at(it.count); it.count += 1; return val; } - pub fn reset(it: &Iterator) void { + pub fn reset(it: *Iterator) void { it.count = 0; } }; - pub fn iterator(self: &const Self) Iterator { + pub fn iterator(self: *const Self) Iterator { return Iterator{ .list = self, .count = 0, diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 35180da8d1..142c958173 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -5,36 +5,36 @@ const AtomicRmwOp = builtin.AtomicRmwOp; /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Queue(comptime T: type) type { return struct { - head: &Node, - tail: &Node, + head: *Node, + tail: *Node, root: Node, pub const Self = this; pub const Node = struct { - next: ?&Node, + next: ?*Node, data: T, }; // TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287 - pub fn init(self: &Self) void { + pub fn init(self: *Self) void { self.root.next = null; self.head = &self.root; self.tail = &self.root; } - pub fn put(self: &Self, node: &Node) void { + pub fn put(self: *Self, node: *Node) void { node.next = null; - const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); - _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); } - pub fn get(self: &Self) ?&Node { - var head = @atomicLoad(&Node, &self.head, AtomicOrder.SeqCst); + pub fn get(self: *Self) ?*Node { + var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); while (true) { const node = head.next ?? return null; - head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; + head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; } } }; @@ -42,8 +42,8 @@ pub fn Queue(comptime T: type) type { const std = @import("std"); const Context = struct { - allocator: &std.mem.Allocator, - queue: &Queue(i32), + allocator: *std.mem.Allocator, + queue: *Queue(i32), put_sum: isize, get_sum: isize, get_count: usize, @@ -79,11 +79,11 @@ test "std.atomic.queue" { .get_count = 0, }; - var putters: [put_thread_count]&std.os.Thread = undefined; + var putters: [put_thread_count]*std.os.Thread = undefined; for (putters) |*t| { t.* = try std.os.spawnThread(&context, startPuts); } - var getters: [put_thread_count]&std.os.Thread = undefined; + var getters: [put_thread_count]*std.os.Thread = undefined; for (getters) |*t| { t.* = try std.os.spawnThread(&context, startGets); } @@ -98,7 +98,7 @@ test "std.atomic.queue" { std.debug.assert(context.get_count == puts_per_thread * put_thread_count); } -fn startPuts(ctx: &Context) u8 { +fn startPuts(ctx: *Context) u8 { var put_count: usize = puts_per_thread; var r = std.rand.DefaultPrng.init(0xdeadbeef); while (put_count != 0) : (put_count -= 1) { @@ -112,7 +112,7 @@ fn startPuts(ctx: &Context) u8 { return 0; } -fn startGets(ctx: &Context) u8 { +fn startGets(ctx: *Context) u8 { while (true) { while (ctx.queue.get()) |node| { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 400a1a3c4f..15611188d2 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -4,12 +4,12 @@ const AtomicOrder = builtin.AtomicOrder; /// Many reader, many writer, non-allocating, thread-safe, lock-free pub fn Stack(comptime T: type) type { return struct { - root: ?&Node, + root: ?*Node, pub const Self = this; pub const Node = struct { - next: ?&Node, + next: ?*Node, data: T, }; @@ -19,36 +19,36 @@ pub fn Stack(comptime T: type) type { /// push operation, but only if you are the first item in the stack. if you did not succeed in /// being the first item in the stack, returns the other item that was there. - pub fn pushFirst(self: &Self, node: &Node) ?&Node { + pub fn pushFirst(self: *Self, node: *Node) ?*Node { node.next = null; - return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); + return @cmpxchgStrong(?*Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); } - pub fn push(self: &Self, node: &Node) void { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); + pub fn push(self: *Self, node: *Node) void { + var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); while (true) { node.next = root; - root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; + root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; } } - pub fn pop(self: &Self) ?&Node { - var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); + pub fn pop(self: *Self) ?*Node { + var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); while (true) { - root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; + root = @cmpxchgWeak(?*Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; } } - pub fn isEmpty(self: &Self) bool { - return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null; + pub fn isEmpty(self: *Self) bool { + return @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst) == null; } }; } const std = @import("std"); const Context = struct { - allocator: &std.mem.Allocator, - stack: &Stack(i32), + allocator: *std.mem.Allocator, + stack: *Stack(i32), put_sum: isize, get_sum: isize, get_count: usize, @@ -82,11 +82,11 @@ test "std.atomic.stack" { .get_count = 0, }; - var putters: [put_thread_count]&std.os.Thread = undefined; + var putters: [put_thread_count]*std.os.Thread = undefined; for (putters) |*t| { t.* = try std.os.spawnThread(&context, startPuts); } - var getters: [put_thread_count]&std.os.Thread = undefined; + var getters: [put_thread_count]*std.os.Thread = undefined; for (getters) |*t| { t.* = try std.os.spawnThread(&context, startGets); } @@ -101,7 +101,7 @@ test "std.atomic.stack" { std.debug.assert(context.get_count == puts_per_thread * put_thread_count); } -fn startPuts(ctx: &Context) u8 { +fn startPuts(ctx: *Context) u8 { var put_count: usize = puts_per_thread; var r = std.rand.DefaultPrng.init(0xdeadbeef); while (put_count != 0) : (put_count -= 1) { @@ -115,7 +115,7 @@ fn startPuts(ctx: &Context) u8 { return 0; } -fn startGets(ctx: &Context) u8 { +fn startGets(ctx: *Context) u8 { while (true) { while (ctx.stack.pop()) |node| { std.os.time.sleep(0, 1); // let the os scheduler be our fuzz diff --git a/std/base64.zig b/std/base64.zig index 204628a405..d27bcbd201 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -32,7 +32,7 @@ pub const Base64Encoder = struct { } /// dest.len must be what you get from ::calcSize. - pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) void { + pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) void { assert(dest.len == Base64Encoder.calcSize(source.len)); var i: usize = 0; @@ -107,7 +107,7 @@ pub const Base64Decoder = struct { } /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding. - pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) !usize { + pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize { if (source.len % 4 != 0) return error.InvalidPadding; return calcDecodedSizeExactUnsafe(source, decoder.pad_char); } @@ -115,7 +115,7 @@ pub const Base64Decoder = struct { /// dest.len must be what you get from ::calcSize. /// invalid characters result in error.InvalidCharacter. /// invalid padding results in error.InvalidPadding. - pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) !void { + pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void { assert(dest.len == (decoder.calcSize(source) catch unreachable)); assert(source.len % 4 == 0); @@ -181,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct { /// Invalid padding results in error.InvalidPadding. /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound. /// Returns the number of bytes writen to dest. - pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize { + pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize { const decoder = &decoder_with_ignore.decoder; var src_cursor: usize = 0; @@ -290,13 +290,13 @@ pub const Base64DecoderUnsafe = struct { } /// The source buffer must be valid. - pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) usize { + pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize { return calcDecodedSizeExactUnsafe(source, decoder.pad_char); } /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe. /// invalid characters or padding will result in undefined values. - pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) void { + pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void { assert(dest.len == decoder.calcSize(source)); var src_index: usize = 0; diff --git a/std/buf_map.zig b/std/buf_map.zig index 930fc36a78..22d821ae7b 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -11,12 +11,12 @@ pub const BufMap = struct { const BufMapHashMap = HashMap([]const u8, []const u8, mem.hash_slice_u8, mem.eql_slice_u8); - pub fn init(allocator: &Allocator) BufMap { + pub fn init(allocator: *Allocator) BufMap { var self = BufMap{ .hash_map = BufMapHashMap.init(allocator) }; return self; } - pub fn deinit(self: &const BufMap) void { + pub fn deinit(self: *const BufMap) void { var it = self.hash_map.iterator(); while (true) { const entry = it.next() ?? break; @@ -27,7 +27,7 @@ pub const BufMap = struct { self.hash_map.deinit(); } - pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void { + pub fn set(self: *BufMap, key: []const u8, value: []const u8) !void { self.delete(key); const key_copy = try self.copy(key); errdefer self.free(key_copy); @@ -36,30 +36,30 @@ pub const BufMap = struct { _ = try self.hash_map.put(key_copy, value_copy); } - pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 { + pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 { const entry = self.hash_map.get(key) ?? return null; return entry.value; } - pub fn delete(self: &BufMap, key: []const u8) void { + pub fn delete(self: *BufMap, key: []const u8) void { const entry = self.hash_map.remove(key) ?? return; self.free(entry.key); self.free(entry.value); } - pub fn count(self: &const BufMap) usize { + pub fn count(self: *const BufMap) usize { return self.hash_map.count(); } - pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator { + pub fn iterator(self: *const BufMap) BufMapHashMap.Iterator { return self.hash_map.iterator(); } - fn free(self: &const BufMap, value: []const u8) void { + fn free(self: *const BufMap, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: &const BufMap, value: []const u8) ![]const u8 { + fn copy(self: *const BufMap, value: []const u8) ![]const u8 { return mem.dupe(self.hash_map.allocator, u8, value); } }; diff --git a/std/buf_set.zig b/std/buf_set.zig index c5a80e16fb..03a050ed8b 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -9,12 +9,12 @@ pub const BufSet = struct { const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); - pub fn init(a: &Allocator) BufSet { + pub fn init(a: *Allocator) BufSet { var self = BufSet{ .hash_map = BufSetHashMap.init(a) }; return self; } - pub fn deinit(self: &const BufSet) void { + pub fn deinit(self: *const BufSet) void { var it = self.hash_map.iterator(); while (true) { const entry = it.next() ?? break; @@ -24,7 +24,7 @@ pub const BufSet = struct { self.hash_map.deinit(); } - pub fn put(self: &BufSet, key: []const u8) !void { + pub fn put(self: *BufSet, key: []const u8) !void { if (self.hash_map.get(key) == null) { const key_copy = try self.copy(key); errdefer self.free(key_copy); @@ -32,28 +32,28 @@ pub const BufSet = struct { } } - pub fn delete(self: &BufSet, key: []const u8) void { + pub fn delete(self: *BufSet, key: []const u8) void { const entry = self.hash_map.remove(key) ?? return; self.free(entry.key); } - pub fn count(self: &const BufSet) usize { + pub fn count(self: *const BufSet) usize { return self.hash_map.count(); } - pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator { + pub fn iterator(self: *const BufSet) BufSetHashMap.Iterator { return self.hash_map.iterator(); } - pub fn allocator(self: &const BufSet) &Allocator { + pub fn allocator(self: *const BufSet) *Allocator { return self.hash_map.allocator; } - fn free(self: &const BufSet, value: []const u8) void { + fn free(self: *const BufSet, value: []const u8) void { self.hash_map.allocator.free(value); } - fn copy(self: &const BufSet, value: []const u8) ![]const u8 { + fn copy(self: *const BufSet, value: []const u8) ![]const u8 { const result = try self.hash_map.allocator.alloc(u8, value.len); mem.copy(u8, result, value); return result; diff --git a/std/buffer.zig b/std/buffer.zig index 90d63719e3..305746e183 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -12,14 +12,14 @@ pub const Buffer = struct { list: ArrayList(u8), /// Must deinitialize with deinit. - pub fn init(allocator: &Allocator, m: []const u8) !Buffer { + pub fn init(allocator: *Allocator, m: []const u8) !Buffer { var self = try initSize(allocator, m.len); mem.copy(u8, self.list.items, m); return self; } /// Must deinitialize with deinit. - pub fn initSize(allocator: &Allocator, size: usize) !Buffer { + pub fn initSize(allocator: *Allocator, size: usize) !Buffer { var self = initNull(allocator); try self.resize(size); return self; @@ -30,19 +30,19 @@ pub const Buffer = struct { /// * ::replaceContents /// * ::replaceContentsBuffer /// * ::resize - pub fn initNull(allocator: &Allocator) Buffer { + pub fn initNull(allocator: *Allocator) Buffer { return Buffer{ .list = ArrayList(u8).init(allocator) }; } /// Must deinitialize with deinit. - pub fn initFromBuffer(buffer: &const Buffer) !Buffer { + pub fn initFromBuffer(buffer: *const Buffer) !Buffer { return Buffer.init(buffer.list.allocator, buffer.toSliceConst()); } /// Buffer takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Must deinitialize with deinit. - pub fn fromOwnedSlice(allocator: &Allocator, slice: []u8) Buffer { + pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) Buffer { var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) }; self.list.append(0); return self; @@ -50,79 +50,79 @@ pub const Buffer = struct { /// The caller owns the returned memory. The Buffer becomes null and /// is safe to `deinit`. - pub fn toOwnedSlice(self: &Buffer) []u8 { + pub fn toOwnedSlice(self: *Buffer) []u8 { const allocator = self.list.allocator; const result = allocator.shrink(u8, self.list.items, self.len()); self.* = initNull(allocator); return result; } - pub fn deinit(self: &Buffer) void { + pub fn deinit(self: *Buffer) void { self.list.deinit(); } - pub fn toSlice(self: &const Buffer) []u8 { + pub fn toSlice(self: *const Buffer) []u8 { return self.list.toSlice()[0..self.len()]; } - pub fn toSliceConst(self: &const Buffer) []const u8 { + pub fn toSliceConst(self: *const Buffer) []const u8 { return self.list.toSliceConst()[0..self.len()]; } - pub fn shrink(self: &Buffer, new_len: usize) void { + pub fn shrink(self: *Buffer, new_len: usize) void { assert(new_len <= self.len()); self.list.shrink(new_len + 1); self.list.items[self.len()] = 0; } - pub fn resize(self: &Buffer, new_len: usize) !void { + pub fn resize(self: *Buffer, new_len: usize) !void { try self.list.resize(new_len + 1); self.list.items[self.len()] = 0; } - pub fn isNull(self: &const Buffer) bool { + pub fn isNull(self: *const Buffer) bool { return self.list.len == 0; } - pub fn len(self: &const Buffer) usize { + pub fn len(self: *const Buffer) usize { return self.list.len - 1; } - pub fn append(self: &Buffer, m: []const u8) !void { + pub fn append(self: *Buffer, m: []const u8) !void { const old_len = self.len(); try self.resize(old_len + m.len); mem.copy(u8, self.list.toSlice()[old_len..], m); } - pub fn appendByte(self: &Buffer, byte: u8) !void { + pub fn appendByte(self: *Buffer, byte: u8) !void { const old_len = self.len(); try self.resize(old_len + 1); self.list.toSlice()[old_len] = byte; } - pub fn eql(self: &const Buffer, m: []const u8) bool { + pub fn eql(self: *const Buffer, m: []const u8) bool { return mem.eql(u8, self.toSliceConst(), m); } - pub fn startsWith(self: &const Buffer, m: []const u8) bool { + pub fn startsWith(self: *const Buffer, m: []const u8) bool { if (self.len() < m.len) return false; return mem.eql(u8, self.list.items[0..m.len], m); } - pub fn endsWith(self: &const Buffer, m: []const u8) bool { + pub fn endsWith(self: *const Buffer, m: []const u8) bool { const l = self.len(); if (l < m.len) return false; const start = l - m.len; return mem.eql(u8, self.list.items[start..l], m); } - pub fn replaceContents(self: &const Buffer, m: []const u8) !void { + pub fn replaceContents(self: *const Buffer, m: []const u8) !void { try self.resize(m.len); mem.copy(u8, self.list.toSlice(), m); } /// For passing to C functions. - pub fn ptr(self: &const Buffer) &u8 { + pub fn ptr(self: *const Buffer) *u8 { return self.list.items.ptr; } }; diff --git a/std/build.zig b/std/build.zig index 9a6e17f728..fed02e0815 100644 --- a/std/build.zig +++ b/std/build.zig @@ -20,7 +20,7 @@ pub const Builder = struct { install_tls: TopLevelStep, have_uninstall_step: bool, have_install_step: bool, - allocator: &Allocator, + allocator: *Allocator, lib_paths: ArrayList([]const u8), include_paths: ArrayList([]const u8), rpaths: ArrayList([]const u8), @@ -36,9 +36,9 @@ pub const Builder = struct { verbose_cimport: bool, invalid_user_input: bool, zig_exe: []const u8, - default_step: &Step, + default_step: *Step, env_map: BufMap, - top_level_steps: ArrayList(&TopLevelStep), + top_level_steps: ArrayList(*TopLevelStep), prefix: []const u8, search_prefixes: ArrayList([]const u8), lib_dir: []const u8, @@ -82,7 +82,7 @@ pub const Builder = struct { description: []const u8, }; - pub fn init(allocator: &Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { + pub fn init(allocator: *Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { var self = Builder{ .zig_exe = zig_exe, .build_root = build_root, @@ -102,7 +102,7 @@ pub const Builder = struct { .user_input_options = UserInputOptionsMap.init(allocator), .available_options_map = AvailableOptionsMap.init(allocator), .available_options_list = ArrayList(AvailableOption).init(allocator), - .top_level_steps = ArrayList(&TopLevelStep).init(allocator), + .top_level_steps = ArrayList(*TopLevelStep).init(allocator), .default_step = undefined, .env_map = os.getEnvMap(allocator) catch unreachable, .prefix = undefined, @@ -127,7 +127,7 @@ pub const Builder = struct { return self; } - pub fn deinit(self: &Builder) void { + pub fn deinit(self: *Builder) void { self.lib_paths.deinit(); self.include_paths.deinit(); self.rpaths.deinit(); @@ -135,81 +135,81 @@ pub const Builder = struct { self.top_level_steps.deinit(); } - pub fn setInstallPrefix(self: &Builder, maybe_prefix: ?[]const u8) void { + pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable; self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable; } - pub fn addExecutable(self: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { return LibExeObjStep.createExecutable(self, name, root_src); } - pub fn addObject(self: &Builder, name: []const u8, root_src: []const u8) &LibExeObjStep { + pub fn addObject(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { return LibExeObjStep.createObject(self, name, root_src); } - pub fn addSharedLibrary(self: &Builder, name: []const u8, root_src: ?[]const u8, ver: &const Version) &LibExeObjStep { + pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: *const Version) *LibExeObjStep { return LibExeObjStep.createSharedLibrary(self, name, root_src, ver); } - pub fn addStaticLibrary(self: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { return LibExeObjStep.createStaticLibrary(self, name, root_src); } - pub fn addTest(self: &Builder, root_src: []const u8) &TestStep { + pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { const test_step = self.allocator.create(TestStep) catch unreachable; test_step.* = TestStep.init(self, root_src); return test_step; } - pub fn addAssemble(self: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { + pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { const obj_step = LibExeObjStep.createObject(self, name, null); obj_step.addAssemblyFile(src); return obj_step; } - pub fn addCStaticLibrary(self: &Builder, name: []const u8) &LibExeObjStep { + pub fn addCStaticLibrary(self: *Builder, name: []const u8) *LibExeObjStep { return LibExeObjStep.createCStaticLibrary(self, name); } - pub fn addCSharedLibrary(self: &Builder, name: []const u8, ver: &const Version) &LibExeObjStep { + pub fn addCSharedLibrary(self: *Builder, name: []const u8, ver: *const Version) *LibExeObjStep { return LibExeObjStep.createCSharedLibrary(self, name, ver); } - pub fn addCExecutable(self: &Builder, name: []const u8) &LibExeObjStep { + pub fn addCExecutable(self: *Builder, name: []const u8) *LibExeObjStep { return LibExeObjStep.createCExecutable(self, name); } - pub fn addCObject(self: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { + pub fn addCObject(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { return LibExeObjStep.createCObject(self, name, src); } /// ::argv is copied. - pub fn addCommand(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) &CommandStep { + pub fn addCommand(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { return CommandStep.create(self, cwd, env_map, argv); } - pub fn addWriteFile(self: &Builder, file_path: []const u8, data: []const u8) &WriteFileStep { + pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; write_file_step.* = WriteFileStep.init(self, file_path, data); return write_file_step; } - pub fn addLog(self: &Builder, comptime format: []const u8, args: ...) &LogStep { + pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { const data = self.fmt(format, args); const log_step = self.allocator.create(LogStep) catch unreachable; log_step.* = LogStep.init(self, data); return log_step; } - pub fn addRemoveDirTree(self: &Builder, dir_path: []const u8) &RemoveDirStep { + pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } - pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) Version { + pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { return Version{ .major = major, .minor = minor, @@ -217,20 +217,20 @@ pub const Builder = struct { }; } - pub fn addCIncludePath(self: &Builder, path: []const u8) void { + pub fn addCIncludePath(self: *Builder, path: []const u8) void { self.include_paths.append(path) catch unreachable; } - pub fn addRPath(self: &Builder, path: []const u8) void { + pub fn addRPath(self: *Builder, path: []const u8) void { self.rpaths.append(path) catch unreachable; } - pub fn addLibPath(self: &Builder, path: []const u8) void { + pub fn addLibPath(self: *Builder, path: []const u8) void { self.lib_paths.append(path) catch unreachable; } - pub fn make(self: &Builder, step_names: []const []const u8) !void { - var wanted_steps = ArrayList(&Step).init(self.allocator); + pub fn make(self: *Builder, step_names: []const []const u8) !void { + var wanted_steps = ArrayList(*Step).init(self.allocator); defer wanted_steps.deinit(); if (step_names.len == 0) { @@ -247,7 +247,7 @@ pub const Builder = struct { } } - pub fn getInstallStep(self: &Builder) &Step { + pub fn getInstallStep(self: *Builder) *Step { if (self.have_install_step) return &self.install_tls.step; self.top_level_steps.append(&self.install_tls) catch unreachable; @@ -255,7 +255,7 @@ pub const Builder = struct { return &self.install_tls.step; } - pub fn getUninstallStep(self: &Builder) &Step { + pub fn getUninstallStep(self: *Builder) *Step { if (self.have_uninstall_step) return &self.uninstall_tls.step; self.top_level_steps.append(&self.uninstall_tls) catch unreachable; @@ -263,7 +263,7 @@ pub const Builder = struct { return &self.uninstall_tls.step; } - fn makeUninstall(uninstall_step: &Step) error!void { + fn makeUninstall(uninstall_step: *Step) error!void { const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); @@ -277,7 +277,7 @@ pub const Builder = struct { // TODO remove empty directories } - fn makeOneStep(self: &Builder, s: &Step) error!void { + fn makeOneStep(self: *Builder, s: *Step) error!void { if (s.loop_flag) { warn("Dependency loop detected:\n {}\n", s.name); return error.DependencyLoopDetected; @@ -298,7 +298,7 @@ pub const Builder = struct { try s.make(); } - fn getTopLevelStepByName(self: &Builder, name: []const u8) !&Step { + fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { for (self.top_level_steps.toSliceConst()) |top_level_step| { if (mem.eql(u8, top_level_step.step.name, name)) { return &top_level_step.step; @@ -308,7 +308,7 @@ pub const Builder = struct { return error.InvalidStepName; } - fn processNixOSEnvVars(self: &Builder) void { + fn processNixOSEnvVars(self: *Builder) void { if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { var it = mem.split(nix_cflags_compile, " "); while (true) { @@ -350,7 +350,7 @@ pub const Builder = struct { } } - pub fn option(self: &Builder, comptime T: type, name: []const u8, description: []const u8) ?T { + pub fn option(self: *Builder, comptime T: type, name: []const u8, description: []const u8) ?T { const type_id = comptime typeToEnum(T); const available_option = AvailableOption{ .name = name, @@ -403,7 +403,7 @@ pub const Builder = struct { } } - pub fn step(self: &Builder, name: []const u8, description: []const u8) &Step { + pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { const step_info = self.allocator.create(TopLevelStep) catch unreachable; step_info.* = TopLevelStep{ .step = Step.initNoOp(name, self.allocator), @@ -413,7 +413,7 @@ pub const Builder = struct { return &step_info.step; } - pub fn standardReleaseOptions(self: &Builder) builtin.Mode { + pub fn standardReleaseOptions(self: *Builder) builtin.Mode { if (self.release_mode) |mode| return mode; const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; @@ -429,7 +429,7 @@ pub const Builder = struct { return mode; } - pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) bool { + pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool { if (self.user_input_options.put(name, UserInputOption{ .name = name, .value = UserValue{ .Scalar = value }, @@ -466,7 +466,7 @@ pub const Builder = struct { return false; } - pub fn addUserInputFlag(self: &Builder, name: []const u8) bool { + pub fn addUserInputFlag(self: *Builder, name: []const u8) bool { if (self.user_input_options.put(name, UserInputOption{ .name = name, .value = UserValue{ .Flag = {} }, @@ -500,7 +500,7 @@ pub const Builder = struct { }; } - fn markInvalidUserInput(self: &Builder) void { + fn markInvalidUserInput(self: *Builder) void { self.invalid_user_input = true; } @@ -514,7 +514,7 @@ pub const Builder = struct { }; } - pub fn validateUserInputDidItFail(self: &Builder) bool { + pub fn validateUserInputDidItFail(self: *Builder) bool { // make sure all args are used var it = self.user_input_options.iterator(); while (true) { @@ -528,7 +528,7 @@ pub const Builder = struct { return self.invalid_user_input; } - fn spawnChild(self: &Builder, argv: []const []const u8) !void { + fn spawnChild(self: *Builder, argv: []const []const u8) !void { return self.spawnChildEnvMap(null, &self.env_map, argv); } @@ -540,7 +540,7 @@ pub const Builder = struct { warn("\n"); } - fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) !void { + fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void { if (self.verbose) { printCmd(cwd, argv); } @@ -573,28 +573,28 @@ pub const Builder = struct { } } - pub fn makePath(self: &Builder, path: []const u8) !void { + pub fn makePath(self: *Builder, path: []const u8) !void { os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { warn("Unable to create path {}: {}\n", path, @errorName(err)); return err; }; } - pub fn installArtifact(self: &Builder, artifact: &LibExeObjStep) void { + pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); } - pub fn addInstallArtifact(self: &Builder, artifact: &LibExeObjStep) &InstallArtifactStep { + pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { return InstallArtifactStep.create(self, artifact); } ///::dest_rel_path is relative to prefix path or it can be an absolute path - pub fn installFile(self: &Builder, src_path: []const u8, dest_rel_path: []const u8) void { + pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { self.getInstallStep().dependOn(&self.addInstallFile(src_path, dest_rel_path).step); } ///::dest_rel_path is relative to prefix path or it can be an absolute path - pub fn addInstallFile(self: &Builder, src_path: []const u8, dest_rel_path: []const u8) &InstallFileStep { + pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; self.pushInstalledFile(full_dest_path); @@ -603,16 +603,16 @@ pub const Builder = struct { return install_step; } - pub fn pushInstalledFile(self: &Builder, full_path: []const u8) void { + pub fn pushInstalledFile(self: *Builder, full_path: []const u8) void { _ = self.getUninstallStep(); self.installed_files.append(full_path) catch unreachable; } - fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void { + fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { return self.copyFileMode(source_path, dest_path, os.default_file_mode); } - fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void { + fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void { if (self.verbose) { warn("cp {} {}\n", source_path, dest_path); } @@ -629,15 +629,15 @@ pub const Builder = struct { }; } - fn pathFromRoot(self: &Builder, rel_path: []const u8) []u8 { + fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable; } - pub fn fmt(self: &Builder, comptime format: []const u8, args: ...) []u8 { + pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; } - fn getCCExe(self: &Builder) []const u8 { + fn getCCExe(self: *Builder) []const u8 { if (builtin.environ == builtin.Environ.msvc) { return "cl.exe"; } else { @@ -645,7 +645,7 @@ pub const Builder = struct { } } - pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { + pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations const exe_extension = (Target{ .Native = {} }).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { @@ -693,7 +693,7 @@ pub const Builder = struct { return error.FileNotFound; } - pub fn exec(self: &Builder, argv: []const []const u8) ![]u8 { + pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { const max_output_size = 100 * 1024; const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); switch (result.term) { @@ -715,7 +715,7 @@ pub const Builder = struct { } } - pub fn addSearchPrefix(self: &Builder, search_prefix: []const u8) void { + pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { self.search_prefixes.append(search_prefix) catch unreachable; } }; @@ -736,7 +736,7 @@ pub const Target = union(enum) { Native: void, Cross: CrossTarget, - pub fn oFileExt(self: &const Target) []const u8 { + pub fn oFileExt(self: *const Target) []const u8 { const environ = switch (self.*) { Target.Native => builtin.environ, Target.Cross => |t| t.environ, @@ -747,49 +747,49 @@ pub const Target = union(enum) { }; } - pub fn exeFileExt(self: &const Target) []const u8 { + pub fn exeFileExt(self: *const Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn libFileExt(self: &const Target) []const u8 { + pub fn libFileExt(self: *const Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".lib", else => ".a", }; } - pub fn getOs(self: &const Target) builtin.Os { + pub fn getOs(self: *const Target) builtin.Os { return switch (self.*) { Target.Native => builtin.os, Target.Cross => |t| t.os, }; } - pub fn isDarwin(self: &const Target) bool { + pub fn isDarwin(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: &const Target) bool { + pub fn isWindows(self: *const Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, }; } - pub fn wantSharedLibSymLinks(self: &const Target) bool { + pub fn wantSharedLibSymLinks(self: *const Target) bool { return !self.isWindows(); } }; pub const LibExeObjStep = struct { step: Step, - builder: &Builder, + builder: *Builder, name: []const u8, target: Target, link_libs: BufSet, @@ -836,56 +836,56 @@ pub const LibExeObjStep = struct { Obj, }; - pub fn createSharedLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8, ver: &const Version) &LibExeObjStep { + pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: *const Version) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); return self; } - pub fn createCSharedLibrary(builder: &Builder, name: []const u8, version: &const Version) &LibExeObjStep { + pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: *const Version) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Lib, version, false); return self; } - pub fn createStaticLibrary(builder: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } - pub fn createCStaticLibrary(builder: &Builder, name: []const u8) &LibExeObjStep { + pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); return self; } - pub fn createObject(builder: &Builder, name: []const u8, root_src: []const u8) &LibExeObjStep { + pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } - pub fn createCObject(builder: &Builder, name: []const u8, src: []const u8) &LibExeObjStep { + pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); self.object_src = src; return self; } - pub fn createExecutable(builder: &Builder, name: []const u8, root_src: ?[]const u8) &LibExeObjStep { + pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0)); return self; } - pub fn createCExecutable(builder: &Builder, name: []const u8) &LibExeObjStep { + pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); return self; } - fn initExtraArgs(builder: &Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: &const Version) LibExeObjStep { + fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { var self = LibExeObjStep{ .strip = false, .builder = builder, @@ -924,7 +924,7 @@ pub const LibExeObjStep = struct { return self; } - fn initC(builder: &Builder, name: []const u8, kind: Kind, version: &const Version, static: bool) LibExeObjStep { + fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { var self = LibExeObjStep{ .builder = builder, .name = name, @@ -964,7 +964,7 @@ pub const LibExeObjStep = struct { return self; } - fn computeOutFileNames(self: &LibExeObjStep) void { + fn computeOutFileNames(self: *LibExeObjStep) void { switch (self.kind) { Kind.Obj => { self.out_filename = self.builder.fmt("{}{}", self.name, self.target.oFileExt()); @@ -996,7 +996,7 @@ pub const LibExeObjStep = struct { } } - pub fn setTarget(self: &LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + pub fn setTarget(self: *LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { self.target = Target{ .Cross = CrossTarget{ .arch = target_arch, @@ -1008,16 +1008,16 @@ pub const LibExeObjStep = struct { } // TODO respect this in the C args - pub fn setLinkerScriptPath(self: &LibExeObjStep, path: []const u8) void { + pub fn setLinkerScriptPath(self: *LibExeObjStep, path: []const u8) void { self.linker_script = path; } - pub fn linkFramework(self: &LibExeObjStep, framework_name: []const u8) void { + pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { assert(self.target.isDarwin()); self.frameworks.put(framework_name) catch unreachable; } - pub fn linkLibrary(self: &LibExeObjStep, lib: &LibExeObjStep) void { + pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { assert(self.kind != Kind.Obj); assert(lib.kind == Kind.Lib); @@ -1038,26 +1038,26 @@ pub const LibExeObjStep = struct { } } - pub fn linkSystemLibrary(self: &LibExeObjStep, name: []const u8) void { + pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { assert(self.kind != Kind.Obj); self.link_libs.put(name) catch unreachable; } - pub fn addSourceFile(self: &LibExeObjStep, file: []const u8) void { + pub fn addSourceFile(self: *LibExeObjStep, file: []const u8) void { assert(self.kind != Kind.Obj); assert(!self.is_zig); self.source_files.append(file) catch unreachable; } - pub fn setVerboseLink(self: &LibExeObjStep, value: bool) void { + pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { self.verbose_link = value; } - pub fn setBuildMode(self: &LibExeObjStep, mode: builtin.Mode) void { + pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void { self.build_mode = mode; } - pub fn setOutputPath(self: &LibExeObjStep, file_path: []const u8) void { + pub fn setOutputPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_path = file_path; // catch a common mistake @@ -1066,11 +1066,11 @@ pub const LibExeObjStep = struct { } } - pub fn getOutputPath(self: &LibExeObjStep) []const u8 { + pub fn getOutputPath(self: *LibExeObjStep) []const u8 { return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable; } - pub fn setOutputHPath(self: &LibExeObjStep, file_path: []const u8) void { + pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_h_path = file_path; // catch a common mistake @@ -1079,21 +1079,21 @@ pub const LibExeObjStep = struct { } } - pub fn getOutputHPath(self: &LibExeObjStep) []const u8 { + pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable; } - pub fn addAssemblyFile(self: &LibExeObjStep, path: []const u8) void { + pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { self.assembly_files.append(path) catch unreachable; } - pub fn addObjectFile(self: &LibExeObjStep, path: []const u8) void { + pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void { assert(self.kind != Kind.Obj); self.object_files.append(path) catch unreachable; } - pub fn addObject(self: &LibExeObjStep, obj: &LibExeObjStep) void { + pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { assert(obj.kind == Kind.Obj); assert(self.kind != Kind.Obj); @@ -1110,15 +1110,15 @@ pub const LibExeObjStep = struct { self.include_dirs.append(self.builder.cache_root) catch unreachable; } - pub fn addIncludeDir(self: &LibExeObjStep, path: []const u8) void { + pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { self.include_dirs.append(path) catch unreachable; } - pub fn addLibPath(self: &LibExeObjStep, path: []const u8) void { + pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void { self.lib_paths.append(path) catch unreachable; } - pub fn addPackagePath(self: &LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { + pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { assert(self.is_zig); self.packages.append(Pkg{ @@ -1127,23 +1127,23 @@ pub const LibExeObjStep = struct { }) catch unreachable; } - pub fn addCompileFlags(self: &LibExeObjStep, flags: []const []const u8) void { + pub fn addCompileFlags(self: *LibExeObjStep, flags: []const []const u8) void { for (flags) |flag| { self.cflags.append(flag) catch unreachable; } } - pub fn setNoStdLib(self: &LibExeObjStep, disable: bool) void { + pub fn setNoStdLib(self: *LibExeObjStep, disable: bool) void { assert(!self.is_zig); self.disable_libc = disable; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(LibExeObjStep, "step", step); return if (self.is_zig) self.makeZig() else self.makeC(); } - fn makeZig(self: &LibExeObjStep) !void { + fn makeZig(self: *LibExeObjStep) !void { const builder = self.builder; assert(self.is_zig); @@ -1309,7 +1309,7 @@ pub const LibExeObjStep = struct { } } - fn appendCompileFlags(self: &LibExeObjStep, args: &ArrayList([]const u8)) void { + fn appendCompileFlags(self: *LibExeObjStep, args: *ArrayList([]const u8)) void { if (!self.strip) { args.append("-g") catch unreachable; } @@ -1354,7 +1354,7 @@ pub const LibExeObjStep = struct { } } - fn makeC(self: &LibExeObjStep) !void { + fn makeC(self: *LibExeObjStep) !void { const builder = self.builder; const cc = builder.getCCExe(); @@ -1580,7 +1580,7 @@ pub const LibExeObjStep = struct { pub const TestStep = struct { step: Step, - builder: &Builder, + builder: *Builder, root_src: []const u8, build_mode: builtin.Mode, verbose: bool, @@ -1591,7 +1591,7 @@ pub const TestStep = struct { exec_cmd_args: ?[]const ?[]const u8, include_dirs: ArrayList([]const u8), - pub fn init(builder: &Builder, root_src: []const u8) TestStep { + pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); return TestStep{ .step = Step.init(step_name, builder.allocator, make), @@ -1608,31 +1608,31 @@ pub const TestStep = struct { }; } - pub fn setVerbose(self: &TestStep, value: bool) void { + pub fn setVerbose(self: *TestStep, value: bool) void { self.verbose = value; } - pub fn addIncludeDir(self: &TestStep, path: []const u8) void { + pub fn addIncludeDir(self: *TestStep, path: []const u8) void { self.include_dirs.append(path) catch unreachable; } - pub fn setBuildMode(self: &TestStep, mode: builtin.Mode) void { + pub fn setBuildMode(self: *TestStep, mode: builtin.Mode) void { self.build_mode = mode; } - pub fn linkSystemLibrary(self: &TestStep, name: []const u8) void { + pub fn linkSystemLibrary(self: *TestStep, name: []const u8) void { self.link_libs.put(name) catch unreachable; } - pub fn setNamePrefix(self: &TestStep, text: []const u8) void { + pub fn setNamePrefix(self: *TestStep, text: []const u8) void { self.name_prefix = text; } - pub fn setFilter(self: &TestStep, text: ?[]const u8) void { + pub fn setFilter(self: *TestStep, text: ?[]const u8) void { self.filter = text; } - pub fn setTarget(self: &TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { self.target = Target{ .Cross = CrossTarget{ .arch = target_arch, @@ -1642,11 +1642,11 @@ pub const TestStep = struct { }; } - pub fn setExecCmd(self: &TestStep, args: []const ?[]const u8) void { + pub fn setExecCmd(self: *TestStep, args: []const ?[]const u8) void { self.exec_cmd_args = args; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(TestStep, "step", step); const builder = self.builder; @@ -1739,13 +1739,13 @@ pub const TestStep = struct { pub const CommandStep = struct { step: Step, - builder: &Builder, + builder: *Builder, argv: [][]const u8, cwd: ?[]const u8, - env_map: &const BufMap, + env_map: *const BufMap, /// ::argv is copied. - pub fn create(builder: &Builder, cwd: ?[]const u8, env_map: &const BufMap, argv: []const []const u8) &CommandStep { + pub fn create(builder: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { const self = builder.allocator.create(CommandStep) catch unreachable; self.* = CommandStep{ .builder = builder, @@ -1759,7 +1759,7 @@ pub const CommandStep = struct { return self; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(CommandStep, "step", step); const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; @@ -1769,13 +1769,13 @@ pub const CommandStep = struct { const InstallArtifactStep = struct { step: Step, - builder: &Builder, - artifact: &LibExeObjStep, + builder: *Builder, + artifact: *LibExeObjStep, dest_file: []const u8, const Self = this; - pub fn create(builder: &Builder, artifact: &LibExeObjStep) &Self { + pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { const self = builder.allocator.create(Self) catch unreachable; const dest_dir = switch (artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, @@ -1797,7 +1797,7 @@ const InstallArtifactStep = struct { return self; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(Self, "step", step); const builder = self.builder; @@ -1818,11 +1818,11 @@ const InstallArtifactStep = struct { pub const InstallFileStep = struct { step: Step, - builder: &Builder, + builder: *Builder, src_path: []const u8, dest_path: []const u8, - pub fn init(builder: &Builder, src_path: []const u8, dest_path: []const u8) InstallFileStep { + pub fn init(builder: *Builder, src_path: []const u8, dest_path: []const u8) InstallFileStep { return InstallFileStep{ .builder = builder, .step = Step.init(builder.fmt("install {}", src_path), builder.allocator, make), @@ -1831,7 +1831,7 @@ pub const InstallFileStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(InstallFileStep, "step", step); try self.builder.copyFile(self.src_path, self.dest_path); } @@ -1839,11 +1839,11 @@ pub const InstallFileStep = struct { pub const WriteFileStep = struct { step: Step, - builder: &Builder, + builder: *Builder, file_path: []const u8, data: []const u8, - pub fn init(builder: &Builder, file_path: []const u8, data: []const u8) WriteFileStep { + pub fn init(builder: *Builder, file_path: []const u8, data: []const u8) WriteFileStep { return WriteFileStep{ .builder = builder, .step = Step.init(builder.fmt("writefile {}", file_path), builder.allocator, make), @@ -1852,7 +1852,7 @@ pub const WriteFileStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(WriteFileStep, "step", step); const full_path = self.builder.pathFromRoot(self.file_path); const full_path_dir = os.path.dirname(full_path); @@ -1869,10 +1869,10 @@ pub const WriteFileStep = struct { pub const LogStep = struct { step: Step, - builder: &Builder, + builder: *Builder, data: []const u8, - pub fn init(builder: &Builder, data: []const u8) LogStep { + pub fn init(builder: *Builder, data: []const u8) LogStep { return LogStep{ .builder = builder, .step = Step.init(builder.fmt("log {}", data), builder.allocator, make), @@ -1880,7 +1880,7 @@ pub const LogStep = struct { }; } - fn make(step: &Step) error!void { + fn make(step: *Step) error!void { const self = @fieldParentPtr(LogStep, "step", step); warn("{}", self.data); } @@ -1888,10 +1888,10 @@ pub const LogStep = struct { pub const RemoveDirStep = struct { step: Step, - builder: &Builder, + builder: *Builder, dir_path: []const u8, - pub fn init(builder: &Builder, dir_path: []const u8) RemoveDirStep { + pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, .step = Step.init(builder.fmt("RemoveDir {}", dir_path), builder.allocator, make), @@ -1899,7 +1899,7 @@ pub const RemoveDirStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: *Step) !void { const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); @@ -1912,39 +1912,39 @@ pub const RemoveDirStep = struct { pub const Step = struct { name: []const u8, - makeFn: fn (self: &Step) error!void, - dependencies: ArrayList(&Step), + makeFn: fn (self: *Step) error!void, + dependencies: ArrayList(*Step), loop_flag: bool, done_flag: bool, - pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step) error!void) Step { + pub fn init(name: []const u8, allocator: *Allocator, makeFn: fn (*Step) error!void) Step { return Step{ .name = name, .makeFn = makeFn, - .dependencies = ArrayList(&Step).init(allocator), + .dependencies = ArrayList(*Step).init(allocator), .loop_flag = false, .done_flag = false, }; } - pub fn initNoOp(name: []const u8, allocator: &Allocator) Step { + pub fn initNoOp(name: []const u8, allocator: *Allocator) Step { return init(name, allocator, makeNoOp); } - pub fn make(self: &Step) !void { + pub fn make(self: *Step) !void { if (self.done_flag) return; try self.makeFn(self); self.done_flag = true; } - pub fn dependOn(self: &Step, other: &Step) void { + pub fn dependOn(self: *Step, other: *Step) void { self.dependencies.append(other) catch unreachable; } - fn makeNoOp(self: &Step) error!void {} + fn makeNoOp(self: *Step) error!void {} }; -fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { +fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { const out_dir = os.path.dirname(output_path); const out_basename = os.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 6a33c994bf..69395e6b27 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,10 +1,10 @@ -extern "c" fn __error() &c_int; -pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int; +extern "c" fn __error() *c_int; +pub extern "c" fn _NSGetExecutablePath(buf: *u8, bufsize: *u32) c_int; -pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; +pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: *u8, buf_len: usize, basep: *i64) usize; pub extern "c" fn mach_absolute_time() u64; -pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void; +pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; pub use @import("../os/darwin_errno.zig"); diff --git a/std/c/index.zig b/std/c/index.zig index f9704f4738..114b79cdae 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -13,49 +13,49 @@ pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: c_int) c_int; pub extern "c" fn close(fd: c_int) c_int; -pub extern "c" fn fstat(fd: c_int, buf: &Stat) c_int; -pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: &Stat) c_int; +pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; +pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; -pub extern "c" fn open(path: &const u8, oflag: c_int, ...) c_int; +pub extern "c" fn open(path: *const u8, oflag: c_int, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize; -pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?&c_void; -pub extern "c" fn munmap(addr: &c_void, len: usize) c_int; -pub extern "c" fn unlink(path: &const u8) c_int; -pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8; -pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int; +pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; +pub extern "c" fn stat(noalias path: *const u8, noalias buf: *Stat) c_int; +pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; +pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; +pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; +pub extern "c" fn unlink(path: *const u8) c_int; +pub extern "c" fn getcwd(buf: *u8, size: usize) ?*u8; +pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; pub extern "c" fn fork() c_int; -pub extern "c" fn access(path: &const u8, mode: c_uint) c_int; -pub extern "c" fn pipe(fds: &c_int) c_int; -pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int; -pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int; -pub extern "c" fn rename(old: &const u8, new: &const u8) c_int; -pub extern "c" fn chdir(path: &const u8) c_int; -pub extern "c" fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) c_int; +pub extern "c" fn access(path: *const u8, mode: c_uint) c_int; +pub extern "c" fn pipe(fds: *c_int) c_int; +pub extern "c" fn mkdir(path: *const u8, mode: c_uint) c_int; +pub extern "c" fn symlink(existing: *const u8, new: *const u8) c_int; +pub extern "c" fn rename(old: *const u8, new: *const u8) c_int; +pub extern "c" fn chdir(path: *const u8) c_int; +pub extern "c" fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) c_int; pub extern "c" fn dup(fd: c_int) c_int; pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; -pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; -pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; -pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; -pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int; -pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; -pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int; +pub extern "c" fn readlink(noalias path: *const u8, noalias buf: *u8, bufsize: usize) isize; +pub extern "c" fn realpath(noalias file_name: *const u8, noalias resolved_name: *u8) ?*u8; +pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; +pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int; +pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; +pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; -pub extern "c" fn rmdir(path: &const u8) c_int; +pub extern "c" fn rmdir(path: *const u8) c_int; -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void; -pub extern "c" fn malloc(usize) ?&c_void; -pub extern "c" fn realloc(&c_void, usize) ?&c_void; -pub extern "c" fn free(&c_void) void; -pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; +pub extern "c" fn malloc(usize) ?*c_void; +pub extern "c" fn realloc(*c_void, usize) ?*c_void; +pub extern "c" fn free(*c_void) void; +pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; -pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, noalias attr: ?&const pthread_attr_t, start_routine: extern fn (?&c_void) ?&c_void, noalias arg: ?&c_void) c_int; -pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; -pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; -pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; +pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; +pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; -pub const pthread_t = &@OpaqueType(); +pub const pthread_t = *@OpaqueType(); diff --git a/std/c/linux.zig b/std/c/linux.zig index 7810fec130..0ab043533e 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,7 +1,7 @@ pub use @import("../os/linux/errno.zig"); -pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; -extern "c" fn __errno_location() &c_int; +pub extern "c" fn getrandom(buf_ptr: *u8, buf_len: usize, flags: c_uint) c_int; +extern "c" fn __errno_location() *c_int; pub const _errno = __errno_location; pub const pthread_attr_t = extern struct { diff --git a/std/c/windows.zig b/std/c/windows.zig index 6e8b17eda8..35ca217131 100644 --- a/std/c/windows.zig +++ b/std/c/windows.zig @@ -1 +1 @@ -pub extern "c" fn _errno() &c_int; +pub extern "c" fn _errno() *c_int; diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index bf3193b5d9..f0a9766c00 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -75,7 +75,7 @@ fn Blake2s(comptime out_len: usize) type { return s; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { mem.copy(u32, d.h[0..], iv[0..]); // No key plus default parameters @@ -90,7 +90,7 @@ fn Blake2s(comptime out_len: usize) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -113,7 +113,7 @@ fn Blake2s(comptime out_len: usize) type { d.buf_len += u8(b[off..].len); } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= out_len / 8); mem.set(u8, d.buf[d.buf_len..], 0); @@ -127,7 +127,7 @@ fn Blake2s(comptime out_len: usize) type { } } - fn round(d: &Self, b: []const u8, last: bool) void { + fn round(d: *Self, b: []const u8, last: bool) void { debug.assert(b.len == 64); var m: [16]u32 = undefined; @@ -310,7 +310,7 @@ fn Blake2b(comptime out_len: usize) type { return s; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { mem.copy(u64, d.h[0..], iv[0..]); // No key plus default parameters @@ -325,7 +325,7 @@ fn Blake2b(comptime out_len: usize) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -348,7 +348,7 @@ fn Blake2b(comptime out_len: usize) type { d.buf_len += u8(b[off..].len); } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); @@ -360,7 +360,7 @@ fn Blake2b(comptime out_len: usize) type { } } - fn round(d: &Self, b: []const u8, last: bool) void { + fn round(d: *Self, b: []const u8, last: bool) void { debug.assert(b.len == 128); var m: [16]u64 = undefined; diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index 3d05597273..c0d1732d37 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -44,7 +44,7 @@ pub const Md5 = struct { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = 0x67452301; d.s[1] = 0xEFCDAB89; d.s[2] = 0x98BADCFE; @@ -59,7 +59,7 @@ pub const Md5 = struct { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -84,7 +84,7 @@ pub const Md5 = struct { d.total_len +%= b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= 16); // The buffer here will never be completely full. @@ -116,7 +116,7 @@ pub const Md5 = struct { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 64); var s: [16]u32 = undefined; diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index e9d8e3e132..9e46fc9239 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -43,7 +43,7 @@ pub const Sha1 = struct { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = 0x67452301; d.s[1] = 0xEFCDAB89; d.s[2] = 0x98BADCFE; @@ -59,7 +59,7 @@ pub const Sha1 = struct { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -83,7 +83,7 @@ pub const Sha1 = struct { d.total_len += b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= 20); // The buffer here will never be completely full. @@ -115,7 +115,7 @@ pub const Sha1 = struct { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 64); var s: [16]u32 = undefined; diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index aedc820f44..d1375d73e8 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -93,7 +93,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = params.iv0; d.s[1] = params.iv1; d.s[2] = params.iv2; @@ -112,7 +112,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -136,7 +136,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { d.total_len += b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= params.out_len / 8); // The buffer here will never be completely full. @@ -171,7 +171,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 64); var s: [64]u32 = undefined; @@ -434,7 +434,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { d.s[0] = params.iv0; d.s[1] = params.iv1; d.s[2] = params.iv2; @@ -453,7 +453,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial buffer exists from previous update. Copy into buffer then hash. @@ -477,7 +477,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { d.total_len += b.len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { debug.assert(out.len >= params.out_len / 8); // The buffer here will never be completely full. @@ -512,7 +512,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { } } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 128); var s: [80]u64 = undefined; diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig index 75bec57a87..ae02d7a482 100644 --- a/std/crypto/sha3.zig +++ b/std/crypto/sha3.zig @@ -26,7 +26,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { return d; } - pub fn reset(d: &Self) void { + pub fn reset(d: *Self) void { mem.set(u8, d.s[0..], 0); d.offset = 0; d.rate = 200 - (bits / 4); @@ -38,7 +38,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { d.final(out); } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var ip: usize = 0; var len = b.len; var rate = d.rate - d.offset; @@ -63,7 +63,7 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { d.offset = offset + len; } - pub fn final(d: &Self, out: []u8) void { + pub fn final(d: *Self, out: []u8) void { // padding d.s[d.offset] ^= delim; d.s[d.rate - 1] ^= 0x80; diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index c5c4f9fe10..0ad6845d1a 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -15,8 +15,8 @@ const BytesToHash = 1024 * MiB; pub fn main() !void { var stdout_file = try std.io.getStdOut(); - var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); - const stdout = &stdout_out_stream.stream; + var stdout_out_stream = std.io.FileOutStream.init(*stdout_file); + const stdout = *stdout_out_stream.stream; var block: [HashFunction.block_size]u8 = undefined; std.mem.set(u8, block[0..], 0); diff --git a/std/cstr.zig b/std/cstr.zig index c9f3026064..dfbfb8047f 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -9,13 +9,13 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; -pub fn len(ptr: &const u8) usize { +pub fn len(ptr: *const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; } -pub fn cmp(a: &const u8, b: &const u8) i8 { +pub fn cmp(a: *const u8, b: *const u8) i8 { var index: usize = 0; while (a[index] == b[index] and a[index] != 0) : (index += 1) {} if (a[index] > b[index]) { @@ -27,11 +27,11 @@ pub fn cmp(a: &const u8, b: &const u8) i8 { } } -pub fn toSliceConst(str: &const u8) []const u8 { +pub fn toSliceConst(str: *const u8) []const u8 { return str[0..len(str)]; } -pub fn toSlice(str: &u8) []u8 { +pub fn toSlice(str: *u8) []u8 { return str[0..len(str)]; } @@ -47,7 +47,7 @@ fn testCStrFnsImpl() void { /// Returns a mutable slice with 1 more byte of length which is a null byte. /// Caller owns the returned memory. -pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 { +pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![]u8 { const result = try allocator.alloc(u8, slice.len + 1); mem.copy(u8, result, slice); result[slice.len] = 0; @@ -55,13 +55,13 @@ pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 { } pub const NullTerminated2DArray = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, byte_count: usize, - ptr: ?&?&u8, + ptr: ?*?*u8, /// Takes N lists of strings, concatenates the lists together, and adds a null terminator /// Caller must deinit result - pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray { + pub fn fromSlices(allocator: *mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray { var new_len: usize = 1; // 1 for the list null var byte_count: usize = 0; for (slices) |slice| { @@ -75,11 +75,11 @@ pub const NullTerminated2DArray = struct { const index_size = @sizeOf(usize) * new_len; // size of the ptrs byte_count += index_size; - const buf = try allocator.alignedAlloc(u8, @alignOf(?&u8), byte_count); + const buf = try allocator.alignedAlloc(u8, @alignOf(?*u8), byte_count); errdefer allocator.free(buf); var write_index = index_size; - const index_buf = ([]?&u8)(buf); + const index_buf = ([]?*u8)(buf); var i: usize = 0; for (slices) |slice| { @@ -97,12 +97,12 @@ pub const NullTerminated2DArray = struct { return NullTerminated2DArray{ .allocator = allocator, .byte_count = byte_count, - .ptr = @ptrCast(?&?&u8, buf.ptr), + .ptr = @ptrCast(?*?*u8, buf.ptr), }; } - pub fn deinit(self: &NullTerminated2DArray) void { - const buf = @ptrCast(&u8, self.ptr); + pub fn deinit(self: *NullTerminated2DArray) void { + const buf = @ptrCast(*u8, self.ptr); self.allocator.free(buf[0..self.byte_count]); } }; diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig index 6b5edff5bf..e16dd21db4 100644 --- a/std/debug/failing_allocator.zig +++ b/std/debug/failing_allocator.zig @@ -7,12 +7,12 @@ pub const FailingAllocator = struct { allocator: mem.Allocator, index: usize, fail_index: usize, - internal_allocator: &mem.Allocator, + internal_allocator: *mem.Allocator, allocated_bytes: usize, freed_bytes: usize, deallocations: usize, - pub fn init(allocator: &mem.Allocator, fail_index: usize) FailingAllocator { + pub fn init(allocator: *mem.Allocator, fail_index: usize) FailingAllocator { return FailingAllocator{ .internal_allocator = allocator, .fail_index = fail_index, @@ -28,7 +28,7 @@ pub const FailingAllocator = struct { }; } - fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *mem.Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); if (self.index == self.fail_index) { return error.OutOfMemory; @@ -39,7 +39,7 @@ pub const FailingAllocator = struct { return result; } - fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); if (new_size <= old_mem.len) { self.freed_bytes += old_mem.len - new_size; @@ -55,7 +55,7 @@ pub const FailingAllocator = struct { return result; } - fn free(allocator: &mem.Allocator, bytes: []u8) void { + fn free(allocator: *mem.Allocator, bytes: []u8) void { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); self.freed_bytes += bytes.len; self.deallocations += 1; diff --git a/std/debug/index.zig b/std/debug/index.zig index 92e565b391..00d9bef121 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -16,12 +16,12 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; /// TODO atomic/multithread support var stderr_file: os.File = undefined; var stderr_file_out_stream: io.FileOutStream = undefined; -var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null; +var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null; pub fn warn(comptime fmt: []const u8, args: ...) void { const stderr = getStderrStream() catch return; stderr.print(fmt, args) catch return; } -fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) { +fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { if (stderr_stream) |st| { return st; } else { @@ -33,8 +33,8 @@ fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) { } } -var self_debug_info: ?&ElfStackTrace = null; -pub fn getSelfDebugInfo() !&ElfStackTrace { +var self_debug_info: ?*ElfStackTrace = null; +pub fn getSelfDebugInfo() !*ElfStackTrace { if (self_debug_info) |info| { return info; } else { @@ -58,7 +58,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { } /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. -pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void { +pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void { const stderr = getStderrStream() catch return; const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; @@ -104,7 +104,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn { var panicking: u8 = 0; // TODO make this a bool -pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { +pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { @setCold(true); if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { @@ -130,7 +130,7 @@ const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; -pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool) !void { +pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool) !void { var frame_index: usize = undefined; var frames_left: usize = undefined; if (stack_trace.index < stack_trace.instruction_addresses.len) { @@ -150,7 +150,7 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, } } -pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { +pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { const AddressState = union(enum) { NotLookingForStartAddress, LookingForStartAddress: usize, @@ -166,8 +166,8 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_ } var fp = @ptrToInt(@frameAddress()); - while (fp != 0) : (fp = @intToPtr(&const usize, fp).*) { - const return_address = @intToPtr(&const usize, fp + @sizeOf(usize)).*; + while (fp != 0) : (fp = @intToPtr(*const usize, fp).*) { + const return_address = @intToPtr(*const usize, fp + @sizeOf(usize)).*; switch (addr_state) { AddressState.NotLookingForStartAddress => {}, @@ -183,7 +183,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_ } } -fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void { +fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize) !void { const ptr_hex = "0x{x}"; switch (builtin.os) { @@ -236,7 +236,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us } } -pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { +pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { switch (builtin.object_format) { builtin.ObjectFormat.elf => { const st = try allocator.create(ElfStackTrace); @@ -289,7 +289,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { } } -fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void { +fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void { var f = try os.File.openRead(allocator, line_info.file_name); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -325,32 +325,32 @@ pub const ElfStackTrace = switch (builtin.os) { builtin.Os.macosx => struct { symbol_table: macho.SymbolTable, - pub fn close(self: &ElfStackTrace) void { + pub fn close(self: *ElfStackTrace) void { self.symbol_table.deinit(); } }, else => struct { self_exe_file: os.File, elf: elf.Elf, - debug_info: &elf.SectionHeader, - debug_abbrev: &elf.SectionHeader, - debug_str: &elf.SectionHeader, - debug_line: &elf.SectionHeader, - debug_ranges: ?&elf.SectionHeader, + debug_info: *elf.SectionHeader, + debug_abbrev: *elf.SectionHeader, + debug_str: *elf.SectionHeader, + debug_line: *elf.SectionHeader, + debug_ranges: ?*elf.SectionHeader, abbrev_table_list: ArrayList(AbbrevTableHeader), compile_unit_list: ArrayList(CompileUnit), - pub fn allocator(self: &const ElfStackTrace) &mem.Allocator { + pub fn allocator(self: *const ElfStackTrace) *mem.Allocator { return self.abbrev_table_list.allocator; } - pub fn readString(self: &ElfStackTrace) ![]u8 { + pub fn readString(self: *ElfStackTrace) ![]u8 { var in_file_stream = io.FileInStream.init(&self.self_exe_file); const in_stream = &in_file_stream.stream; return readStringRaw(self.allocator(), in_stream); } - pub fn close(self: &ElfStackTrace) void { + pub fn close(self: *ElfStackTrace) void { self.self_exe_file.close(); self.elf.close(); } @@ -365,7 +365,7 @@ const PcRange = struct { const CompileUnit = struct { version: u16, is_64: bool, - die: &Die, + die: *Die, index: usize, pc_range: ?PcRange, }; @@ -408,7 +408,7 @@ const Constant = struct { payload: []u8, signed: bool, - fn asUnsignedLe(self: &const Constant) !u64 { + fn asUnsignedLe(self: *const Constant) !u64 { if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; if (self.signed) return error.InvalidDebugInfo; return mem.readInt(self.payload, u64, builtin.Endian.Little); @@ -425,14 +425,14 @@ const Die = struct { value: FormValue, }; - fn getAttr(self: &const Die, id: u64) ?&const FormValue { + fn getAttr(self: *const Die, id: u64) ?*const FormValue { for (self.attrs.toSliceConst()) |*attr| { if (attr.id == id) return &attr.value; } return null; } - fn getAttrAddr(self: &const Die, id: u64) !u64 { + fn getAttrAddr(self: *const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Address => |value| value, @@ -440,7 +440,7 @@ const Die = struct { }; } - fn getAttrSecOffset(self: &const Die, id: u64) !u64 { + fn getAttrSecOffset(self: *const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), @@ -449,7 +449,7 @@ const Die = struct { }; } - fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 { + fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), @@ -457,7 +457,7 @@ const Die = struct { }; } - fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 { + fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 { const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, @@ -478,9 +478,9 @@ const LineInfo = struct { line: usize, column: usize, file_name: []u8, - allocator: &mem.Allocator, + allocator: *mem.Allocator, - fn deinit(self: &const LineInfo) void { + fn deinit(self: *const LineInfo) void { self.allocator.free(self.file_name); } }; @@ -496,7 +496,7 @@ const LineNumberProgram = struct { target_address: usize, include_dirs: []const []const u8, - file_entries: &ArrayList(FileEntry), + file_entries: *ArrayList(FileEntry), prev_address: usize, prev_file: usize, @@ -506,7 +506,7 @@ const LineNumberProgram = struct { prev_basic_block: bool, prev_end_sequence: bool, - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram { + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { return LineNumberProgram{ .address = 0, .file = 1, @@ -528,7 +528,7 @@ const LineNumberProgram = struct { }; } - pub fn checkLineMatch(self: &LineNumberProgram) !?LineInfo { + pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { if (self.target_address >= self.prev_address and self.target_address < self.address) { const file_entry = if (self.prev_file == 0) { return error.MissingDebugInfo; @@ -562,7 +562,7 @@ const LineNumberProgram = struct { } }; -fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { +fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { var buf = ArrayList(u8).init(allocator); while (true) { const byte = try in_stream.readByte(); @@ -572,30 +572,30 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { return buf.toSlice(); } -fn getString(st: &ElfStackTrace, offset: u64) ![]u8 { +fn getString(st: *ElfStackTrace, offset: u64) ![]u8 { const pos = st.debug_str.offset + offset; try st.self_exe_file.seekTo(pos); return st.readString(); } -fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 { +fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { const buf = try allocator.alloc(u8, size); errdefer allocator.free(buf); if ((try in_stream.read(buf)) < size) return error.EndOfFile; return buf; } -fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { +fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); return FormValue{ .Block = buf }; } -fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { +fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size); return parseFormValueBlockLen(allocator, in_stream, block_len); } -fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { +fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { return FormValue{ .Const = Constant{ .signed = signed, @@ -612,12 +612,12 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 { return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable; } -fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { +fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); return FormValue{ .Ref = buf }; } -fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue { +fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue { const block_len = try in_stream.readIntLe(T); return parseFormValueRefLen(allocator, in_stream, block_len); } @@ -632,7 +632,7 @@ const ParseFormValueError = error{ OutOfMemory, }; -fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { +fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { return switch (form_id) { DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), @@ -682,7 +682,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64 }; } -fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { +fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable { const in_file = &st.self_exe_file; var in_file_stream = io.FileInStream.init(in_file); const in_stream = &in_file_stream.stream; @@ -712,7 +712,7 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable { /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, /// seeks in the stream and parses it. -fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { +fn getAbbrevTable(st: *ElfStackTrace, abbrev_offset: u64) !*const AbbrevTable { for (st.abbrev_table_list.toSlice()) |*header| { if (header.offset == abbrev_offset) { return &header.table; @@ -726,14 +726,14 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable { return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table; } -fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&const AbbrevTableEntry { +fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { for (abbrev_table.toSliceConst()) |*table_entry| { if (table_entry.abbrev_code == abbrev_code) return table_entry; } return null; } -fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) !Die { +fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !Die { const in_file = &st.self_exe_file; var in_file_stream = io.FileInStream.init(in_file); const in_stream = &in_file_stream.stream; @@ -755,7 +755,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) ! return result; } -fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) !LineInfo { +fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, target_address: usize) !LineInfo { const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir); const in_file = &st.self_exe_file; @@ -934,7 +934,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe return error.MissingDebugInfo; } -fn scanAllCompileUnits(st: &ElfStackTrace) !void { +fn scanAllCompileUnits(st: *ElfStackTrace) !void { const debug_info_end = st.debug_info.offset + st.debug_info.size; var this_unit_offset = st.debug_info.offset; var cu_index: usize = 0; @@ -1005,7 +1005,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { } } -fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit { +fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit { var in_file_stream = io.FileInStream.init(&st.self_exe_file); const in_stream = &in_file_stream.stream; for (st.compile_unit_list.toSlice()) |*compile_unit| { @@ -1039,7 +1039,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit return error.MissingDebugInfo; } -fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 { +fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { const first_32_bits = try in_stream.readIntLe(u32); is_64.* = (first_32_bits == 0xffffffff); if (is_64.*) { @@ -1096,10 +1096,10 @@ var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator var global_allocator_mem: [100 * 1024]u8 = undefined; // TODO make thread safe -var debug_info_allocator: ?&mem.Allocator = null; +var debug_info_allocator: ?*mem.Allocator = null; var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; -fn getDebugInfoAllocator() &mem.Allocator { +fn getDebugInfoAllocator() *mem.Allocator { if (debug_info_allocator) |a| return a; debug_info_direct_allocator = std.heap.DirectAllocator.init(); diff --git a/std/elf.zig b/std/elf.zig index 29b9473f98..50e97ab271 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -338,7 +338,7 @@ pub const SectionHeader = struct { }; pub const Elf = struct { - in_file: &os.File, + in_file: *os.File, auto_close_stream: bool, is_64: bool, endian: builtin.Endian, @@ -348,20 +348,20 @@ pub const Elf = struct { program_header_offset: u64, section_header_offset: u64, string_section_index: u64, - string_section: &SectionHeader, + string_section: *SectionHeader, section_headers: []SectionHeader, - allocator: &mem.Allocator, + allocator: *mem.Allocator, prealloc_file: os.File, /// Call close when done. - pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void { + pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void { try elf.prealloc_file.open(path); - try elf.openFile(allocator, &elf.prealloc_file); + try elf.openFile(allocator, *elf.prealloc_file); elf.auto_close_stream = true; } /// Call close when done. - pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &os.File) !void { + pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void { elf.allocator = allocator; elf.in_file = file; elf.auto_close_stream = false; @@ -503,13 +503,13 @@ pub const Elf = struct { } } - pub fn close(elf: &Elf) void { + pub fn close(elf: *Elf) void { elf.allocator.free(elf.section_headers); if (elf.auto_close_stream) elf.in_file.close(); } - pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader { + pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { var file_stream = io.FileInStream.init(elf.in_file); const in = &file_stream.stream; @@ -533,7 +533,7 @@ pub const Elf = struct { return null; } - pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void { + pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void { try elf.in_file.seekTo(elf_section.offset); } }; diff --git a/std/event.zig b/std/event.zig index 4604eb8d02..89ab816bb6 100644 --- a/std/event.zig +++ b/std/event.zig @@ -6,9 +6,9 @@ const mem = std.mem; const posix = std.os.posix; pub const TcpServer = struct { - handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void, + handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, - loop: &Loop, + loop: *Loop, sockfd: i32, accept_coro: ?promise, listen_address: std.net.Address, @@ -17,7 +17,7 @@ pub const TcpServer = struct { const PromiseNode = std.LinkedList(promise).Node; - pub fn init(loop: &Loop) !TcpServer { + pub fn init(loop: *Loop) !TcpServer { const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); errdefer std.os.close(sockfd); @@ -32,7 +32,7 @@ pub const TcpServer = struct { }; } - pub fn listen(self: &TcpServer, address: &const std.net.Address, handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void) !void { + pub fn listen(self: *TcpServer, address: *const std.net.Address, handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void) !void { self.handleRequestFn = handleRequestFn; try std.os.posixBind(self.sockfd, &address.os_addr); @@ -46,13 +46,13 @@ pub const TcpServer = struct { errdefer self.loop.removeFd(self.sockfd); } - pub fn deinit(self: &TcpServer) void { + pub fn deinit(self: *TcpServer) void { self.loop.removeFd(self.sockfd); if (self.accept_coro) |accept_coro| cancel accept_coro; std.os.close(self.sockfd); } - pub async fn handler(self: &TcpServer) void { + pub async fn handler(self: *TcpServer) void { while (true) { var accepted_addr: std.net.Address = undefined; if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { @@ -92,11 +92,11 @@ pub const TcpServer = struct { }; pub const Loop = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, epollfd: i32, keep_running: bool, - fn init(allocator: &mem.Allocator) !Loop { + fn init(allocator: *mem.Allocator) !Loop { const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); return Loop{ .keep_running = true, @@ -105,7 +105,7 @@ pub const Loop = struct { }; } - pub fn addFd(self: &Loop, fd: i32, prom: promise) !void { + pub fn addFd(self: *Loop, fd: i32, prom: promise) !void { var ev = std.os.linux.epoll_event{ .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) }, @@ -113,23 +113,23 @@ pub const Loop = struct { try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); } - pub fn removeFd(self: &Loop, fd: i32) void { + pub fn removeFd(self: *Loop, fd: i32) void { std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; } - async fn waitFd(self: &Loop, fd: i32) !void { + async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); suspend |p| { try self.addFd(fd, p); } } - pub fn stop(self: &Loop) void { + pub fn stop(self: *Loop) void { // TODO make atomic self.keep_running = false; // TODO activate an fd in the epoll set } - pub fn run(self: &Loop) void { + pub fn run(self: *Loop) void { while (self.keep_running) { var events: [16]std.os.linux.epoll_event = undefined; const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1); @@ -141,7 +141,7 @@ pub const Loop = struct { } }; -pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File { +pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File { var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); @@ -163,7 +163,7 @@ test "listen on a port, send bytes, receive bytes" { tcp_server: TcpServer, const Self = this; - async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address, _socket: &const std.os.File) void { + async<*mem.Allocator> fn handler(tcp_server: *TcpServer, _addr: *const std.net.Address, _socket: *const std.os.File) void { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 defer socket.close(); @@ -177,7 +177,7 @@ test "listen on a port, send bytes, receive bytes" { cancel p; } } - async fn errorableHandler(self: &Self, _addr: &const std.net.Address, _socket: &const std.os.File) !void { + async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 @@ -199,7 +199,7 @@ test "listen on a port, send bytes, receive bytes" { defer cancel p; loop.run(); } -async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void { +async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { errdefer @panic("test failure"); var socket_file = try await try async event.connect(loop, address); diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 65e8d448a8..933958ac18 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -21,7 +21,7 @@ pub const RoundMode = enum { /// Round a FloatDecimal as returned by errol3 to the specified fractional precision. /// All digits after the specified precision should be considered invalid. -pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void { +pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: RoundMode) void { // The round digit refers to the index which we should look at to determine // whether we need to round to match the specified precision. var round_digit: usize = 0; @@ -59,7 +59,7 @@ pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: Ro float_decimal.exp += 1; // Re-size the buffer to use the reserved leading byte. - const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1); + const one_before = @intToPtr(*u8, @ptrToInt(&float_decimal.digits[0]) - 1); float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; float_decimal.digits[0] = '1'; return; @@ -217,7 +217,7 @@ fn tableLowerBound(k: u64) usize { /// @in: The HP number. /// @val: The double. /// &returns: The HP number. -fn hpProd(in: &const HP, val: f64) HP { +fn hpProd(in: *const HP, val: f64) HP { var hi: f64 = undefined; var lo: f64 = undefined; split(in.val, &hi, &lo); @@ -239,7 +239,7 @@ fn hpProd(in: &const HP, val: f64) HP { /// @val: The double. /// @hi: The high bits. /// @lo: The low bits. -fn split(val: f64, hi: &f64, lo: &f64) void { +fn split(val: f64, hi: *f64, lo: *f64) void { hi.* = gethi(val); lo.* = val - hi.*; } @@ -252,7 +252,7 @@ fn gethi(in: f64) f64 { /// Normalize the number by factoring in the error. /// @hp: The float pair. -fn hpNormalize(hp: &HP) void { +fn hpNormalize(hp: *HP) void { // Required to avoid segfaults causing buffer overrun during errol3 digit output termination. @setFloatMode(this, @import("builtin").FloatMode.Strict); @@ -264,7 +264,7 @@ fn hpNormalize(hp: &HP) void { /// Divide the high-precision number by ten. /// @hp: The high-precision number -fn hpDiv10(hp: &HP) void { +fn hpDiv10(hp: *HP) void { var val = hp.val; hp.val /= 10.0; @@ -280,7 +280,7 @@ fn hpDiv10(hp: &HP) void { /// Multiply the high-precision number by ten. /// @hp: The high-precision number -fn hpMul10(hp: &HP) void { +fn hpMul10(hp: *HP) void { const val = hp.val; hp.val *= 10.0; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 0ffbc59895..b522d9d37d 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -679,7 +679,7 @@ const FormatIntBuf = struct { out_buf: []u8, index: usize, }; -fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) { +fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { mem.copy(u8, context.out_buf[context.index..], bytes); context.index += bytes.len; } @@ -751,7 +751,7 @@ const BufPrintContext = struct { remaining: []u8, }; -fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void { +fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { if (context.remaining.len < bytes.len) return error.BufferTooSmall; mem.copy(u8, context.remaining, bytes); context.remaining = context.remaining[bytes.len..]; @@ -763,14 +763,14 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { return buf[0 .. buf.len - context.remaining.len]; } -pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } -fn countSize(size: &usize, bytes: []const u8) (error{}!void) { +fn countSize(size: *usize, bytes: []const u8) (error{}!void) { size.* += bytes.len; } diff --git a/std/hash/adler.zig b/std/hash/adler.zig index 12dab1457c..9c5966f89b 100644 --- a/std/hash/adler.zig +++ b/std/hash/adler.zig @@ -18,7 +18,7 @@ pub const Adler32 = struct { // This fast variant is taken from zlib. It reduces the required modulos and unrolls longer // buffer inputs and should be much quicker. - pub fn update(self: &Adler32, input: []const u8) void { + pub fn update(self: *Adler32, input: []const u8) void { var s1 = self.adler & 0xffff; var s2 = (self.adler >> 16) & 0xffff; @@ -77,7 +77,7 @@ pub const Adler32 = struct { self.adler = s1 | (s2 << 16); } - pub fn final(self: &Adler32) u32 { + pub fn final(self: *Adler32) u32 { return self.adler; } diff --git a/std/hash/crc.zig b/std/hash/crc.zig index 45bcb70e8b..ec831cdc2e 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -58,7 +58,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { return Self{ .crc = 0xffffffff }; } - pub fn update(self: &Self, input: []const u8) void { + pub fn update(self: *Self, input: []const u8) void { var i: usize = 0; while (i + 8 <= input.len) : (i += 8) { const p = input[i .. i + 8]; @@ -86,7 +86,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { } } - pub fn final(self: &Self) u32 { + pub fn final(self: *Self) u32 { return ~self.crc; } @@ -143,14 +143,14 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { return Self{ .crc = 0xffffffff }; } - pub fn update(self: &Self, input: []const u8) void { + pub fn update(self: *Self, input: []const u8) void { for (input) |b| { self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 0))] ^ (self.crc >> 4); self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 4))] ^ (self.crc >> 4); } } - pub fn final(self: &Self) u32 { + pub fn final(self: *Self) u32 { return ~self.crc; } diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig index c2439e0ebc..447c996772 100644 --- a/std/hash/fnv.zig +++ b/std/hash/fnv.zig @@ -21,14 +21,14 @@ fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type { return Self{ .value = offset }; } - pub fn update(self: &Self, input: []const u8) void { + pub fn update(self: *Self, input: []const u8) void { for (input) |b| { self.value ^= b; self.value *%= prime; } } - pub fn final(self: &Self) T { + pub fn final(self: *Self) T { return self.value; } diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index 750e23d4c8..8a90308a46 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -63,7 +63,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) return d; } - pub fn update(d: &Self, b: []const u8) void { + pub fn update(d: *Self, b: []const u8) void { var off: usize = 0; // Partial from previous. @@ -85,7 +85,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) d.msg_len +%= @truncate(u8, b.len); } - pub fn final(d: &Self) T { + pub fn final(d: *Self) T { // Padding mem.set(u8, d.buf[d.buf_len..], 0); d.buf[7] = d.msg_len; @@ -118,7 +118,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) return (u128(b2) << 64) | b1; } - fn round(d: &Self, b: []const u8) void { + fn round(d: *Self, b: []const u8) void { debug.assert(b.len == 8); const m = mem.readInt(b[0..], u64, Endian.Little); @@ -132,7 +132,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) d.v0 ^= m; } - fn sipRound(d: &Self) void { + fn sipRound(d: *Self) void { d.v0 +%= d.v1; d.v1 = math.rotl(u64, d.v1, u64(13)); d.v1 ^= d.v0; diff --git a/std/hash_map.zig b/std/hash_map.zig index f51b9c66ba..a323cdc197 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -14,7 +14,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 entries: []Entry, size: usize, max_distance_from_start_index: usize, - allocator: &Allocator, + allocator: *Allocator, // this is used to detect bugs where a hashtable is edited while an iterator is running. modification_count: debug_u32, @@ -28,7 +28,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; pub const Iterator = struct { - hm: &const Self, + hm: *const Self, // how many items have we returned count: usize, // iterator through the entry array @@ -36,7 +36,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 // used to detect concurrent modification initial_modification_count: debug_u32, - pub fn next(it: &Iterator) ?&Entry { + pub fn next(it: *Iterator) ?*Entry { if (want_modification_safety) { assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification } @@ -53,7 +53,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } // Reset the iterator to the initial index - pub fn reset(it: &Iterator) void { + pub fn reset(it: *Iterator) void { it.count = 0; it.index = 0; // Resetting the modification count too @@ -61,7 +61,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } }; - pub fn init(allocator: &Allocator) Self { + pub fn init(allocator: *Allocator) Self { return Self{ .entries = []Entry{}, .allocator = allocator, @@ -71,11 +71,11 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; } - pub fn deinit(hm: &const Self) void { + pub fn deinit(hm: *const Self) void { hm.allocator.free(hm.entries); } - pub fn clear(hm: &Self) void { + pub fn clear(hm: *Self) void { for (hm.entries) |*entry| { entry.used = false; } @@ -84,12 +84,12 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 hm.incrementModificationCount(); } - pub fn count(hm: &const Self) usize { + pub fn count(hm: *const Self) usize { return hm.size; } /// Returns the value that was already there. - pub fn put(hm: &Self, key: K, value: &const V) !?V { + pub fn put(hm: *Self, key: K, value: *const V) !?V { if (hm.entries.len == 0) { try hm.initCapacity(16); } @@ -111,18 +111,18 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 return hm.internalPut(key, value); } - pub fn get(hm: &const Self, key: K) ?&Entry { + pub fn get(hm: *const Self, key: K) ?*Entry { if (hm.entries.len == 0) { return null; } return hm.internalGet(key); } - pub fn contains(hm: &const Self, key: K) bool { + pub fn contains(hm: *const Self, key: K) bool { return hm.get(key) != null; } - pub fn remove(hm: &Self, key: K) ?&Entry { + pub fn remove(hm: *Self, key: K) ?*Entry { if (hm.entries.len == 0) return null; hm.incrementModificationCount(); const start_index = hm.keyToIndex(key); @@ -154,7 +154,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 return null; } - pub fn iterator(hm: &const Self) Iterator { + pub fn iterator(hm: *const Self) Iterator { return Iterator{ .hm = hm, .count = 0, @@ -163,7 +163,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 }; } - fn initCapacity(hm: &Self, capacity: usize) !void { + fn initCapacity(hm: *Self, capacity: usize) !void { hm.entries = try hm.allocator.alloc(Entry, capacity); hm.size = 0; hm.max_distance_from_start_index = 0; @@ -172,14 +172,14 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } } - fn incrementModificationCount(hm: &Self) void { + fn incrementModificationCount(hm: *Self) void { if (want_modification_safety) { hm.modification_count +%= 1; } } /// Returns the value that was already there. - fn internalPut(hm: &Self, orig_key: K, orig_value: &const V) ?V { + fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V { var key = orig_key; var value = orig_value.*; const start_index = hm.keyToIndex(key); @@ -231,7 +231,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 unreachable; // put into a full map } - fn internalGet(hm: &const Self, key: K) ?&Entry { + fn internalGet(hm: *const Self, key: K) ?*Entry { const start_index = hm.keyToIndex(key); { var roll_over: usize = 0; @@ -246,7 +246,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 return null; } - fn keyToIndex(hm: &const Self, key: K) usize { + fn keyToIndex(hm: *const Self, key: K) usize { return usize(hash(key)) % hm.entries.len; } }; diff --git a/std/heap.zig b/std/heap.zig index 8d4938a7c3..81d6f25282 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -16,15 +16,15 @@ var c_allocator_state = Allocator{ .freeFn = cFree, }; -fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 { +fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { assert(alignment <= @alignOf(c_longdouble)); - return if (c.malloc(n)) |buf| @ptrCast(&u8, buf)[0..n] else error.OutOfMemory; + return if (c.malloc(n)) |buf| @ptrCast(*u8, buf)[0..n] else error.OutOfMemory; } -fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - const old_ptr = @ptrCast(&c_void, old_mem.ptr); +fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + const old_ptr = @ptrCast(*c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { - return @ptrCast(&u8, buf)[0..new_size]; + return @ptrCast(*u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -32,8 +32,8 @@ fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![ } } -fn cFree(self: &Allocator, old_mem: []u8) void { - const old_ptr = @ptrCast(&c_void, old_mem.ptr); +fn cFree(self: *Allocator, old_mem: []u8) void { + const old_ptr = @ptrCast(*c_void, old_mem.ptr); c.free(old_ptr); } @@ -55,7 +55,7 @@ pub const DirectAllocator = struct { }; } - pub fn deinit(self: &DirectAllocator) void { + pub fn deinit(self: *DirectAllocator) void { switch (builtin.os) { Os.windows => if (self.heap_handle) |heap_handle| { _ = os.windows.HeapDestroy(heap_handle); @@ -64,7 +64,7 @@ pub const DirectAllocator = struct { } } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -74,7 +74,7 @@ pub const DirectAllocator = struct { const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr(&u8, addr)[0..n]; + if (alloc_size == n) return @intToPtr(*u8, addr)[0..n]; var aligned_addr = addr & ~usize(alignment - 1); aligned_addr += alignment; @@ -93,7 +93,7 @@ pub const DirectAllocator = struct { //It is impossible that there is an unoccupied page at the top of our // mmap. - return @intToPtr(&u8, aligned_addr)[0..n]; + return @intToPtr(*u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); @@ -108,14 +108,14 @@ pub const DirectAllocator = struct { const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); const adjusted_addr = root_addr + march_forward_bytes; const record_addr = adjusted_addr + n; - @intToPtr(&align(1) usize, record_addr).* = root_addr; - return @intToPtr(&u8, adjusted_addr)[0..n]; + @intToPtr(*align(1) usize, record_addr).* = root_addr; + return @intToPtr(*u8, adjusted_addr)[0..n]; }, else => @compileError("Unsupported OS"), } } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -139,13 +139,13 @@ pub const DirectAllocator = struct { Os.windows => { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; - const root_addr = @intToPtr(&align(1) usize, old_record_addr).*; + const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; const old_ptr = @intToPtr(os.windows.LPVOID, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; const new_record_addr = old_record_addr - new_size + old_mem.len; - @intToPtr(&align(1) usize, new_record_addr).* = root_addr; + @intToPtr(*align(1) usize, new_record_addr).* = root_addr; return old_mem[0..new_size]; }; const offset = old_adjusted_addr - root_addr; @@ -153,14 +153,14 @@ pub const DirectAllocator = struct { const new_adjusted_addr = new_root_addr + offset; assert(new_adjusted_addr % alignment == 0); const new_record_addr = new_adjusted_addr + new_size; - @intToPtr(&align(1) usize, new_record_addr).* = new_root_addr; - return @intToPtr(&u8, new_adjusted_addr)[0..new_size]; + @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; + return @intToPtr(*u8, new_adjusted_addr)[0..new_size]; }, else => @compileError("Unsupported OS"), } } - fn free(allocator: &Allocator, bytes: []u8) void { + fn free(allocator: *Allocator, bytes: []u8) void { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { @@ -169,7 +169,7 @@ pub const DirectAllocator = struct { }, Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; - const root_addr = @intToPtr(&align(1) usize, record_addr).*; + const root_addr = @intToPtr(*align(1) usize, record_addr).*; const ptr = @intToPtr(os.windows.LPVOID, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, @@ -183,13 +183,13 @@ pub const DirectAllocator = struct { pub const ArenaAllocator = struct { pub allocator: Allocator, - child_allocator: &Allocator, + child_allocator: *Allocator, buffer_list: std.LinkedList([]u8), end_index: usize, const BufNode = std.LinkedList([]u8).Node; - pub fn init(child_allocator: &Allocator) ArenaAllocator { + pub fn init(child_allocator: *Allocator) ArenaAllocator { return ArenaAllocator{ .allocator = Allocator{ .allocFn = alloc, @@ -202,7 +202,7 @@ pub const ArenaAllocator = struct { }; } - pub fn deinit(self: &ArenaAllocator) void { + pub fn deinit(self: *ArenaAllocator) void { var it = self.buffer_list.first; while (it) |node| { // this has to occur before the free because the free frees node @@ -212,7 +212,7 @@ pub const ArenaAllocator = struct { } } - fn createNode(self: &ArenaAllocator, prev_len: usize, minimum_size: usize) !&BufNode { + fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode { const actual_min_size = minimum_size + @sizeOf(BufNode); var len = prev_len; while (true) { @@ -233,7 +233,7 @@ pub const ArenaAllocator = struct { return buf_node; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator); var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment); @@ -254,7 +254,7 @@ pub const ArenaAllocator = struct { } } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -264,7 +264,7 @@ pub const ArenaAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void {} + fn free(allocator: *Allocator, bytes: []u8) void {} }; pub const FixedBufferAllocator = struct { @@ -284,7 +284,7 @@ pub const FixedBufferAllocator = struct { }; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); const addr = @ptrToInt(self.buffer.ptr) + self.end_index; const rem = @rem(addr, alignment); @@ -300,7 +300,7 @@ pub const FixedBufferAllocator = struct { return result; } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -310,7 +310,7 @@ pub const FixedBufferAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void {} + fn free(allocator: *Allocator, bytes: []u8) void {} }; /// lock free @@ -331,7 +331,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { }; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { + fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); while (true) { @@ -343,11 +343,11 @@ pub const ThreadSafeFixedBufferAllocator = struct { if (new_end_index > self.buffer.len) { return error.OutOfMemory; } - end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index]; + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst,) ?? return self.buffer[adjusted_index..new_end_index]; } } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { + fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -357,7 +357,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { } } - fn free(allocator: &Allocator, bytes: []u8) void {} + fn free(allocator: *Allocator, bytes: []u8) void {} }; test "c_allocator" { @@ -403,8 +403,8 @@ test "ThreadSafeFixedBufferAllocator" { try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); } -fn testAllocator(allocator: &mem.Allocator) !void { - var slice = try allocator.alloc(&i32, 100); +fn testAllocator(allocator: *mem.Allocator) !void { + var slice = try allocator.alloc(*i32, 100); for (slice) |*item, i| { item.* = try allocator.create(i32); @@ -415,15 +415,15 @@ fn testAllocator(allocator: &mem.Allocator) !void { allocator.destroy(item); } - slice = try allocator.realloc(&i32, slice, 20000); - slice = try allocator.realloc(&i32, slice, 50); - slice = try allocator.realloc(&i32, slice, 25); - slice = try allocator.realloc(&i32, slice, 10); + slice = try allocator.realloc(*i32, slice, 20000); + slice = try allocator.realloc(*i32, slice, 50); + slice = try allocator.realloc(*i32, slice, 25); + slice = try allocator.realloc(*i32, slice, 10); allocator.free(slice); } -fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void { +fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void { //Maybe a platform's page_size is actually the same as or // very near usize? if (os.page_size << 2 > @maxValue(usize)) return; diff --git a/std/io.zig b/std/io.zig index 39d319159e..e20a284e4e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -34,20 +34,20 @@ pub fn getStdIn() GetStdIoErrs!File { /// Implementation of InStream trait for File pub const FileInStream = struct { - file: &File, + file: *File, stream: Stream, pub const Error = @typeOf(File.read).ReturnType.ErrorSet; pub const Stream = InStream(Error); - pub fn init(file: &File) FileInStream { + pub fn init(file: *File) FileInStream { return FileInStream{ .file = file, .stream = Stream{ .readFn = readFn }, }; } - fn readFn(in_stream: &Stream, buffer: []u8) Error!usize { + fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { const self = @fieldParentPtr(FileInStream, "stream", in_stream); return self.file.read(buffer); } @@ -55,20 +55,20 @@ pub const FileInStream = struct { /// Implementation of OutStream trait for File pub const FileOutStream = struct { - file: &File, + file: *File, stream: Stream, pub const Error = File.WriteError; pub const Stream = OutStream(Error); - pub fn init(file: &File) FileOutStream { + pub fn init(file: *File) FileOutStream { return FileOutStream{ .file = file, .stream = Stream{ .writeFn = writeFn }, }; } - fn writeFn(out_stream: &Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) !void { const self = @fieldParentPtr(FileOutStream, "stream", out_stream); return self.file.write(bytes); } @@ -82,12 +82,12 @@ pub fn InStream(comptime ReadError: type) type { /// Return the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - readFn: fn (self: &Self, buffer: []u8) Error!usize, + readFn: fn (self: *Self, buffer: []u8) Error!usize, /// Replaces `buffer` contents by reading from the stream until it is finished. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and /// the contents read from the stream are lost. - pub fn readAllBuffer(self: &Self, buffer: &Buffer, max_size: usize) !void { + pub fn readAllBuffer(self: *Self, buffer: *Buffer, max_size: usize) !void { try buffer.resize(0); var actual_buf_len: usize = 0; @@ -111,7 +111,7 @@ pub fn InStream(comptime ReadError: type) type { /// memory would be greater than `max_size`, returns `error.StreamTooLong`. /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. - pub fn readAllAlloc(self: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 { + pub fn readAllAlloc(self: *Self, allocator: *mem.Allocator, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -123,7 +123,7 @@ pub fn InStream(comptime ReadError: type) type { /// Does not include the delimiter in the result. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents /// read from the stream so far are lost. - pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void { + pub fn readUntilDelimiterBuffer(self: *Self, buffer: *Buffer, delimiter: u8, max_size: usize) !void { try buffer.resize(0); while (true) { @@ -145,7 +145,7 @@ pub fn InStream(comptime ReadError: type) type { /// memory would be greater than `max_size`, returns `error.StreamTooLong`. /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. - pub fn readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator, delimiter: u8, max_size: usize) ![]u8 { + pub fn readUntilDelimiterAlloc(self: *Self, allocator: *mem.Allocator, delimiter: u8, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -156,43 +156,43 @@ pub fn InStream(comptime ReadError: type) type { /// Returns the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - pub fn read(self: &Self, buffer: []u8) !usize { + pub fn read(self: *Self, buffer: []u8) !usize { return self.readFn(self, buffer); } /// Same as `read` but end of stream returns `error.EndOfStream`. - pub fn readNoEof(self: &Self, buf: []u8) !void { + pub fn readNoEof(self: *Self, buf: []u8) !void { const amt_read = try self.read(buf); if (amt_read < buf.len) return error.EndOfStream; } /// Reads 1 byte from the stream or returns `error.EndOfStream`. - pub fn readByte(self: &Self) !u8 { + pub fn readByte(self: *Self) !u8 { var result: [1]u8 = undefined; try self.readNoEof(result[0..]); return result[0]; } /// Same as `readByte` except the returned byte is signed. - pub fn readByteSigned(self: &Self) !i8 { + pub fn readByteSigned(self: *Self) !i8 { return @bitCast(i8, try self.readByte()); } - pub fn readIntLe(self: &Self, comptime T: type) !T { + pub fn readIntLe(self: *Self, comptime T: type) !T { return self.readInt(builtin.Endian.Little, T); } - pub fn readIntBe(self: &Self, comptime T: type) !T { + pub fn readIntBe(self: *Self, comptime T: type) !T { return self.readInt(builtin.Endian.Big, T); } - pub fn readInt(self: &Self, endian: builtin.Endian, comptime T: type) !T { + pub fn readInt(self: *Self, endian: builtin.Endian, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); return mem.readInt(bytes, T, endian); } - pub fn readVarInt(self: &Self, endian: builtin.Endian, comptime T: type, size: usize) !T { + pub fn readVarInt(self: *Self, endian: builtin.Endian, comptime T: type, size: usize) !T { assert(size <= @sizeOf(T)); assert(size <= 8); var input_buf: [8]u8 = undefined; @@ -208,22 +208,22 @@ pub fn OutStream(comptime WriteError: type) type { const Self = this; pub const Error = WriteError; - writeFn: fn (self: &Self, bytes: []const u8) Error!void, + writeFn: fn (self: *Self, bytes: []const u8) Error!void, - pub fn print(self: &Self, comptime format: []const u8, args: ...) !void { + pub fn print(self: *Self, comptime format: []const u8, args: ...) !void { return std.fmt.format(self, Error, self.writeFn, format, args); } - pub fn write(self: &Self, bytes: []const u8) !void { + pub fn write(self: *Self, bytes: []const u8) !void { return self.writeFn(self, bytes); } - pub fn writeByte(self: &Self, byte: u8) !void { + pub fn writeByte(self: *Self, byte: u8) !void { const slice = (&byte)[0..1]; return self.writeFn(self, slice); } - pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void { + pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) !void { const slice = (&byte)[0..1]; var i: usize = 0; while (i < n) : (i += 1) { @@ -234,14 +234,14 @@ pub fn OutStream(comptime WriteError: type) type { } /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. -pub fn writeFile(allocator: &mem.Allocator, path: []const u8, data: []const u8) !void { +pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) !void { var file = try File.openWrite(allocator, path); defer file.close(); try file.write(data); } /// On success, caller owns returned buffer. -pub fn readFileAlloc(allocator: &mem.Allocator, path: []const u8) ![]u8 { +pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { var file = try File.openRead(allocator, path); defer file.close(); @@ -265,13 +265,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) pub stream: Stream, - unbuffered_in_stream: &Stream, + unbuffered_in_stream: *Stream, buffer: [buffer_size]u8, start_index: usize, end_index: usize, - pub fn init(unbuffered_in_stream: &Stream) Self { + pub fn init(unbuffered_in_stream: *Stream) Self { return Self{ .unbuffered_in_stream = unbuffered_in_stream, .buffer = undefined, @@ -287,7 +287,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) }; } - fn readFn(in_stream: &Stream, dest: []u8) !usize { + fn readFn(in_stream: *Stream, dest: []u8) !usize { const self = @fieldParentPtr(Self, "stream", in_stream); var dest_index: usize = 0; @@ -338,12 +338,12 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr pub stream: Stream, - unbuffered_out_stream: &Stream, + unbuffered_out_stream: *Stream, buffer: [buffer_size]u8, index: usize, - pub fn init(unbuffered_out_stream: &Stream) Self { + pub fn init(unbuffered_out_stream: *Stream) Self { return Self{ .unbuffered_out_stream = unbuffered_out_stream, .buffer = undefined, @@ -352,12 +352,12 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr }; } - pub fn flush(self: &Self) !void { + pub fn flush(self: *Self) !void { try self.unbuffered_out_stream.write(self.buffer[0..self.index]); self.index = 0; } - fn writeFn(out_stream: &Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) !void { const self = @fieldParentPtr(Self, "stream", out_stream); if (bytes.len >= self.buffer.len) { @@ -383,20 +383,20 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr /// Implementation of OutStream trait for Buffer pub const BufferOutStream = struct { - buffer: &Buffer, + buffer: *Buffer, stream: Stream, pub const Error = error{OutOfMemory}; pub const Stream = OutStream(Error); - pub fn init(buffer: &Buffer) BufferOutStream { + pub fn init(buffer: *Buffer) BufferOutStream { return BufferOutStream{ .buffer = buffer, .stream = Stream{ .writeFn = writeFn }, }; } - fn writeFn(out_stream: &Stream, bytes: []const u8) !void { + fn writeFn(out_stream: *Stream, bytes: []const u8) !void { const self = @fieldParentPtr(BufferOutStream, "stream", out_stream); return self.buffer.append(bytes); } @@ -407,7 +407,7 @@ pub const BufferedAtomicFile = struct { file_stream: FileOutStream, buffered_stream: BufferedOutStream(FileOutStream.Error), - pub fn create(allocator: &mem.Allocator, dest_path: []const u8) !&BufferedAtomicFile { + pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile { // TODO with well defined copy elision we don't need this allocation var self = try allocator.create(BufferedAtomicFile); errdefer allocator.destroy(self); @@ -427,18 +427,18 @@ pub const BufferedAtomicFile = struct { } /// always call destroy, even after successful finish() - pub fn destroy(self: &BufferedAtomicFile) void { + pub fn destroy(self: *BufferedAtomicFile) void { const allocator = self.atomic_file.allocator; self.atomic_file.deinit(); allocator.destroy(self); } - pub fn finish(self: &BufferedAtomicFile) !void { + pub fn finish(self: *BufferedAtomicFile) !void { try self.buffered_stream.flush(); try self.atomic_file.finish(); } - pub fn stream(self: &BufferedAtomicFile) &OutStream(FileOutStream.Error) { + pub fn stream(self: *BufferedAtomicFile) *OutStream(FileOutStream.Error) { return &self.buffered_stream.stream; } }; diff --git a/std/json.zig b/std/json.zig index 9de8f0b53e..c8aef7688b 100644 --- a/std/json.zig +++ b/std/json.zig @@ -76,7 +76,7 @@ pub const Token = struct { } // Slice into the underlying input string. - pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 { + pub fn slice(self: *const Token, input: []const u8, i: usize) []const u8 { return input[i + self.offset - self.count .. i + self.offset]; } }; @@ -115,7 +115,7 @@ pub const StreamingJsonParser = struct { return p; } - pub fn reset(p: &StreamingJsonParser) void { + pub fn reset(p: *StreamingJsonParser) void { p.state = State.TopLevelBegin; p.count = 0; // Set before ever read in main transition function @@ -205,7 +205,7 @@ pub const StreamingJsonParser = struct { // tokens. token2 is always null if token1 is null. // // There is currently no error recovery on a bad stream. - pub fn feed(p: &StreamingJsonParser, c: u8, token1: &?Token, token2: &?Token) Error!void { + pub fn feed(p: *StreamingJsonParser, c: u8, token1: *?Token, token2: *?Token) Error!void { token1.* = null; token2.* = null; p.count += 1; @@ -217,7 +217,7 @@ pub const StreamingJsonParser = struct { } // Perform a single transition on the state machine and return any possible token. - fn transition(p: &StreamingJsonParser, c: u8, token: &?Token) Error!bool { + fn transition(p: *StreamingJsonParser, c: u8, token: *?Token) Error!bool { switch (p.state) { State.TopLevelBegin => switch (c) { '{' => { @@ -861,7 +861,7 @@ pub fn validate(s: []const u8) bool { var token1: ?Token = undefined; var token2: ?Token = undefined; - p.feed(c, &token1, &token2) catch |err| { + p.feed(c, *token1, *token2) catch |err| { return false; }; } @@ -878,7 +878,7 @@ pub const ValueTree = struct { arena: ArenaAllocator, root: Value, - pub fn deinit(self: &ValueTree) void { + pub fn deinit(self: *ValueTree) void { self.arena.deinit(); } }; @@ -894,7 +894,7 @@ pub const Value = union(enum) { Array: ArrayList(Value), Object: ObjectMap, - pub fn dump(self: &const Value) void { + pub fn dump(self: *const Value) void { switch (self.*) { Value.Null => { std.debug.warn("null"); @@ -941,7 +941,7 @@ pub const Value = union(enum) { } } - pub fn dumpIndent(self: &const Value, indent: usize) void { + pub fn dumpIndent(self: *const Value, indent: usize) void { if (indent == 0) { self.dump(); } else { @@ -949,7 +949,7 @@ pub const Value = union(enum) { } } - fn dumpIndentLevel(self: &const Value, indent: usize, level: usize) void { + fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void { switch (self.*) { Value.Null => { std.debug.warn("null"); @@ -1013,7 +1013,7 @@ pub const Value = union(enum) { // A non-stream JSON parser which constructs a tree of Value's. pub const JsonParser = struct { - allocator: &Allocator, + allocator: *Allocator, state: State, copy_strings: bool, // Stores parent nodes and un-combined Values. @@ -1026,7 +1026,7 @@ pub const JsonParser = struct { Simple, }; - pub fn init(allocator: &Allocator, copy_strings: bool) JsonParser { + pub fn init(allocator: *Allocator, copy_strings: bool) JsonParser { return JsonParser{ .allocator = allocator, .state = State.Simple, @@ -1035,16 +1035,16 @@ pub const JsonParser = struct { }; } - pub fn deinit(p: &JsonParser) void { + pub fn deinit(p: *JsonParser) void { p.stack.deinit(); } - pub fn reset(p: &JsonParser) void { + pub fn reset(p: *JsonParser) void { p.state = State.Simple; p.stack.shrink(0); } - pub fn parse(p: &JsonParser, input: []const u8) !ValueTree { + pub fn parse(p: *JsonParser, input: []const u8) !ValueTree { var mp = StreamingJsonParser.init(); var arena = ArenaAllocator.init(p.allocator); @@ -1090,7 +1090,7 @@ pub const JsonParser = struct { // Even though p.allocator exists, we take an explicit allocator so that allocation state // can be cleaned up on error correctly during a `parse` on call. - fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void { + fn transition(p: *JsonParser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { switch (p.state) { State.ObjectKey => switch (token.id) { Token.Id.ObjectEnd => { @@ -1223,7 +1223,7 @@ pub const JsonParser = struct { } } - fn pushToParent(p: &JsonParser, value: &const Value) !void { + fn pushToParent(p: *JsonParser, value: *const Value) !void { switch (p.stack.at(p.stack.len - 1)) { // Object Parent -> [ ..., object, , value ] Value.String => |key| { @@ -1244,14 +1244,14 @@ pub const JsonParser = struct { } } - fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value { + fn parseString(p: *JsonParser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { // TODO: We don't strictly have to copy values which do not contain any escape // characters if flagged with the option. const slice = token.slice(input, i); return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; } - fn parseNumber(p: &JsonParser, token: &const Token, input: []const u8, i: usize) !Value { + fn parseNumber(p: *JsonParser, token: *const Token, input: []const u8, i: usize) !Value { return if (token.number_is_integer) Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else diff --git a/std/linked_list.zig b/std/linked_list.zig index c6be08171e..fbc0a0c42a 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -21,11 +21,11 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Node inside the linked list wrapping the actual data. pub const Node = struct { - prev: ?&Node, - next: ?&Node, + prev: ?*Node, + next: ?*Node, data: T, - pub fn init(value: &const T) Node { + pub fn init(value: *const T) Node { return Node{ .prev = null, .next = null, @@ -38,14 +38,14 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na return Node.init({}); } - pub fn toData(node: &Node) &ParentType { + pub fn toData(node: *Node) *ParentType { comptime assert(isIntrusive()); return @fieldParentPtr(ParentType, field_name, node); } }; - first: ?&Node, - last: ?&Node, + first: ?*Node, + last: ?*Node, len: usize, /// Initialize a linked list. @@ -69,7 +69,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Arguments: /// node: Pointer to a node in the list. /// new_node: Pointer to the new node to insert. - pub fn insertAfter(list: &Self, node: &Node, new_node: &Node) void { + pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void { new_node.prev = node; if (node.next) |next_node| { // Intermediate node. @@ -90,7 +90,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Arguments: /// node: Pointer to a node in the list. /// new_node: Pointer to the new node to insert. - pub fn insertBefore(list: &Self, node: &Node, new_node: &Node) void { + pub fn insertBefore(list: *Self, node: *Node, new_node: *Node) void { new_node.next = node; if (node.prev) |prev_node| { // Intermediate node. @@ -110,7 +110,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Arguments: /// new_node: Pointer to the new node to insert. - pub fn append(list: &Self, new_node: &Node) void { + pub fn append(list: *Self, new_node: *Node) void { if (list.last) |last| { // Insert after last. list.insertAfter(last, new_node); @@ -124,7 +124,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Arguments: /// new_node: Pointer to the new node to insert. - pub fn prepend(list: &Self, new_node: &Node) void { + pub fn prepend(list: *Self, new_node: *Node) void { if (list.first) |first| { // Insert before first. list.insertBefore(first, new_node); @@ -143,7 +143,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Arguments: /// node: Pointer to the node to be removed. - pub fn remove(list: &Self, node: &Node) void { + pub fn remove(list: *Self, node: *Node) void { if (node.prev) |prev_node| { // Intermediate node. prev_node.next = node.next; @@ -168,7 +168,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the last node in the list. - pub fn pop(list: &Self) ?&Node { + pub fn pop(list: *Self) ?*Node { const last = list.last ?? return null; list.remove(last); return last; @@ -178,7 +178,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the first node in the list. - pub fn popFirst(list: &Self) ?&Node { + pub fn popFirst(list: *Self) ?*Node { const first = list.first ?? return null; list.remove(first); return first; @@ -191,7 +191,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the new node. - pub fn allocateNode(list: &Self, allocator: &Allocator) !&Node { + pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); return allocator.create(Node); } @@ -201,7 +201,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Arguments: /// node: Pointer to the node to deallocate. /// allocator: Dynamic memory allocator. - pub fn destroyNode(list: &Self, node: &Node, allocator: &Allocator) void { + pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void { comptime assert(!isIntrusive()); allocator.destroy(node); } @@ -214,7 +214,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the new node. - pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) !&Node { + pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node { comptime assert(!isIntrusive()); var node = try list.allocateNode(allocator); node.* = Node.init(data); diff --git a/std/macho.zig b/std/macho.zig index 615569e4b4..e71ac76b1a 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -42,13 +42,13 @@ pub const Symbol = struct { name: []const u8, address: u64, - fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool { + fn addressLessThan(lhs: *const Symbol, rhs: *const Symbol) bool { return lhs.address < rhs.address; } }; pub const SymbolTable = struct { - allocator: &mem.Allocator, + allocator: *mem.Allocator, symbols: []const Symbol, strings: []const u8, @@ -56,7 +56,7 @@ pub const SymbolTable = struct { // Ideally we'd use _mh_execute_header because it's always at 0x100000000 // in the image but as it's located in a different section than executable // code, its displacement is different. - pub fn deinit(self: &SymbolTable) void { + pub fn deinit(self: *SymbolTable) void { self.allocator.free(self.symbols); self.symbols = []const Symbol{}; @@ -64,7 +64,7 @@ pub const SymbolTable = struct { self.strings = []const u8{}; } - pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol { + pub fn search(self: *const SymbolTable, address: usize) ?*const Symbol { var min: usize = 0; var max: usize = self.symbols.len - 1; // Exclude sentinel. while (min < max) { @@ -83,7 +83,7 @@ pub const SymbolTable = struct { } }; -pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable { +pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable { var file = in.file; try file.seekTo(0); @@ -160,13 +160,13 @@ pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable }; } -fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void { +fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void { return in.stream.readNoEof(([]u8)(result)); } -fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void { +fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void { return readNoEof(in, T, result[0..1]); } -fn isSymbol(sym: &const Nlist64) bool { +fn isSymbol(sym: *const Nlist64) bool { return sym.n_value != 0 and sym.n_desc == 0; } diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index b7bbf930eb..9bfe5fe724 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -29,7 +29,7 @@ fn redupif32(x: f32) f32 { return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan32(z: &const Complex(f32)) Complex(f32) { +fn atan32(z: *const Complex(f32)) Complex(f32) { const maxnum = 1.0e38; const x = z.re; @@ -78,7 +78,7 @@ fn redupif64(x: f64) f64 { return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan64(z: &const Complex(f64)) Complex(f64) { +fn atan64(z: *const Complex(f64)) Complex(f64) { const maxnum = 1.0e308; const x = z.re; diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index 96eac68556..c2f9a47b8d 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -15,7 +15,7 @@ pub fn cosh(z: var) Complex(@typeOf(z.re)) { }; } -fn cosh32(z: &const Complex(f32)) Complex(f32) { +fn cosh32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -78,7 +78,7 @@ fn cosh32(z: &const Complex(f32)) Complex(f32) { return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); } -fn cosh64(z: &const Complex(f64)) Complex(f64) { +fn cosh64(z: *const Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 8fe069a43d..44c354f246 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -16,7 +16,7 @@ pub fn exp(z: var) Complex(@typeOf(z.re)) { }; } -fn exp32(z: &const Complex(f32)) Complex(f32) { +fn exp32(z: *const Complex(f32)) Complex(f32) { @setFloatMode(this, @import("builtin").FloatMode.Strict); const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 @@ -63,7 +63,7 @@ fn exp32(z: &const Complex(f32)) Complex(f32) { } } -fn exp64(z: &const Complex(f64)) Complex(f64) { +fn exp64(z: *const Complex(f64)) Complex(f64) { const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig index 5902ffaa19..b00296beda 100644 --- a/std/math/complex/index.zig +++ b/std/math/complex/index.zig @@ -37,28 +37,28 @@ pub fn Complex(comptime T: type) type { }; } - pub fn add(self: &const Self, other: &const Self) Self { + pub fn add(self: *const Self, other: *const Self) Self { return Self{ .re = self.re + other.re, .im = self.im + other.im, }; } - pub fn sub(self: &const Self, other: &const Self) Self { + pub fn sub(self: *const Self, other: *const Self) Self { return Self{ .re = self.re - other.re, .im = self.im - other.im, }; } - pub fn mul(self: &const Self, other: &const Self) Self { + pub fn mul(self: *const Self, other: *const Self) Self { return Self{ .re = self.re * other.re - self.im * other.im, .im = self.im * other.re + self.re * other.im, }; } - pub fn div(self: &const Self, other: &const Self) Self { + pub fn div(self: *const Self, other: *const Self) Self { const re_num = self.re * other.re + self.im * other.im; const im_num = self.im * other.re - self.re * other.im; const den = other.re * other.re + other.im * other.im; @@ -69,14 +69,14 @@ pub fn Complex(comptime T: type) type { }; } - pub fn conjugate(self: &const Self) Self { + pub fn conjugate(self: *const Self) Self { return Self{ .re = self.re, .im = -self.im, }; } - pub fn reciprocal(self: &const Self) Self { + pub fn reciprocal(self: *const Self) Self { const m = self.re * self.re + self.im * self.im; return Self{ .re = self.re / m, @@ -84,7 +84,7 @@ pub fn Complex(comptime T: type) type { }; } - pub fn magnitude(self: &const Self) T { + pub fn magnitude(self: *const Self) T { return math.sqrt(self.re * self.re + self.im * self.im); } }; diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index 7ebefff40c..a56c2ef2eb 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -14,7 +14,7 @@ pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { }; } -fn frexp_exp32(x: f32, expt: &i32) f32 { +fn frexp_exp32(x: f32, expt: *i32) f32 { const k = 235; // reduction constant const kln2 = 162.88958740; // k * ln2 @@ -24,7 +24,7 @@ fn frexp_exp32(x: f32, expt: &i32) f32 { return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); } -fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { +fn ldexp_cexp32(z: *const Complex(f32), expt: i32) Complex(f32) { var ex_expt: i32 = undefined; const exp_x = frexp_exp32(z.re, &ex_expt); const exptf = expt + ex_expt; @@ -38,7 +38,7 @@ fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) { return Complex(f32).new(math.cos(z.im) * exp_x * scale1 * scale2, math.sin(z.im) * exp_x * scale1 * scale2); } -fn frexp_exp64(x: f64, expt: &i32) f64 { +fn frexp_exp64(x: f64, expt: *i32) f64 { const k = 1799; // reduction constant const kln2 = 1246.97177782734161156; // k * ln2 @@ -54,7 +54,7 @@ fn frexp_exp64(x: f64, expt: &i32) f64 { return @bitCast(f64, (u64(high_word) << 32) | lx); } -fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) { +fn ldexp_cexp64(z: *const Complex(f64), expt: i32) Complex(f64) { var ex_expt: i32 = undefined; const exp_x = frexp_exp64(z.re, &ex_expt); const exptf = i64(expt + ex_expt); diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig index bef9fde542..4c2cd9cf34 100644 --- a/std/math/complex/pow.zig +++ b/std/math/complex/pow.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn pow(comptime T: type, z: &const T, c: &const T) T { +pub fn pow(comptime T: type, z: *const T, c: *const T) T { const p = cmath.log(z); const q = c.mul(p); return cmath.exp(q); diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 09a62ca058..3d196bfd50 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -15,7 +15,7 @@ pub fn sinh(z: var) Complex(@typeOf(z.re)) { }; } -fn sinh32(z: &const Complex(f32)) Complex(f32) { +fn sinh32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -78,7 +78,7 @@ fn sinh32(z: &const Complex(f32)) Complex(f32) { return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); } -fn sinh64(z: &const Complex(f64)) Complex(f64) { +fn sinh64(z: *const Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index afda69f7c9..d4f5a67528 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -15,7 +15,7 @@ pub fn sqrt(z: var) Complex(@typeOf(z.re)) { }; } -fn sqrt32(z: &const Complex(f32)) Complex(f32) { +fn sqrt32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -57,7 +57,7 @@ fn sqrt32(z: &const Complex(f32)) Complex(f32) { } } -fn sqrt64(z: &const Complex(f64)) Complex(f64) { +fn sqrt64(z: *const Complex(f64)) Complex(f64) { // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2)) const threshold = 0x1.a827999fcef32p+1022; diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index 34250b1b4a..1d754838a3 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -13,7 +13,7 @@ pub fn tanh(z: var) Complex(@typeOf(z.re)) { }; } -fn tanh32(z: &const Complex(f32)) Complex(f32) { +fn tanh32(z: *const Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -51,7 +51,7 @@ fn tanh32(z: &const Complex(f32)) Complex(f32) { return Complex(f32).new((beta * rho * s) / den, t / den); } -fn tanh64(z: &const Complex(f64)) Complex(f64) { +fn tanh64(z: *const Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; diff --git a/std/math/hypot.zig b/std/math/hypot.zig index fe0de3a1ea..494df22ba6 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -52,7 +52,7 @@ fn hypot32(x: f32, y: f32) f32 { return z * math.sqrt(f32(f64(x) * x + f64(y) * y)); } -fn sq(hi: &f64, lo: &f64, x: f64) void { +fn sq(hi: *f64, lo: *f64, x: f64) void { const split: f64 = 0x1.0p27 + 1.0; const xc = x * split; const xh = x - xc + xc; diff --git a/std/math/index.zig b/std/math/index.zig index 847e972500..33bc1082f7 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -46,12 +46,12 @@ pub fn forceEval(value: var) void { switch (T) { f32 => { var x: f32 = undefined; - const p = @ptrCast(&volatile f32, &x); + const p = @ptrCast(*volatile f32, &x); p.* = x; }, f64 => { var x: f64 = undefined; - const p = @ptrCast(&volatile f64, &x); + const p = @ptrCast(*volatile f64, &x); p.* = x; }, else => { diff --git a/std/mem.zig b/std/mem.zig index f4696cff9f..aec24e8491 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -13,7 +13,7 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, + allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -26,22 +26,22 @@ pub const Allocator = struct { /// The returned newly allocated memory is undefined. /// `alignment` is guaranteed to be >= 1 /// `alignment` is guaranteed to be a power of 2 - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, + reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` - freeFn: fn (self: &Allocator, old_mem: []u8) void, + freeFn: fn (self: *Allocator, old_mem: []u8) void, - fn create(self: &Allocator, comptime T: type) !&T { - if (@sizeOf(T) == 0) return &{}; + fn create(self: *Allocator, comptime T: type) !*T { + if (@sizeOf(T) == 0) return *{}; const slice = try self.alloc(T, 1); return &slice[0]; } // TODO once #733 is solved, this will replace create - fn construct(self: &Allocator, init: var) t: { + fn construct(self: *Allocator, init: var) t: { // TODO this is a workaround for type getting parsed as Error!&const T const T = @typeOf(init).Child; - break :t Error!&T; + break :t Error!*T; } { const T = @typeOf(init).Child; if (@sizeOf(T) == 0) return &{}; @@ -51,17 +51,17 @@ pub const Allocator = struct { return ptr; } - fn destroy(self: &Allocator, ptr: var) void { + fn destroy(self: *Allocator, ptr: var) void { self.free(ptr[0..1]); } - fn alloc(self: &Allocator, comptime T: type, n: usize) ![]T { + fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { return self.alignedAlloc(T, @alignOf(T), n); } - fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { + fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { if (n == 0) { - return (&align(alignment) T)(undefined)[0..0]; + return (*align(alignment) T)(undefined)[0..0]; } const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); @@ -73,17 +73,17 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { + fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { + fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); } if (n == 0) { self.free(old_mem); - return (&align(alignment) T)(undefined)[0..0]; + return (*align(alignment) T)(undefined)[0..0]; } const old_byte_slice = ([]u8)(old_mem); @@ -102,11 +102,11 @@ pub const Allocator = struct { /// Reallocate, but `n` must be less than or equal to `old_mem.len`. /// Unlike `realloc`, this function cannot fail. /// Shrinking to 0 is the same as calling `free`. - fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) []T { + fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { + fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { if (n == 0) { self.free(old_mem); return old_mem[0..0]; @@ -123,10 +123,10 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn free(self: &Allocator, memory: var) void { + fn free(self: *Allocator, memory: var) void { const bytes = ([]const u8)(memory); if (bytes.len == 0) return; - const non_const_ptr = @intToPtr(&u8, @ptrToInt(bytes.ptr)); + const non_const_ptr = @intToPtr(*u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } }; @@ -186,7 +186,7 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { } /// Copies ::m to newly allocated memory. Caller is responsible to free it. -pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { +pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T { const new_buf = try allocator.alloc(T, m.len); copy(T, new_buf, m); return new_buf; @@ -457,7 +457,7 @@ pub const SplitIterator = struct { split_bytes: []const u8, index: usize, - pub fn next(self: &SplitIterator) ?[]const u8 { + pub fn next(self: *SplitIterator) ?[]const u8 { // move to beginning of token while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} const start = self.index; @@ -473,14 +473,14 @@ pub const SplitIterator = struct { } /// Returns a slice of the remaining bytes. Does not affect iterator state. - pub fn rest(self: &const SplitIterator) []const u8 { + pub fn rest(self: *const SplitIterator) []const u8 { // move to beginning of token var index: usize = self.index; while (index < self.buffer.len and self.isSplitByte(self.buffer[index])) : (index += 1) {} return self.buffer[index..]; } - fn isSplitByte(self: &const SplitIterator, byte: u8) bool { + fn isSplitByte(self: *const SplitIterator, byte: u8) bool { for (self.split_bytes) |split_byte| { if (byte == split_byte) { return true; @@ -492,7 +492,7 @@ pub const SplitIterator = struct { /// Naively combines a series of strings with a separator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: &Allocator, sep: u8, strings: ...) ![]u8 { +pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { comptime assert(strings.len >= 1); var total_strings_len: usize = strings.len; // 1 sep per string { @@ -649,7 +649,7 @@ test "mem.max" { assert(max(u8, "abcdefg") == 'g'); } -pub fn swap(comptime T: type, a: &T, b: &T) void { +pub fn swap(comptime T: type, a: *T, b: *T) void { const tmp = a.*; a.* = b.*; b.* = tmp; diff --git a/std/net.zig b/std/net.zig index 3af4e0b525..bfe4b1c2a0 100644 --- a/std/net.zig +++ b/std/net.zig @@ -31,7 +31,7 @@ pub const Address = struct { }; } - pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address { + pub fn initIp6(ip6: *const Ip6Addr, port: u16) Address { return Address{ .family = posix.AF_INET6, .os_addr = posix.sockaddr{ @@ -46,15 +46,15 @@ pub const Address = struct { }; } - pub fn initPosix(addr: &const posix.sockaddr) Address { + pub fn initPosix(addr: *const posix.sockaddr) Address { return Address{ .os_addr = addr.* }; } - pub fn format(self: &const Address, out_stream: var) !void { + pub fn format(self: *const Address, out_stream: var) !void { switch (self.os_addr.in.family) { posix.AF_INET => { const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port); - const bytes = ([]const u8)((&self.os_addr.in.addr)[0..1]); + const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); }, posix.AF_INET6 => { diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 51f1bd96e5..30a2fd1408 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -20,7 +20,7 @@ pub const ChildProcess = struct { pub handle: if (is_windows) windows.HANDLE else void, pub thread_handle: if (is_windows) windows.HANDLE else void, - pub allocator: &mem.Allocator, + pub allocator: *mem.Allocator, pub stdin: ?os.File, pub stdout: ?os.File, @@ -31,7 +31,7 @@ pub const ChildProcess = struct { pub argv: []const []const u8, /// Leave as null to use the current env map using the supplied allocator. - pub env_map: ?&const BufMap, + pub env_map: ?*const BufMap, pub stdin_behavior: StdIo, pub stdout_behavior: StdIo, @@ -47,7 +47,7 @@ pub const ChildProcess = struct { pub cwd: ?[]const u8, err_pipe: if (is_windows) void else [2]i32, - llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, + llnode: if (is_windows) void else LinkedList(*ChildProcess).Node, pub const SpawnError = error{ ProcessFdQuotaExceeded, @@ -84,7 +84,7 @@ pub const ChildProcess = struct { /// First argument in argv is the executable. /// On success must call deinit. - pub fn init(argv: []const []const u8, allocator: &mem.Allocator) !&ChildProcess { + pub fn init(argv: []const []const u8, allocator: *mem.Allocator) !*ChildProcess { const child = try allocator.create(ChildProcess); errdefer allocator.destroy(child); @@ -114,14 +114,14 @@ pub const ChildProcess = struct { return child; } - pub fn setUserName(self: &ChildProcess, name: []const u8) !void { + pub fn setUserName(self: *ChildProcess, name: []const u8) !void { const user_info = try os.getUserInfo(name); self.uid = user_info.uid; self.gid = user_info.gid; } /// On success must call `kill` or `wait`. - pub fn spawn(self: &ChildProcess) !void { + pub fn spawn(self: *ChildProcess) !void { if (is_windows) { return self.spawnWindows(); } else { @@ -129,13 +129,13 @@ pub const ChildProcess = struct { } } - pub fn spawnAndWait(self: &ChildProcess) !Term { + pub fn spawnAndWait(self: *ChildProcess) !Term { try self.spawn(); return self.wait(); } /// Forcibly terminates child process and then cleans up all resources. - pub fn kill(self: &ChildProcess) !Term { + pub fn kill(self: *ChildProcess) !Term { if (is_windows) { return self.killWindows(1); } else { @@ -143,7 +143,7 @@ pub const ChildProcess = struct { } } - pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) !Term { + pub fn killWindows(self: *ChildProcess, exit_code: windows.UINT) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -159,7 +159,7 @@ pub const ChildProcess = struct { return ??self.term; } - pub fn killPosix(self: &ChildProcess) !Term { + pub fn killPosix(self: *ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -179,7 +179,7 @@ pub const ChildProcess = struct { } /// Blocks until child process terminates and then cleans up all resources. - pub fn wait(self: &ChildProcess) !Term { + pub fn wait(self: *ChildProcess) !Term { if (is_windows) { return self.waitWindows(); } else { @@ -195,7 +195,7 @@ pub const ChildProcess = struct { /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. - pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?&const BufMap, max_output_size: usize) !ExecResult { + pub fn exec(allocator: *mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const BufMap, max_output_size: usize) !ExecResult { const child = try ChildProcess.init(argv, allocator); defer child.deinit(); @@ -225,7 +225,7 @@ pub const ChildProcess = struct { }; } - fn waitWindows(self: &ChildProcess) !Term { + fn waitWindows(self: *ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -235,7 +235,7 @@ pub const ChildProcess = struct { return ??self.term; } - fn waitPosix(self: &ChildProcess) !Term { + fn waitPosix(self: *ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -245,16 +245,16 @@ pub const ChildProcess = struct { return ??self.term; } - pub fn deinit(self: &ChildProcess) void { + pub fn deinit(self: *ChildProcess) void { self.allocator.destroy(self); } - fn waitUnwrappedWindows(self: &ChildProcess) !void { + fn waitUnwrappedWindows(self: *ChildProcess) !void { const result = os.windowsWaitSingle(self.handle, windows.INFINITE); self.term = (SpawnError!Term)(x: { var exit_code: windows.DWORD = undefined; - if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { + if (windows.GetExitCodeProcess(self.handle, *exit_code) == 0) { break :x Term{ .Unknown = 0 }; } else { break :x Term{ .Exited = @bitCast(i32, exit_code) }; @@ -267,7 +267,7 @@ pub const ChildProcess = struct { return result; } - fn waitUnwrapped(self: &ChildProcess) void { + fn waitUnwrapped(self: *ChildProcess) void { var status: i32 = undefined; while (true) { const err = posix.getErrno(posix.waitpid(self.pid, &status, 0)); @@ -283,11 +283,11 @@ pub const ChildProcess = struct { } } - fn handleWaitResult(self: &ChildProcess, status: i32) void { + fn handleWaitResult(self: *ChildProcess, status: i32) void { self.term = self.cleanupAfterWait(status); } - fn cleanupStreams(self: &ChildProcess) void { + fn cleanupStreams(self: *ChildProcess) void { if (self.stdin) |*stdin| { stdin.close(); self.stdin = null; @@ -302,7 +302,7 @@ pub const ChildProcess = struct { } } - fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term { + fn cleanupAfterWait(self: *ChildProcess, status: i32) !Term { defer { os.close(self.err_pipe[0]); os.close(self.err_pipe[1]); @@ -335,7 +335,7 @@ pub const ChildProcess = struct { Term{ .Unknown = status }; } - fn spawnPosix(self: &ChildProcess) !void { + fn spawnPosix(self: *ChildProcess) !void { const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try makePipe() else undefined; errdefer if (self.stdin_behavior == StdIo.Pipe) { destroyPipe(stdin_pipe); @@ -432,7 +432,7 @@ pub const ChildProcess = struct { self.pid = pid; self.err_pipe = err_pipe; - self.llnode = LinkedList(&ChildProcess).Node.init(self); + self.llnode = LinkedList(*ChildProcess).Node.init(self); self.term = null; if (self.stdin_behavior == StdIo.Pipe) { @@ -446,7 +446,7 @@ pub const ChildProcess = struct { } } - fn spawnWindows(self: &ChildProcess) !void { + fn spawnWindows(self: *ChildProcess) !void { const saAttr = windows.SECURITY_ATTRIBUTES{ .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, @@ -639,8 +639,8 @@ pub const ChildProcess = struct { } }; -fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8, lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void { - if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { +fn windowsCreateProcess(app_name: *u8, cmd_line: *u8, envp_ptr: ?*u8, cwd_ptr: ?*u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { + if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, @@ -653,7 +653,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ? /// Caller must dealloc. /// Guarantees a null byte at result[result.len]. -fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) ![]u8 { +fn windowsCreateCommandLine(allocator: *mem.Allocator, argv: []const []const u8) ![]u8 { var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -698,7 +698,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { // a namespace field lookup const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; -fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipe(rd: *windows.HANDLE, wr: *windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { if (windows.CreatePipe(rd, wr, sattr, 0) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -716,7 +716,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D } } -fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; try windowsMakePipe(&rd_h, &wr_h, sattr); @@ -726,7 +726,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S wr.* = wr_h; } -fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { +fn windowsMakePipeOut(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; try windowsMakePipe(&rd_h, &wr_h, sattr); @@ -748,7 +748,7 @@ fn makePipe() ![2]i32 { return fds; } -fn destroyPipe(pipe: &const [2]i32) void { +fn destroyPipe(pipe: *const [2]i32) void { os.close((pipe.*)[0]); os.close((pipe.*)[1]); } diff --git a/std/os/darwin.zig b/std/os/darwin.zig index a3fc230ac5..77e8b6bb6a 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -309,7 +309,7 @@ pub fn isatty(fd: i32) bool { return c.isatty(fd) != 0; } -pub fn fstat(fd: i32, buf: &c.Stat) usize { +pub fn fstat(fd: i32, buf: *c.Stat) usize { return errnoWrap(c.@"fstat$INODE64"(fd, buf)); } @@ -317,7 +317,7 @@ pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { return errnoWrap(c.lseek(fd, offset, whence)); } -pub fn open(path: &const u8, flags: u32, mode: usize) usize { +pub fn open(path: *const u8, flags: u32, mode: usize) usize { return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); } @@ -325,79 +325,79 @@ pub fn raise(sig: i32) usize { return errnoWrap(c.raise(sig)); } -pub fn read(fd: i32, buf: &u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(&c_void, buf), nbyte)); +pub fn read(fd: i32, buf: *u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); } -pub fn stat(noalias path: &const u8, noalias buf: &stat) usize { +pub fn stat(noalias path: *const u8, noalias buf: *stat) usize { return errnoWrap(c.stat(path, buf)); } -pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte)); +pub fn write(fd: i32, buf: *const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast(&c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); +pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(&c_void, address), length)); + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); } -pub fn unlink(path: &const u8) usize { +pub fn unlink(path: *const u8) usize { return errnoWrap(c.unlink(path)); } -pub fn getcwd(buf: &u8, size: usize) usize { +pub fn getcwd(buf: *u8, size: usize) usize { return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } -pub fn waitpid(pid: i32, status: &i32, options: u32) usize { +pub fn waitpid(pid: i32, status: *i32, options: u32) usize { comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(&c_int, status), @bitCast(c_int, options))); + return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); } pub fn fork() usize { return errnoWrap(c.fork()); } -pub fn access(path: &const u8, mode: u32) usize { +pub fn access(path: *const u8, mode: u32) usize { return errnoWrap(c.access(path, mode)); } -pub fn pipe(fds: &[2]i32) usize { +pub fn pipe(fds: *[2]i32) usize { comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe(@ptrCast(&c_int, fds))); + return errnoWrap(c.pipe(@ptrCast(*c_int, fds))); } -pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize { +pub fn getdirentries64(fd: i32, buf_ptr: *u8, buf_len: usize, basep: *i64) usize { return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); } -pub fn mkdir(path: &const u8, mode: u32) usize { +pub fn mkdir(path: *const u8, mode: u32) usize { return errnoWrap(c.mkdir(path, mode)); } -pub fn symlink(existing: &const u8, new: &const u8) usize { +pub fn symlink(existing: *const u8, new: *const u8) usize { return errnoWrap(c.symlink(existing, new)); } -pub fn rename(old: &const u8, new: &const u8) usize { +pub fn rename(old: *const u8, new: *const u8) usize { return errnoWrap(c.rename(old, new)); } -pub fn rmdir(path: &const u8) usize { +pub fn rmdir(path: *const u8) usize { return errnoWrap(c.rmdir(path)); } -pub fn chdir(path: &const u8) usize { +pub fn chdir(path: *const u8) usize { return errnoWrap(c.chdir(path)); } -pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize { +pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { return errnoWrap(c.execve(path, argv, envp)); } @@ -405,19 +405,19 @@ pub fn dup2(old: i32, new: i32) usize { return errnoWrap(c.dup2(old, new)); } -pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) usize { +pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } -pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize { +pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) usize { return errnoWrap(c.gettimeofday(tv, tz)); } -pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { return errnoWrap(c.nanosleep(req, rem)); } -pub fn realpath(noalias filename: &const u8, noalias resolved_name: &u8) usize { +pub fn realpath(noalias filename: *const u8, noalias resolved_name: *u8) usize { return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } @@ -429,11 +429,11 @@ pub fn setregid(rgid: u32, egid: u32) usize { return errnoWrap(c.setregid(rgid, egid)); } -pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize { +pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { return errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset)); } -pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize { +pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { assert(sig != SIGKILL); assert(sig != SIGSTOP); var cact = c.Sigaction{ @@ -442,7 +442,7 @@ pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigacti .sa_mask = act.mask, }; var coact: c.Sigaction = undefined; - const result = errnoWrap(c.sigaction(sig, &cact, &coact)); + const result = errnoWrap(c.sigaction(sig, *cact, *coact)); if (result != 0) { return result; } @@ -473,7 +473,7 @@ pub const Sigaction = struct { flags: u32, }; -pub fn sigaddset(set: &sigset_t, signo: u5) void { +pub fn sigaddset(set: *sigset_t, signo: u5) void { set.* |= u32(1) << (signo - 1); } diff --git a/std/os/file.zig b/std/os/file.zig index c07e2c5c8b..d943da30ca 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -19,7 +19,7 @@ pub const File = struct { /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. - pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File { + pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File { if (is_posix) { const flags = posix.O_LARGEFILE | posix.O_RDONLY; const fd = try os.posixOpen(allocator, path, flags, 0); @@ -40,7 +40,7 @@ pub const File = struct { } /// Calls `openWriteMode` with os.default_file_mode for the mode. - pub fn openWrite(allocator: &mem.Allocator, path: []const u8) OpenError!File { + pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File { return openWriteMode(allocator, path, os.default_file_mode); } @@ -48,7 +48,7 @@ pub const File = struct { /// If a file already exists in the destination it will be truncated. /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. - pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { + pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { if (is_posix) { const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; const fd = try os.posixOpen(allocator, path, flags, file_mode); @@ -72,7 +72,7 @@ pub const File = struct { /// If a file already exists in the destination this returns OpenError.PathAlreadyExists /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. /// Call close to clean up. - pub fn openWriteNoClobber(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { + pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { if (is_posix) { const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; const fd = try os.posixOpen(allocator, path, flags, file_mode); @@ -96,7 +96,7 @@ pub const File = struct { return File{ .handle = handle }; } - pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { + pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { const path_with_null = try std.cstr.addNullByte(allocator, path); defer allocator.free(path_with_null); @@ -140,17 +140,17 @@ pub const File = struct { /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. - pub fn close(self: &File) void { + pub fn close(self: *File) void { os.close(self.handle); self.handle = undefined; } /// Calls `os.isTty` on `self.handle`. - pub fn isTty(self: &File) bool { + pub fn isTty(self: *File) bool { return os.isTty(self.handle); } - pub fn seekForward(self: &File, amount: isize) !void { + pub fn seekForward(self: *File, amount: isize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); @@ -179,7 +179,7 @@ pub const File = struct { } } - pub fn seekTo(self: &File, pos: usize) !void { + pub fn seekTo(self: *File, pos: usize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const ipos = try math.cast(isize, pos); @@ -210,7 +210,7 @@ pub const File = struct { } } - pub fn getPos(self: &File) !usize { + pub fn getPos(self: *File) !usize { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); @@ -229,7 +229,7 @@ pub const File = struct { }, Os.windows => { var pos: windows.LARGE_INTEGER = undefined; - if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) { + if (windows.SetFilePointerEx(self.handle, 0, *pos, windows.FILE_CURRENT) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_PARAMETER => error.BadFd, @@ -250,7 +250,7 @@ pub const File = struct { } } - pub fn getEndPos(self: &File) !usize { + pub fn getEndPos(self: *File) !usize { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -285,7 +285,7 @@ pub const File = struct { Unexpected, }; - fn mode(self: &File) ModeError!os.FileMode { + fn mode(self: *File) ModeError!os.FileMode { if (is_posix) { var stat: posix.Stat = undefined; const err = posix.getErrno(posix.fstat(self.handle, &stat)); @@ -309,7 +309,7 @@ pub const File = struct { pub const ReadError = error{}; - pub fn read(self: &File, buffer: []u8) !usize { + pub fn read(self: *File, buffer: []u8) !usize { if (is_posix) { var index: usize = 0; while (index < buffer.len) { @@ -334,7 +334,7 @@ pub const File = struct { while (index < buffer.len) { const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { + if (windows.ReadFile(self.handle, @ptrCast(*c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.OPERATION_ABORTED => continue, @@ -353,7 +353,7 @@ pub const File = struct { pub const WriteError = os.WindowsWriteError || os.PosixWriteError; - fn write(self: &File, bytes: []const u8) WriteError!void { + fn write(self: *File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig index 2a15e1d495..c0c1b1cc4b 100644 --- a/std/os/get_user_id.zig +++ b/std/os/get_user_id.zig @@ -77,8 +77,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; - if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile; + if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile; }, }, State.ReadGroupId => switch (byte) { @@ -93,8 +93,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo { '0'...'9' => byte - '0', else => return error.CorruptPasswordFile, }; - if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile; - if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile; + if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile; + if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile; }, }, } diff --git a/std/os/index.zig b/std/os/index.zig index 70e654bcd9..ff638c670b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -321,14 +321,14 @@ pub const PosixOpenError = error{ /// ::file_path needs to be copied in memory to add a null terminating byte. /// Calls POSIX open, keeps trying if it gets interrupted, and translates /// the return value into zig errors. -pub fn posixOpen(allocator: &Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { +pub fn posixOpen(allocator: *Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { const path_with_null = try cstr.addNullByte(allocator, file_path); defer allocator.free(path_with_null); return posixOpenC(path_with_null.ptr, flags, perm); } -pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 { +pub fn posixOpenC(file_path: *const u8, flags: u32, perm: usize) !i32 { while (true) { const result = posix.open(file_path, flags, perm); const err = posix.getErrno(result); @@ -374,10 +374,10 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { } } -pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) ![]?&u8 { +pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?*u8 { const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?&u8, envp_count + 1); - mem.set(?&u8, envp_buf, null); + const envp_buf = try allocator.alloc(?*u8, envp_count + 1); + mem.set(?*u8, envp_buf, null); errdefer freeNullDelimitedEnvMap(allocator, envp_buf); { var it = env_map.iterator(); @@ -397,7 +397,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) return envp_buf; } -pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { +pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?*u8) void { for (envp_buf) |env| { const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; allocator.free(env_buf); @@ -410,9 +410,9 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { /// pointers after the args and after the environment variables. /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. -pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void { - const argv_buf = try allocator.alloc(?&u8, argv.len + 1); - mem.set(?&u8, argv_buf, null); +pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { + const argv_buf = try allocator.alloc(?*u8, argv.len + 1); + mem.set(?*u8, argv_buf, null); defer { for (argv_buf) |arg| { const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; @@ -494,10 +494,10 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { } pub var linux_aux_raw = []usize{0} ** 38; -pub var posix_environ_raw: []&u8 = undefined; +pub var posix_environ_raw: []*u8 = undefined; /// Caller must free result when done. -pub fn getEnvMap(allocator: &Allocator) !BufMap { +pub fn getEnvMap(allocator: *Allocator) !BufMap { var result = BufMap.init(allocator); errdefer result.deinit(); @@ -557,7 +557,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { } /// Caller must free returned memory. -pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) ![]u8 { +pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { if (is_windows) { const key_with_null = try cstr.addNullByte(allocator, key); defer allocator.free(key_with_null); @@ -591,7 +591,7 @@ pub fn getEnvVarOwned(allocator: &mem.Allocator, key: []const u8) ![]u8 { } /// Caller must free the returned memory. -pub fn getCwd(allocator: &Allocator) ![]u8 { +pub fn getCwd(allocator: *Allocator) ![]u8 { switch (builtin.os) { Os.windows => { var buf = try allocator.alloc(u8, 256); @@ -640,7 +640,7 @@ test "os.getCwd" { pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; -pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { +pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { if (is_windows) { return symLinkWindows(allocator, existing_path, new_path); } else { @@ -653,7 +653,7 @@ pub const WindowsSymLinkError = error{ Unexpected, }; -pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { +pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { const existing_with_null = try cstr.addNullByte(allocator, existing_path); defer allocator.free(existing_with_null); const new_with_null = try cstr.addNullByte(allocator, new_path); @@ -683,7 +683,7 @@ pub const PosixSymLinkError = error{ Unexpected, }; -pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { +pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2); defer allocator.free(full_buf); @@ -718,7 +718,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: // here we replace the standard +/ with -_ so that it can be used in a file name const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); -pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { if (symLink(allocator, existing_path, new_path)) { return; } else |err| switch (err) { @@ -746,7 +746,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: } } -pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { +pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void { if (builtin.os == Os.windows) { return deleteFileWindows(allocator, file_path); } else { @@ -754,7 +754,7 @@ pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { } } -pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { +pub fn deleteFileWindows(allocator: *Allocator, file_path: []const u8) !void { const buf = try allocator.alloc(u8, file_path.len + 1); defer allocator.free(buf); @@ -772,7 +772,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { } } -pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { +pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void { const buf = try allocator.alloc(u8, file_path.len + 1); defer allocator.free(buf); @@ -803,7 +803,7 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { /// there is a possibility of power loss or application termination leaving temporary files present /// in the same directory as dest_path. /// Destination file will have the same mode as the source file. -pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []const u8) !void { +pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void { var in_file = try os.File.openRead(allocator, source_path); defer in_file.close(); @@ -825,7 +825,7 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is /// merged and readily available, /// there is a possibility of power loss or application termination leaving temporary files present -pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { +pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { var in_file = try os.File.openRead(allocator, source_path); defer in_file.close(); @@ -843,7 +843,7 @@ pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: [ } pub const AtomicFile = struct { - allocator: &Allocator, + allocator: *Allocator, file: os.File, tmp_path: []u8, dest_path: []const u8, @@ -851,7 +851,7 @@ pub const AtomicFile = struct { /// dest_path must remain valid for the lifetime of AtomicFile /// call finish to atomically replace dest_path with contents - pub fn init(allocator: &Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile { + pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile { const dirname = os.path.dirname(dest_path); var rand_buf: [12]u8 = undefined; @@ -888,7 +888,7 @@ pub const AtomicFile = struct { } /// always call deinit, even after successful finish() - pub fn deinit(self: &AtomicFile) void { + pub fn deinit(self: *AtomicFile) void { if (!self.finished) { self.file.close(); deleteFile(self.allocator, self.tmp_path) catch {}; @@ -897,7 +897,7 @@ pub const AtomicFile = struct { } } - pub fn finish(self: &AtomicFile) !void { + pub fn finish(self: *AtomicFile) !void { assert(!self.finished); self.file.close(); try rename(self.allocator, self.tmp_path, self.dest_path); @@ -906,7 +906,7 @@ pub const AtomicFile = struct { } }; -pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) !void { +pub fn rename(allocator: *Allocator, old_path: []const u8, new_path: []const u8) !void { const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2); defer allocator.free(full_buf); @@ -951,7 +951,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) } } -pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void { +pub fn makeDir(allocator: *Allocator, dir_path: []const u8) !void { if (is_windows) { return makeDirWindows(allocator, dir_path); } else { @@ -959,7 +959,7 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) !void { } } -pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void { +pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try cstr.addNullByte(allocator, dir_path); defer allocator.free(path_buf); @@ -973,7 +973,7 @@ pub fn makeDirWindows(allocator: &Allocator, dir_path: []const u8) !void { } } -pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { +pub fn makeDirPosix(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try cstr.addNullByte(allocator, dir_path); defer allocator.free(path_buf); @@ -999,7 +999,7 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { /// Calls makeDir recursively to make an entire path. Returns success if the path /// already exists and is a directory. -pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { +pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { const resolved_path = try path.resolve(allocator, full_path); defer allocator.free(resolved_path); @@ -1033,7 +1033,7 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { /// Returns ::error.DirNotEmpty if the directory is not empty. /// To delete a directory recursively, see ::deleteTree -pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { +pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try allocator.alloc(u8, dir_path.len + 1); defer allocator.free(path_buf); @@ -1084,7 +1084,7 @@ const DeleteTreeError = error{ DirNotEmpty, Unexpected, }; -pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void { +pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { start_over: while (true) { var got_access_denied = false; // First, try deleting the item as a file. This way we don't follow sym links. @@ -1153,7 +1153,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! pub const Dir = struct { fd: i32, darwin_seek: darwin_seek_t, - allocator: &Allocator, + allocator: *Allocator, buf: []u8, index: usize, end_index: usize, @@ -1180,7 +1180,7 @@ pub const Dir = struct { }; }; - pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { + pub fn open(allocator: *Allocator, dir_path: []const u8) !Dir { const fd = switch (builtin.os) { Os.windows => @compileError("TODO support Dir.open for windows"), Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), @@ -1206,14 +1206,14 @@ pub const Dir = struct { }; } - pub fn close(self: &Dir) void { + pub fn close(self: *Dir) void { self.allocator.free(self.buf); os.close(self.fd); } /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to next, as well as when this ::Dir is deinitialized. - pub fn next(self: &Dir) !?Entry { + pub fn next(self: *Dir) !?Entry { switch (builtin.os) { Os.linux => return self.nextLinux(), Os.macosx, Os.ios => return self.nextDarwin(), @@ -1222,7 +1222,7 @@ pub const Dir = struct { } } - fn nextDarwin(self: &Dir) !?Entry { + fn nextDarwin(self: *Dir) !?Entry { start_over: while (true) { if (self.index >= self.end_index) { if (self.buf.len == 0) { @@ -1248,7 +1248,7 @@ pub const Dir = struct { break; } } - const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); + const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + darwin_entry.d_reclen; self.index = next_index; @@ -1277,11 +1277,11 @@ pub const Dir = struct { } } - fn nextWindows(self: &Dir) !?Entry { + fn nextWindows(self: *Dir) !?Entry { @compileError("TODO support Dir.next for windows"); } - fn nextLinux(self: &Dir) !?Entry { + fn nextLinux(self: *Dir) !?Entry { start_over: while (true) { if (self.index >= self.end_index) { if (self.buf.len == 0) { @@ -1307,7 +1307,7 @@ pub const Dir = struct { break; } } - const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); + const linux_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]); const next_index = self.index + linux_entry.d_reclen; self.index = next_index; @@ -1337,7 +1337,7 @@ pub const Dir = struct { } }; -pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void { +pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { const path_buf = try allocator.alloc(u8, dir_path.len + 1); defer allocator.free(path_buf); @@ -1361,7 +1361,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void { } /// Read value of a symbolic link. -pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { +pub fn readLink(allocator: *Allocator, pathname: []const u8) ![]u8 { const path_buf = try allocator.alloc(u8, pathname.len + 1); defer allocator.free(path_buf); @@ -1468,7 +1468,7 @@ pub const ArgIteratorPosix = struct { }; } - pub fn next(self: &ArgIteratorPosix) ?[]const u8 { + pub fn next(self: *ArgIteratorPosix) ?[]const u8 { if (self.index == self.count) return null; const s = raw[self.index]; @@ -1476,7 +1476,7 @@ pub const ArgIteratorPosix = struct { return cstr.toSlice(s); } - pub fn skip(self: &ArgIteratorPosix) bool { + pub fn skip(self: *ArgIteratorPosix) bool { if (self.index == self.count) return false; self.index += 1; @@ -1485,12 +1485,12 @@ pub const ArgIteratorPosix = struct { /// This is marked as public but actually it's only meant to be used /// internally by zig's startup code. - pub var raw: []&u8 = undefined; + pub var raw: []*u8 = undefined; }; pub const ArgIteratorWindows = struct { index: usize, - cmd_line: &const u8, + cmd_line: *const u8, in_quote: bool, quote_count: usize, seen_quote_count: usize, @@ -1501,7 +1501,7 @@ pub const ArgIteratorWindows = struct { return initWithCmdLine(windows.GetCommandLineA()); } - pub fn initWithCmdLine(cmd_line: &const u8) ArgIteratorWindows { + pub fn initWithCmdLine(cmd_line: *const u8) ArgIteratorWindows { return ArgIteratorWindows{ .index = 0, .cmd_line = cmd_line, @@ -1512,7 +1512,7 @@ pub const ArgIteratorWindows = struct { } /// You must free the returned memory when done. - pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(NextError![]u8) { + pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { // march forward over whitespace while (true) : (self.index += 1) { const byte = self.cmd_line[self.index]; @@ -1526,7 +1526,7 @@ pub const ArgIteratorWindows = struct { return self.internalNext(allocator); } - pub fn skip(self: &ArgIteratorWindows) bool { + pub fn skip(self: *ArgIteratorWindows) bool { // march forward over whitespace while (true) : (self.index += 1) { const byte = self.cmd_line[self.index]; @@ -1565,7 +1565,7 @@ pub const ArgIteratorWindows = struct { } } - fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) NextError![]u8 { + fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -1609,14 +1609,14 @@ pub const ArgIteratorWindows = struct { } } - fn emitBackslashes(self: &ArgIteratorWindows, buf: &Buffer, emit_count: usize) !void { + fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { var i: usize = 0; while (i < emit_count) : (i += 1) { try buf.appendByte('\\'); } } - fn countQuotes(cmd_line: &const u8) usize { + fn countQuotes(cmd_line: *const u8) usize { var result: usize = 0; var backslash_count: usize = 0; var index: usize = 0; @@ -1649,7 +1649,7 @@ pub const ArgIterator = struct { pub const NextError = ArgIteratorWindows.NextError; /// You must free the returned memory when done. - pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) { + pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { if (builtin.os == Os.windows) { return self.inner.next(allocator); } else { @@ -1658,13 +1658,13 @@ pub const ArgIterator = struct { } /// If you only are targeting posix you can call this and not need an allocator. - pub fn nextPosix(self: &ArgIterator) ?[]const u8 { + pub fn nextPosix(self: *ArgIterator) ?[]const u8 { return self.inner.next(); } /// Parse past 1 argument without capturing it. /// Returns `true` if skipped an arg, `false` if we are at the end. - pub fn skip(self: &ArgIterator) bool { + pub fn skip(self: *ArgIterator) bool { return self.inner.skip(); } }; @@ -1674,7 +1674,7 @@ pub fn args() ArgIterator { } /// Caller must call freeArgs on result. -pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 { +pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { // TODO refactor to only make 1 allocation. var it = args(); var contents = try Buffer.initSize(allocator, 0); @@ -1711,12 +1711,12 @@ pub fn argsAlloc(allocator: &mem.Allocator) ![]const []u8 { return result_slice_list; } -pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { +pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { var total_bytes: usize = 0; for (args_alloc) |arg| { total_bytes += @sizeOf([]u8) + arg.len; } - const unaligned_allocated_buf = @ptrCast(&const u8, args_alloc.ptr)[0..total_bytes]; + const unaligned_allocated_buf = @ptrCast(*const u8, args_alloc.ptr)[0..total_bytes]; const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); return allocator.free(aligned_allocated_buf); } @@ -1765,7 +1765,7 @@ test "windows arg parsing" { }); } -fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void { +fn testWindowsCmdLine(input_cmd_line: *const u8, expected_args: []const []const u8) void { var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { const arg = ??it.next(debug.global_allocator) catch unreachable; @@ -1832,7 +1832,7 @@ test "openSelfExe" { /// This function may return an error if the current executable /// was deleted after spawning. /// Caller owns returned memory. -pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { +pub fn selfExePath(allocator: *mem.Allocator) ![]u8 { switch (builtin.os) { Os.linux => { // If the currently executing binary has been deleted, @@ -1875,7 +1875,7 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { /// Get the directory path that contains the current executable. /// Caller owns returned memory. -pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 { +pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 { switch (builtin.os) { Os.linux => { // If the currently executing binary has been deleted, @@ -2001,7 +2001,7 @@ pub const PosixBindError = error{ }; /// addr is `&const T` where T is one of the sockaddr -pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void { +pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); switch (err) { @@ -2096,7 +2096,7 @@ pub const PosixAcceptError = error{ Unexpected, }; -pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 { +pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { while (true) { var sockaddr_size = u32(@sizeOf(posix.sockaddr)); const rc = posix.accept4(fd, addr, &sockaddr_size, flags); @@ -2195,7 +2195,7 @@ pub const LinuxEpollCtlError = error{ Unexpected, }; -pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void { +pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { const rc = posix.epoll_ctl(epfd, op, fd, event); const err = posix.getErrno(rc); switch (err) { @@ -2288,7 +2288,7 @@ pub const PosixConnectError = error{ Unexpected, }; -pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void { +pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { while (true) { const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); @@ -2319,7 +2319,7 @@ pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectEr /// Same as posixConnect except it is for blocking socket file descriptors. /// It expects to receive EINPROGRESS. -pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void { +pub fn posixConnectAsync(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { while (true) { const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); const err = posix.getErrno(rc); @@ -2350,7 +2350,7 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { var err_code: i32 = undefined; var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size); + const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(*u8, &err_code), &size); assert(size == 4); const err = posix.getErrno(rc); switch (err) { @@ -2401,13 +2401,13 @@ pub const Thread = struct { }, builtin.Os.windows => struct { handle: windows.HANDLE, - alloc_start: &c_void, + alloc_start: *c_void, heap_handle: windows.HANDLE, }, else => @compileError("Unsupported OS"), }; - pub fn wait(self: &const Thread) void { + pub fn wait(self: *const Thread) void { if (use_pthreads) { const err = c.pthread_join(self.data.handle, null); switch (err) { @@ -2473,7 +2473,7 @@ pub const SpawnThreadError = error{ /// fn startFn(@typeOf(context)) T /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread -pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread { +pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/ziglang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; @@ -2491,7 +2491,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread if (@sizeOf(Context) == 0) { return startFn({}); } else { - return startFn(@ptrCast(&Context, @alignCast(@alignOf(Context), arg)).*); + return startFn(@ptrCast(*Context, @alignCast(@alignOf(Context), arg)).*); } } }; @@ -2500,13 +2500,13 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); - const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; + const bytes = @ptrCast(*u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; outer_context.inner = context; outer_context.thread.data.heap_handle = heap_handle; outer_context.thread.data.alloc_start = bytes_ptr; - const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); + const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { const err = windows.GetLastError(); return switch (err) { @@ -2521,15 +2521,15 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread if (@sizeOf(Context) == 0) { return startFn({}); } else { - return startFn(@intToPtr(&const Context, ctx_addr).*); + return startFn(@intToPtr(*const Context, ctx_addr).*); } } - extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { + extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { if (@sizeOf(Context) == 0) { _ = startFn({}); return null; } else { - _ = startFn(@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)).*); + _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); return null; } } @@ -2548,7 +2548,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread stack_end -= @sizeOf(Context); stack_end -= stack_end % @alignOf(Context); assert(stack_end >= stack_addr); - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end)); context_ptr.* = context; arg = stack_end; } @@ -2556,7 +2556,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread stack_end -= @sizeOf(Thread); stack_end -= stack_end % @alignOf(Thread); assert(stack_end >= stack_addr); - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end)); thread_ptr.data.stack_addr = stack_addr; thread_ptr.data.stack_len = mmap_len; @@ -2572,9 +2572,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); - const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); + const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { 0 => return thread_ptr, posix.EAGAIN => return SpawnThreadError.SystemResources, diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 5186ff32d3..3e7b836ac7 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -665,15 +665,15 @@ pub fn dup2(old: i32, new: i32) usize { return syscall2(SYS_dup2, usize(old), usize(new)); } -pub fn chdir(path: &const u8) usize { +pub fn chdir(path: *const u8) usize { return syscall1(SYS_chdir, @ptrToInt(path)); } -pub fn chroot(path: &const u8) usize { +pub fn chroot(path: *const u8) usize { return syscall1(SYS_chroot, @ptrToInt(path)); } -pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize { +pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); } @@ -681,15 +681,15 @@ pub fn fork() usize { return syscall0(SYS_fork); } -pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?×pec) usize { +pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) usize { return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); } -pub fn getcwd(buf: &u8, size: usize) usize { +pub fn getcwd(buf: *u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } -pub fn getdents(fd: i32, dirp: &u8, count: usize) usize { +pub fn getdents(fd: i32, dirp: *u8, count: usize) usize { return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count); } @@ -698,27 +698,27 @@ pub fn isatty(fd: i32) bool { return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } -pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) usize { +pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } -pub fn mkdir(path: &const u8, mode: u32) usize { +pub fn mkdir(path: *const u8, mode: u32) usize { return syscall2(SYS_mkdir, @ptrToInt(path), mode); } -pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize { +pub fn mount(special: *const u8, dir: *const u8, fstype: *const u8, flags: usize, data: usize) usize { return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); } -pub fn umount(special: &const u8) usize { +pub fn umount(special: *const u8) usize { return syscall2(SYS_umount2, @ptrToInt(special), 0); } -pub fn umount2(special: &const u8, flags: u32) usize { +pub fn umount2(special: *const u8, flags: u32) usize { return syscall2(SYS_umount2, @ptrToInt(special), flags); } -pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { +pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } @@ -726,60 +726,60 @@ pub fn munmap(address: usize, length: usize) usize { return syscall2(SYS_munmap, address, length); } -pub fn read(fd: i32, buf: &u8, count: usize) usize { +pub fn read(fd: i32, buf: *u8, count: usize) usize { return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count); } -pub fn rmdir(path: &const u8) usize { +pub fn rmdir(path: *const u8) usize { return syscall1(SYS_rmdir, @ptrToInt(path)); } -pub fn symlink(existing: &const u8, new: &const u8) usize { +pub fn symlink(existing: *const u8, new: *const u8) usize { return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); } -pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize { +pub fn pread(fd: i32, buf: *u8, count: usize, offset: usize) usize { return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); } -pub fn access(path: &const u8, mode: u32) usize { +pub fn access(path: *const u8, mode: u32) usize { return syscall2(SYS_access, @ptrToInt(path), mode); } -pub fn pipe(fd: &[2]i32) usize { +pub fn pipe(fd: *[2]i32) usize { return pipe2(fd, 0); } -pub fn pipe2(fd: &[2]i32, flags: usize) usize { +pub fn pipe2(fd: *[2]i32, flags: usize) usize { return syscall2(SYS_pipe2, @ptrToInt(fd), flags); } -pub fn write(fd: i32, buf: &const u8, count: usize) usize { +pub fn write(fd: i32, buf: *const u8, count: usize) usize { return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count); } -pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) usize { +pub fn pwrite(fd: i32, buf: *const u8, count: usize, offset: usize) usize { return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset); } -pub fn rename(old: &const u8, new: &const u8) usize { +pub fn rename(old: *const u8, new: *const u8) usize { return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); } -pub fn open(path: &const u8, flags: u32, perm: usize) usize { +pub fn open(path: *const u8, flags: u32, perm: usize) usize { return syscall3(SYS_open, @ptrToInt(path), flags, perm); } -pub fn create(path: &const u8, perm: usize) usize { +pub fn create(path: *const u8, perm: usize) usize { return syscall2(SYS_creat, @ptrToInt(path), perm); } -pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize { +pub fn openat(dirfd: i32, path: *const u8, flags: usize, mode: usize) usize { return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); } /// See also `clone` (from the arch-specific include) -pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize { +pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); } @@ -801,7 +801,7 @@ pub fn exit(status: i32) noreturn { unreachable; } -pub fn getrandom(buf: &u8, count: usize, flags: u32) usize { +pub fn getrandom(buf: *u8, count: usize, flags: u32) usize { return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags)); } @@ -809,15 +809,15 @@ pub fn kill(pid: i32, sig: i32) usize { return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig)); } -pub fn unlink(path: &const u8) usize { +pub fn unlink(path: *const u8) usize { return syscall1(SYS_unlink, @ptrToInt(path)); } -pub fn waitpid(pid: i32, status: &i32, options: i32) usize { +pub fn waitpid(pid: i32, status: *i32, options: i32) usize { return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); } -pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { if (VDSO_CGT_SYM.len != 0) { const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); if (@ptrToInt(f) != 0) { @@ -831,7 +831,7 @@ pub fn clock_gettime(clk_id: i32, tp: ×pec) usize { return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } var vdso_clock_gettime = init_vdso_clock_gettime; -extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { +extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); @@ -839,23 +839,23 @@ extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize { return f(clk, ts); } -pub fn clock_getres(clk_id: i32, tp: ×pec) usize { +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } -pub fn clock_settime(clk_id: i32, tp: &const timespec) usize { +pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); } -pub fn gettimeofday(tv: &timeval, tz: &timezone) usize { +pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); } -pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize { +pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); } -pub fn nanosleep(req: &const timespec, rem: ?×pec) usize { +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); } @@ -899,11 +899,11 @@ pub fn setegid(egid: u32) usize { return syscall1(SYS_setegid, egid); } -pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize { +pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); } -pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize { +pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); } @@ -915,11 +915,11 @@ pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { return syscall3(SYS_setresgid, rgid, egid, sgid); } -pub fn getgroups(size: usize, list: &u32) usize { +pub fn getgroups(size: usize, list: *u32) usize { return syscall2(SYS_getgroups, size, @ptrToInt(list)); } -pub fn setgroups(size: usize, list: &const u32) usize { +pub fn setgroups(size: usize, list: *const u32) usize { return syscall2(SYS_setgroups, size, @ptrToInt(list)); } @@ -927,11 +927,11 @@ pub fn getpid() i32 { return @bitCast(i32, u32(syscall0(SYS_getpid))); } -pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize { +pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); } -pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize { +pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { assert(sig >= 1); assert(sig != SIGKILL); assert(sig != SIGSTOP); @@ -942,8 +942,8 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti .restorer = @ptrCast(extern fn () void, restore_rt), }; var ksa_old: k_sigaction = undefined; - @memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8); - const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); + @memcpy(@ptrCast(*u8, *ksa.mask), @ptrCast(*const u8, *act.mask), 8); + const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(*ksa), @ptrToInt(*ksa_old), @sizeOf(@typeOf(ksa.mask))); const err = getErrno(result); if (err != 0) { return result; @@ -951,7 +951,7 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti if (oact) |old| { old.handler = ksa_old.handler; old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast(&u8, &old.mask), @ptrCast(&const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); + @memcpy(@ptrCast(*u8, *old.mask), @ptrCast(*const u8, *ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); } return 0; } @@ -989,24 +989,24 @@ pub fn raise(sig: i32) usize { return ret; } -fn blockAllSignals(set: &sigset_t) void { +fn blockAllSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); } -fn blockAppSignals(set: &sigset_t) void { +fn blockAppSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); } -fn restoreSignals(set: &sigset_t) void { +fn restoreSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); } -pub fn sigaddset(set: &sigset_t, sig: u6) void { +pub fn sigaddset(set: *sigset_t, sig: u6) void { const s = sig - 1; (set.*)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); } -pub fn sigismember(set: &const sigset_t, sig: u6) bool { +pub fn sigismember(set: *const sigset_t, sig: u6) bool { const s = sig - 1; return ((set.*)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; } @@ -1036,15 +1036,15 @@ pub const sockaddr_in6 = extern struct { }; pub const iovec = extern struct { - iov_base: &u8, + iov_base: *u8, iov_len: usize, }; -pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize { +pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return syscall3(SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len)); } -pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize { +pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len)); } @@ -1052,27 +1052,27 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { return syscall3(SYS_socket, domain, socket_type, protocol); } -pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize { +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: *const u8, optlen: socklen_t) usize { return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); } -pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize { +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: *u8, noalias optlen: *socklen_t) usize { return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } -pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize { +pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { return syscall3(SYS_sendmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn connect(fd: i32, addr: &const sockaddr, len: socklen_t) usize { +pub fn connect(fd: i32, addr: *const sockaddr, len: socklen_t) usize { return syscall3(SYS_connect, usize(fd), @ptrToInt(addr), usize(len)); } -pub fn recvmsg(fd: i32, msg: &msghdr, flags: u32) usize { +pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32, noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) usize { +pub fn recvfrom(fd: i32, noalias buf: *u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } @@ -1080,7 +1080,7 @@ pub fn shutdown(fd: i32, how: i32) usize { return syscall2(SYS_shutdown, usize(fd), usize(how)); } -pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize { +pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len)); } @@ -1088,79 +1088,79 @@ pub fn listen(fd: i32, backlog: u32) usize { return syscall2(SYS_listen, usize(fd), backlog); } -pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize { +pub fn sendto(fd: i32, buf: *const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen)); } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { - return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0])); + return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(*fd[0])); } -pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize { +pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { return accept4(fd, addr, len, 0); } -pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: u32) usize { +pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags); } -pub fn fstat(fd: i32, stat_buf: &Stat) usize { +pub fn fstat(fd: i32, stat_buf: *Stat) usize { return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); } -pub fn stat(pathname: &const u8, statbuf: &Stat) usize { +pub fn stat(pathname: *const u8, statbuf: *Stat) usize { return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn lstat(pathname: &const u8, statbuf: &Stat) usize { +pub fn lstat(pathname: *const u8, statbuf: *Stat) usize { return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn listxattr(path: &const u8, list: &u8, size: usize) usize { +pub fn listxattr(path: *const u8, list: *u8, size: usize) usize { return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize { +pub fn llistxattr(path: *const u8, list: *u8, size: usize) usize { return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn flistxattr(fd: usize, list: &u8, size: usize) usize { +pub fn flistxattr(fd: usize, list: *u8, size: usize) usize { return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); } -pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize { +pub fn getxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize { +pub fn lgetxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize { +pub fn fgetxattr(fd: usize, name: *const u8, value: *void, size: usize) usize { return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); } -pub fn setxattr(path: &const u8, name: &const u8, value: &const void, size: usize, flags: usize) usize { +pub fn setxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void, size: usize, flags: usize) usize { +pub fn lsetxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn fsetxattr(fd: usize, name: &const u8, value: &const void, size: usize, flags: usize) usize { +pub fn fsetxattr(fd: usize, name: *const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn removexattr(path: &const u8, name: &const u8) usize { +pub fn removexattr(path: *const u8, name: *const u8) usize { return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn lremovexattr(path: &const u8, name: &const u8) usize { +pub fn lremovexattr(path: *const u8, name: *const u8) usize { return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn fremovexattr(fd: usize, name: &const u8) usize { +pub fn fremovexattr(fd: usize, name: *const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } @@ -1184,11 +1184,11 @@ pub fn epoll_create1(flags: usize) usize { return syscall1(SYS_epoll_create1, flags); } -pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize { +pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); } -pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize { +pub fn epoll_wait(epoll_fd: i32, events: *epoll_event, maxevents: u32, timeout: i32) usize { return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); } @@ -1201,11 +1201,11 @@ pub const itimerspec = extern struct { it_value: timespec, }; -pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) usize { +pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { return syscall2(SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value)); } -pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) usize { +pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)); } @@ -1300,8 +1300,8 @@ pub fn CAP_TO_INDEX(cap: u8) u8 { } pub const cap_t = extern struct { - hdrp: &cap_user_header_t, - datap: &cap_user_data_t, + hdrp: *cap_user_header_t, + datap: *cap_user_data_t, }; pub const cap_user_header_t = extern struct { @@ -1319,11 +1319,11 @@ pub fn unshare(flags: usize) usize { return syscall1(SYS_unshare, usize(flags)); } -pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize { +pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); } -pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize { +pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 8e0a285841..1317da6388 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -8,11 +8,11 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR]; if (vdso_addr == 0) return 0; - const eh = @intToPtr(&elf.Ehdr, vdso_addr); + const eh = @intToPtr(*elf.Ehdr, vdso_addr); var ph_addr: usize = vdso_addr + eh.e_phoff; - const ph = @intToPtr(&elf.Phdr, ph_addr); + const ph = @intToPtr(*elf.Phdr, ph_addr); - var maybe_dynv: ?&usize = null; + var maybe_dynv: ?*usize = null; var base: usize = @maxValue(usize); { var i: usize = 0; @@ -20,10 +20,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { i += 1; ph_addr += eh.e_phentsize; }) { - const this_ph = @intToPtr(&elf.Phdr, ph_addr); + const this_ph = @intToPtr(*elf.Phdr, ph_addr); switch (this_ph.p_type) { elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, - elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset), + elf.PT_DYNAMIC => maybe_dynv = @intToPtr(*usize, vdso_addr + this_ph.p_offset), else => {}, } } @@ -31,22 +31,22 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const dynv = maybe_dynv ?? return 0; if (base == @maxValue(usize)) return 0; - var maybe_strings: ?&u8 = null; - var maybe_syms: ?&elf.Sym = null; - var maybe_hashtab: ?&linux.Elf_Symndx = null; - var maybe_versym: ?&u16 = null; - var maybe_verdef: ?&elf.Verdef = null; + var maybe_strings: ?*u8 = null; + var maybe_syms: ?*elf.Sym = null; + var maybe_hashtab: ?*linux.Elf_Symndx = null; + var maybe_versym: ?*u16 = null; + var maybe_verdef: ?*elf.Verdef = null; { var i: usize = 0; while (dynv[i] != 0) : (i += 2) { const p = base + dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p), - elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p), - elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p), - elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p), - elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p), + elf.DT_STRTAB => maybe_strings = @intToPtr(*u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr(*elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr(*linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr(*u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), else => {}, } } @@ -76,7 +76,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { return 0; } -fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: *u8) bool { var def = def_arg; const vsym = @bitCast(u32, vsym_arg) & 0x7fff; while (true) { @@ -84,8 +84,8 @@ fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: & break; if (def.vd_next == 0) return false; - def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next); + def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } - const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def) + def.vd_aux); + const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); } diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index b43a642038..9a90e64757 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -463,7 +463,7 @@ pub fn syscall6( } /// This matches the libc clone function. -pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize; +pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; pub nakedcc fn restore_rt() void { return asm volatile ("syscall" @@ -474,12 +474,12 @@ pub nakedcc fn restore_rt() void { } pub const msghdr = extern struct { - msg_name: &u8, + msg_name: *u8, msg_namelen: socklen_t, - msg_iov: &iovec, + msg_iov: *iovec, msg_iovlen: i32, __pad1: i32, - msg_control: &u8, + msg_control: *u8, msg_controllen: socklen_t, __pad2: socklen_t, msg_flags: i32, diff --git a/std/os/path.zig b/std/os/path.zig index 162faffc42..4df6179bf5 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool { /// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: &Allocator, paths: ...) ![]u8 { +pub fn join(allocator: *Allocator, paths: ...) ![]u8 { if (is_windows) { return joinWindows(allocator, paths); } else { @@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) ![]u8 { } } -pub fn joinWindows(allocator: &Allocator, paths: ...) ![]u8 { +pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 { return mem.join(allocator, sep_windows, paths); } -pub fn joinPosix(allocator: &Allocator, paths: ...) ![]u8 { +pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 { return mem.join(allocator, sep_posix, paths); } @@ -310,7 +310,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { } /// Converts the command line arguments into a slice and calls `resolveSlice`. -pub fn resolve(allocator: &Allocator, args: ...) ![]u8 { +pub fn resolve(allocator: *Allocator, args: ...) ![]u8 { var paths: [args.len][]const u8 = undefined; comptime var arg_i = 0; inline while (arg_i < args.len) : (arg_i += 1) { @@ -320,7 +320,7 @@ pub fn resolve(allocator: &Allocator, args: ...) ![]u8 { } /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. -pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 { +pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (is_windows) { return resolveWindows(allocator, paths); } else { @@ -334,7 +334,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 { /// If all paths are relative it uses the current working directory as a starting point. /// Each drive has its own current working directory. /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. -pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { +pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(is_windows); // resolveWindows called on non windows can't use getCwd return os.getCwd(allocator); @@ -513,7 +513,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { /// It resolves "." and "..". /// The result does not have a trailing path separator. /// If all paths are relative it uses the current working directory as a starting point. -pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) ![]u8 { +pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(!is_windows); // resolvePosix called on windows can't use getCwd return os.getCwd(allocator); @@ -883,7 +883,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// resolve to the same path (after calling `resolve` on each), a zero-length /// string is returned. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. -pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { +pub fn relative(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { if (is_windows) { return relativeWindows(allocator, from, to); } else { @@ -891,7 +891,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { } } -pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { +pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { const resolved_from = try resolveWindows(allocator, [][]const u8{from}); defer allocator.free(resolved_from); @@ -964,7 +964,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) return []u8{}; } -pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { +pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![]u8 { const resolved_from = try resolvePosix(allocator, [][]const u8{from}); defer allocator.free(resolved_from); @@ -1063,7 +1063,7 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons /// Expands all symbolic links and resolves references to `.`, `..`, and /// extra `/` characters in ::pathname. /// Caller must deallocate result. -pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 { +pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 { switch (builtin.os) { Os.windows => { const pathname_buf = try allocator.alloc(u8, pathname.len + 1); diff --git a/std/os/test.zig b/std/os/test.zig index 4dfe76224a..4aa3535829 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -63,7 +63,7 @@ fn start1(ctx: void) u8 { return 0; } -fn start2(ctx: &i32) u8 { +fn start2(ctx: *i32) u8 { _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); return 0; } diff --git a/std/os/time.zig b/std/os/time.zig index 9a7c682483..8629504323 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -200,7 +200,7 @@ pub const Timer = struct { } /// Reads the timer value since start or the last reset in nanoseconds - pub fn read(self: &Timer) u64 { + pub fn read(self: *Timer) u64 { var clock = clockNative() - self.start_time; return switch (builtin.os) { Os.windows => @divFloor(clock * ns_per_s, self.frequency), @@ -211,12 +211,12 @@ pub const Timer = struct { } /// Resets the timer value to 0/now. - pub fn reset(self: &Timer) void { + pub fn reset(self: *Timer) void { self.start_time = clockNative(); } /// Returns the current value of the timer in nanoseconds, then resets it - pub fn lap(self: &Timer) u64 { + pub fn lap(self: *Timer) u64 { var now = clockNative(); var lap_time = self.read(); self.start_time = now; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 264ea391c4..85f69836d5 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,7 +1,7 @@ pub const ERROR = @import("error.zig"); pub extern "advapi32" stdcallcc fn CryptAcquireContextA( - phProv: &HCRYPTPROV, + phProv: *HCRYPTPROV, pszContainer: ?LPCSTR, pszProvider: ?LPCSTR, dwProvType: DWORD, @@ -10,13 +10,13 @@ pub extern "advapi32" stdcallcc fn CryptAcquireContextA( pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; -pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: &BYTE) BOOL; +pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *BYTE) BOOL; pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; pub extern "kernel32" stdcallcc fn CreateDirectoryA( lpPathName: LPCSTR, - lpSecurityAttributes: ?&SECURITY_ATTRIBUTES, + lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, ) BOOL; pub extern "kernel32" stdcallcc fn CreateFileA( @@ -30,23 +30,23 @@ pub extern "kernel32" stdcallcc fn CreateFileA( ) HANDLE; pub extern "kernel32" stdcallcc fn CreatePipe( - hReadPipe: &HANDLE, - hWritePipe: &HANDLE, - lpPipeAttributes: &const SECURITY_ATTRIBUTES, + hReadPipe: *HANDLE, + hWritePipe: *HANDLE, + lpPipeAttributes: *const SECURITY_ATTRIBUTES, nSize: DWORD, ) BOOL; pub extern "kernel32" stdcallcc fn CreateProcessA( lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR, - lpProcessAttributes: ?&SECURITY_ATTRIBUTES, - lpThreadAttributes: ?&SECURITY_ATTRIBUTES, + lpProcessAttributes: ?*SECURITY_ATTRIBUTES, + lpThreadAttributes: ?*SECURITY_ATTRIBUTES, bInheritHandles: BOOL, dwCreationFlags: DWORD, - lpEnvironment: ?&c_void, + lpEnvironment: ?*c_void, lpCurrentDirectory: ?LPCSTR, - lpStartupInfo: &STARTUPINFOA, - lpProcessInformation: &PROCESS_INFORMATION, + lpStartupInfo: *STARTUPINFOA, + lpProcessInformation: *PROCESS_INFORMATION, ) BOOL; pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( @@ -65,7 +65,7 @@ pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: LPCH) BOOL; pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; -pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) BOOL; +pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; @@ -73,9 +73,9 @@ pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?LPCH; pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; -pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) BOOL; +pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; -pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: &LARGE_INTEGER) BOOL; +pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; @@ -84,7 +84,7 @@ pub extern "kernel32" stdcallcc fn GetLastError() DWORD; pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( in_hFile: HANDLE, in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - out_lpFileInformation: &c_void, + out_lpFileInformation: *c_void, in_dwBufferSize: DWORD, ) BOOL; @@ -97,21 +97,21 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; -pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void; +pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: &const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: &const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?&c_void; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; pub extern "kernel32" stdcallcc fn MoveFileExA( lpExistingFileName: LPCSTR, @@ -119,24 +119,24 @@ pub extern "kernel32" stdcallcc fn MoveFileExA( dwFlags: DWORD, ) BOOL; -pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL; +pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; -pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL; +pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, - out_lpBuffer: &c_void, + out_lpBuffer: *c_void, in_nNumberOfBytesToRead: DWORD, - out_lpNumberOfBytesRead: &DWORD, - in_out_lpOverlapped: ?&OVERLAPPED, + out_lpNumberOfBytesRead: *DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, ) BOOL; pub extern "kernel32" stdcallcc fn SetFilePointerEx( in_fFile: HANDLE, in_liDistanceToMove: LARGE_INTEGER, - out_opt_ldNewFilePointer: ?&LARGE_INTEGER, + out_opt_ldNewFilePointer: ?*LARGE_INTEGER, in_dwMoveMethod: DWORD, ) BOOL; @@ -150,10 +150,10 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis pub extern "kernel32" stdcallcc fn WriteFile( in_hFile: HANDLE, - in_lpBuffer: &const c_void, + in_lpBuffer: *const c_void, in_nNumberOfBytesToWrite: DWORD, - out_lpNumberOfBytesWritten: ?&DWORD, - in_out_lpOverlapped: ?&OVERLAPPED, + out_lpNumberOfBytesWritten: ?*DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, ) BOOL; //TODO: call unicode versions instead of relying on ANSI code page @@ -171,23 +171,23 @@ pub const BYTE = u8; pub const CHAR = u8; pub const DWORD = u32; pub const FLOAT = f32; -pub const HANDLE = &c_void; +pub const HANDLE = *c_void; pub const HCRYPTPROV = ULONG_PTR; -pub const HINSTANCE = &@OpaqueType(); -pub const HMODULE = &@OpaqueType(); +pub const HINSTANCE = *@OpaqueType(); +pub const HMODULE = *@OpaqueType(); pub const INT = c_int; -pub const LPBYTE = &BYTE; -pub const LPCH = &CHAR; -pub const LPCSTR = &const CHAR; -pub const LPCTSTR = &const TCHAR; -pub const LPCVOID = &const c_void; -pub const LPDWORD = &DWORD; -pub const LPSTR = &CHAR; +pub const LPBYTE = *BYTE; +pub const LPCH = *CHAR; +pub const LPCSTR = *const CHAR; +pub const LPCTSTR = *const TCHAR; +pub const LPCVOID = *const c_void; +pub const LPDWORD = *DWORD; +pub const LPSTR = *CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; -pub const LPVOID = &c_void; -pub const LPWSTR = &WCHAR; -pub const PVOID = &c_void; -pub const PWSTR = &WCHAR; +pub const LPVOID = *c_void; +pub const LPWSTR = *WCHAR; +pub const PVOID = *c_void; +pub const PWSTR = *WCHAR; pub const SIZE_T = usize; pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; @@ -218,7 +218,7 @@ pub const OVERLAPPED = extern struct { Pointer: PVOID, hEvent: HANDLE, }; -pub const LPOVERLAPPED = &OVERLAPPED; +pub const LPOVERLAPPED = *OVERLAPPED; pub const MAX_PATH = 260; @@ -271,11 +271,11 @@ pub const VOLUME_NAME_NT = 0x2; pub const SECURITY_ATTRIBUTES = extern struct { nLength: DWORD, - lpSecurityDescriptor: ?&c_void, + lpSecurityDescriptor: ?*c_void, bInheritHandle: BOOL, }; -pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; -pub const LPSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; +pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; +pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; pub const GENERIC_READ = 0x80000000; pub const GENERIC_WRITE = 0x40000000; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 2bd8a157e4..7170346108 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, @@ -68,11 +68,11 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); - if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(&c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { + if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(*c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { return true; } - const name_info = @ptrCast(&const windows.FILE_NAME_INFO, &name_info_bytes[0]); + const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; const name_wide = ([]u16)(name_bytes); return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or @@ -91,7 +91,7 @@ pub const OpenError = error{ /// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator. pub fn windowsOpen( - allocator: &mem.Allocator, + allocator: *mem.Allocator, file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD, @@ -119,7 +119,7 @@ pub fn windowsOpen( } /// Caller must free result. -pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) ![]u8 { +pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { // count bytes needed const bytes_needed = x: { var bytes_needed: usize = 1; // 1 for the final null byte @@ -150,7 +150,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) return result; } -pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows.HMODULE { +pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { const padded_buff = try cstr.addNullByte(allocator, dll_path); defer allocator.free(padded_buff); return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound; diff --git a/std/os/zen.zig b/std/os/zen.zig index 2411c5363e..2312b36dea 100644 --- a/std/os/zen.zig +++ b/std/os/zen.zig @@ -8,7 +8,7 @@ pub const Message = struct { type: usize, payload: usize, - pub fn from(mailbox_id: &const MailboxId) Message { + pub fn from(mailbox_id: *const MailboxId) Message { return Message{ .sender = MailboxId.Undefined, .receiver = *mailbox_id, @@ -17,7 +17,7 @@ pub const Message = struct { }; } - pub fn to(mailbox_id: &const MailboxId, msg_type: usize) Message { + pub fn to(mailbox_id: *const MailboxId, msg_type: usize) Message { return Message{ .sender = MailboxId.This, .receiver = *mailbox_id, @@ -26,7 +26,7 @@ pub const Message = struct { }; } - pub fn withData(mailbox_id: &const MailboxId, msg_type: usize, payload: usize) Message { + pub fn withData(mailbox_id: *const MailboxId, msg_type: usize, payload: usize) Message { return Message{ .sender = MailboxId.This, .receiver = *mailbox_id, @@ -67,7 +67,7 @@ pub const getErrno = @import("linux/index.zig").getErrno; use @import("linux/errno.zig"); // TODO: implement this correctly. -pub fn read(fd: i32, buf: &u8, count: usize) usize { +pub fn read(fd: i32, buf: *u8, count: usize) usize { switch (fd) { STDIN_FILENO => { var i: usize = 0; @@ -75,7 +75,7 @@ pub fn read(fd: i32, buf: &u8, count: usize) usize { send(Message.to(Server.Keyboard, 0)); var message = Message.from(MailboxId.This); - receive(&message); + receive(*message); buf[i] = u8(message.payload); } @@ -86,7 +86,7 @@ pub fn read(fd: i32, buf: &u8, count: usize) usize { } // TODO: implement this correctly. -pub fn write(fd: i32, buf: &const u8, count: usize) usize { +pub fn write(fd: i32, buf: *const u8, count: usize) usize { switch (fd) { STDOUT_FILENO, STDERR_FILENO => { var i: usize = 0; @@ -126,22 +126,22 @@ pub fn exit(status: i32) noreturn { unreachable; } -pub fn createPort(mailbox_id: &const MailboxId) void { +pub fn createPort(mailbox_id: *const MailboxId) void { _ = switch (*mailbox_id) { MailboxId.Port => |id| syscall1(Syscall.createPort, id), else => unreachable, }; } -pub fn send(message: &const Message) void { +pub fn send(message: *const Message) void { _ = syscall1(Syscall.send, @ptrToInt(message)); } -pub fn receive(destination: &Message) void { +pub fn receive(destination: *Message) void { _ = syscall1(Syscall.receive, @ptrToInt(destination)); } -pub fn subscribeIRQ(irq: u8, mailbox_id: &const MailboxId) void { +pub fn subscribeIRQ(irq: u8, mailbox_id: *const MailboxId) void { _ = syscall2(Syscall.subscribeIRQ, irq, @ptrToInt(mailbox_id)); } diff --git a/std/rand/index.zig b/std/rand/index.zig index c32309a0fd..3a1a559cd9 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -28,15 +28,15 @@ pub const DefaultPrng = Xoroshiro128; pub const DefaultCsprng = Isaac64; pub const Random = struct { - fillFn: fn (r: &Random, buf: []u8) void, + fillFn: fn (r: *Random, buf: []u8) void, /// Read random bytes into the specified buffer until fill. - pub fn bytes(r: &Random, buf: []u8) void { + pub fn bytes(r: *Random, buf: []u8) void { r.fillFn(r, buf); } /// Return a random integer/boolean type. - pub fn scalar(r: &Random, comptime T: type) T { + pub fn scalar(r: *Random, comptime T: type) T { var rand_bytes: [@sizeOf(T)]u8 = undefined; r.bytes(rand_bytes[0..]); @@ -50,7 +50,7 @@ pub const Random = struct { /// Get a random unsigned integer with even distribution between `start` /// inclusive and `end` exclusive. - pub fn range(r: &Random, comptime T: type, start: T, end: T) T { + pub fn range(r: *Random, comptime T: type, start: T, end: T) T { assert(start <= end); if (T.is_signed) { const uint = @IntType(false, T.bit_count); @@ -92,7 +92,7 @@ pub const Random = struct { } /// Return a floating point value evenly distributed in the range [0, 1). - pub fn float(r: &Random, comptime T: type) T { + pub fn float(r: *Random, comptime T: type) T { // Generate a uniform value between [1, 2) and scale down to [0, 1). // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. switch (T) { @@ -113,7 +113,7 @@ pub const Random = struct { /// Return a floating point value normally distributed with mean = 0, stddev = 1. /// /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. - pub fn floatNorm(r: &Random, comptime T: type) T { + pub fn floatNorm(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.NormDist); switch (T) { f32 => return f32(value), @@ -125,7 +125,7 @@ pub const Random = struct { /// Return an exponentially distributed float with a rate parameter of 1. /// /// To use a different rate parameter, use: floatExp(...) / desiredRate. - pub fn floatExp(r: &Random, comptime T: type) T { + pub fn floatExp(r: *Random, comptime T: type) T { const value = ziggurat.next_f64(r, ziggurat.ExpDist); switch (T) { f32 => return f32(value), @@ -135,7 +135,7 @@ pub const Random = struct { } /// Shuffle a slice into a random order. - pub fn shuffle(r: &Random, comptime T: type, buf: []T) void { + pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { if (buf.len < 2) { return; } @@ -159,7 +159,7 @@ const SplitMix64 = struct { return SplitMix64{ .s = seed }; } - pub fn next(self: &SplitMix64) u64 { + pub fn next(self: *SplitMix64) u64 { self.s +%= 0x9e3779b97f4a7c15; var z = self.s; @@ -208,7 +208,7 @@ pub const Pcg = struct { return pcg; } - fn next(self: &Pcg) u32 { + fn next(self: *Pcg) u32 { const l = self.s; self.s = l *% default_multiplier +% (self.i | 1); @@ -218,13 +218,13 @@ pub const Pcg = struct { return (xor_s >> u5(rot)) | (xor_s << u5((0 -% rot) & 31)); } - fn seed(self: &Pcg, init_s: u64) void { + fn seed(self: *Pcg, init_s: u64) void { // Pcg requires 128-bits of seed. var gen = SplitMix64.init(init_s); self.seedTwo(gen.next(), gen.next()); } - fn seedTwo(self: &Pcg, init_s: u64, init_i: u64) void { + fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { self.s = 0; self.i = (init_s << 1) | 1; self.s = self.s *% default_multiplier +% self.i; @@ -232,7 +232,7 @@ pub const Pcg = struct { self.s = self.s *% default_multiplier +% self.i; } - fn fill(r: &Random, buf: []u8) void { + fn fill(r: *Random, buf: []u8) void { const self = @fieldParentPtr(Pcg, "random", r); var i: usize = 0; @@ -297,7 +297,7 @@ pub const Xoroshiro128 = struct { return x; } - fn next(self: &Xoroshiro128) u64 { + fn next(self: *Xoroshiro128) u64 { const s0 = self.s[0]; var s1 = self.s[1]; const r = s0 +% s1; @@ -310,7 +310,7 @@ pub const Xoroshiro128 = struct { } // Skip 2^64 places ahead in the sequence - fn jump(self: &Xoroshiro128) void { + fn jump(self: *Xoroshiro128) void { var s0: u64 = 0; var s1: u64 = 0; @@ -334,7 +334,7 @@ pub const Xoroshiro128 = struct { self.s[1] = s1; } - fn seed(self: &Xoroshiro128, init_s: u64) void { + fn seed(self: *Xoroshiro128, init_s: u64) void { // Xoroshiro requires 128-bits of seed. var gen = SplitMix64.init(init_s); @@ -342,7 +342,7 @@ pub const Xoroshiro128 = struct { self.s[1] = gen.next(); } - fn fill(r: &Random, buf: []u8) void { + fn fill(r: *Random, buf: []u8) void { const self = @fieldParentPtr(Xoroshiro128, "random", r); var i: usize = 0; @@ -435,7 +435,7 @@ pub const Isaac64 = struct { return isaac; } - fn step(self: &Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { + fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { const x = self.m[base + m1]; self.a = mix +% self.m[base + m2]; @@ -446,7 +446,7 @@ pub const Isaac64 = struct { self.r[self.r.len - 1 - base - m1] = self.b; } - fn refill(self: &Isaac64) void { + fn refill(self: *Isaac64) void { const midpoint = self.r.len / 2; self.c +%= 1; @@ -475,7 +475,7 @@ pub const Isaac64 = struct { self.i = 0; } - fn next(self: &Isaac64) u64 { + fn next(self: *Isaac64) u64 { if (self.i >= self.r.len) { self.refill(); } @@ -485,7 +485,7 @@ pub const Isaac64 = struct { return value; } - fn seed(self: &Isaac64, init_s: u64, comptime rounds: usize) void { + fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { // We ignore the multi-pass requirement since we don't currently expose full access to // seeding the self.m array completely. mem.set(u64, self.m[0..], 0); @@ -551,7 +551,7 @@ pub const Isaac64 = struct { self.i = self.r.len; // trigger refill on first value } - fn fill(r: &Random, buf: []u8) void { + fn fill(r: *Random, buf: []u8) void { const self = @fieldParentPtr(Isaac64, "random", r); var i: usize = 0; @@ -666,7 +666,7 @@ test "Random range" { testRange(&prng.random, 10, 14); } -fn testRange(r: &Random, start: i32, end: i32) void { +fn testRange(r: *Random, start: i32, end: i32) void { const count = usize(end - start); var values_buffer = []bool{false} ** 20; const values = values_buffer[0..count]; diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 7daeb59165..774d3bd52a 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -12,7 +12,7 @@ const std = @import("../index.zig"); const math = std.math; const Random = std.rand.Random; -pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 { +pub fn next_f64(random: *Random, comptime tables: *const ZigTable) f64 { while (true) { // We manually construct a float from parts as we can avoid an extra random lookup here by // using the unused exponent for the lookup table entry. @@ -60,7 +60,7 @@ pub const ZigTable = struct { // whether the distribution is symmetric is_symmetric: bool, // fallback calculation in the case we are in the 0 block - zero_case: fn (&Random, f64) f64, + zero_case: fn (*Random, f64) f64, }; // zigNorInit @@ -70,7 +70,7 @@ fn ZigTableGen( comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, - comptime zero_case: fn (&Random, f64) f64, + comptime zero_case: fn (*Random, f64) f64, ) ZigTable { var tables: ZigTable = undefined; @@ -110,7 +110,7 @@ fn norm_f(x: f64) f64 { fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } -fn norm_zero_case(random: &Random, u: f64) f64 { +fn norm_zero_case(random: *Random, u: f64) f64 { var x: f64 = 1; var y: f64 = 0; @@ -149,7 +149,7 @@ fn exp_f(x: f64) f64 { fn exp_f_inv(y: f64) f64 { return -math.ln(y); } -fn exp_zero_case(random: &Random, _: f64) f64 { +fn exp_zero_case(random: *Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); } diff --git a/std/segmented_list.zig b/std/segmented_list.zig index d755135fe8..be9a2071a0 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -87,49 +87,49 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const ShelfIndex = std.math.Log2Int(usize); prealloc_segment: [prealloc_item_count]T, - dynamic_segments: []&T, - allocator: &Allocator, + dynamic_segments: []*T, + allocator: *Allocator, len: usize, pub const prealloc_count = prealloc_item_count; /// Deinitialize with `deinit` - pub fn init(allocator: &Allocator) Self { + pub fn init(allocator: *Allocator) Self { return Self{ .allocator = allocator, .len = 0, .prealloc_segment = undefined, - .dynamic_segments = []&T{}, + .dynamic_segments = []*T{}, }; } - pub fn deinit(self: &Self) void { + pub fn deinit(self: *Self) void { self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); self.allocator.free(self.dynamic_segments); self.* = undefined; } - pub fn at(self: &Self, i: usize) &T { + pub fn at(self: *Self, i: usize) *T { assert(i < self.len); return self.uncheckedAt(i); } - pub fn count(self: &const Self) usize { + pub fn count(self: *const Self) usize { return self.len; } - pub fn push(self: &Self, item: &const T) !void { + pub fn push(self: *Self, item: *const T) !void { const new_item_ptr = try self.addOne(); new_item_ptr.* = item.*; } - pub fn pushMany(self: &Self, items: []const T) !void { + pub fn pushMany(self: *Self, items: []const T) !void { for (items) |item| { try self.push(item); } } - pub fn pop(self: &Self) ?T { + pub fn pop(self: *Self) ?T { if (self.len == 0) return null; const index = self.len - 1; @@ -138,7 +138,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return result; } - pub fn addOne(self: &Self) !&T { + pub fn addOne(self: *Self) !*T { const new_length = self.len + 1; try self.growCapacity(new_length); const result = self.uncheckedAt(self.len); @@ -147,7 +147,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } /// Grows or shrinks capacity to match usage. - pub fn setCapacity(self: &Self, new_capacity: usize) !void { + pub fn setCapacity(self: *Self, new_capacity: usize) !void { if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) { return self.shrinkCapacity(new_capacity); } else { @@ -156,15 +156,15 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } /// Only grows capacity, or retains current capacity - pub fn growCapacity(self: &Self, new_capacity: usize) !void { + pub fn growCapacity(self: *Self, new_capacity: usize) !void { const new_cap_shelf_count = shelfCount(new_capacity); const old_shelf_count = ShelfIndex(self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { - self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = try self.allocator.realloc(*T, self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; errdefer { self.freeShelves(i, old_shelf_count); - self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count); + self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, old_shelf_count); } while (i < new_cap_shelf_count) : (i += 1) { self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; @@ -173,12 +173,12 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } /// Only shrinks capacity or retains current capacity - pub fn shrinkCapacity(self: &Self, new_capacity: usize) void { + pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { if (new_capacity <= prealloc_item_count) { const len = ShelfIndex(self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); - self.dynamic_segments = []&T{}; + self.dynamic_segments = []*T{}; return; } @@ -190,10 +190,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } self.freeShelves(old_shelf_count, new_cap_shelf_count); - self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, new_cap_shelf_count); } - pub fn uncheckedAt(self: &Self, index: usize) &T { + pub fn uncheckedAt(self: *Self, index: usize) *T { if (index < prealloc_item_count) { return &self.prealloc_segment[index]; } @@ -230,7 +230,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index)); } - fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void { + fn freeShelves(self: *Self, from_count: ShelfIndex, to_count: ShelfIndex) void { var i = from_count; while (i != to_count) { i -= 1; @@ -239,13 +239,13 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub const Iterator = struct { - list: &Self, + list: *Self, index: usize, box_index: usize, shelf_index: ShelfIndex, shelf_size: usize, - pub fn next(it: &Iterator) ?&T { + pub fn next(it: *Iterator) ?*T { if (it.index >= it.list.len) return null; if (it.index < prealloc_item_count) { const ptr = &it.list.prealloc_segment[it.index]; @@ -269,7 +269,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return ptr; } - pub fn prev(it: &Iterator) ?&T { + pub fn prev(it: *Iterator) ?*T { if (it.index == 0) return null; it.index -= 1; @@ -286,7 +286,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } - pub fn peek(it: &Iterator) ?&T { + pub fn peek(it: *Iterator) ?*T { if (it.index >= it.list.len) return null; if (it.index < prealloc_item_count) @@ -295,7 +295,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type return &it.list.dynamic_segments[it.shelf_index][it.box_index]; } - pub fn set(it: &Iterator, index: usize) void { + pub fn set(it: *Iterator, index: usize) void { it.index = index; if (index < prealloc_item_count) return; it.shelf_index = shelfIndex(index); @@ -304,7 +304,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } }; - pub fn iterator(self: &Self, start_index: usize) Iterator { + pub fn iterator(self: *Self, start_index: usize) Iterator { var it = Iterator{ .list = self, .index = undefined, @@ -331,7 +331,7 @@ test "std.SegmentedList" { try testSegmentedList(16, a); } -fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void { +fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { var list = SegmentedList(i32, prealloc).init(allocator); defer list.deinit(); diff --git a/std/sort.zig b/std/sort.zig index 4e17718241..1b44c18dd9 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -5,7 +5,7 @@ const math = std.math; const builtin = @import("builtin"); /// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required). -pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) void { +pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void { { var i: usize = 1; while (i < items.len) : (i += 1) { @@ -30,7 +30,7 @@ const Range = struct { }; } - fn length(self: &const Range) usize { + fn length(self: *const Range) usize { return self.end - self.start; } }; @@ -58,12 +58,12 @@ const Iterator = struct { }; } - fn begin(self: &Iterator) void { + fn begin(self: *Iterator) void { self.numerator = 0; self.decimal = 0; } - fn nextRange(self: &Iterator) Range { + fn nextRange(self: *Iterator) Range { const start = self.decimal; self.decimal += self.decimal_step; @@ -79,11 +79,11 @@ const Iterator = struct { }; } - fn finished(self: &Iterator) bool { + fn finished(self: *Iterator) bool { return self.decimal >= self.size; } - fn nextLevel(self: &Iterator) bool { + fn nextLevel(self: *Iterator) bool { self.decimal_step += self.decimal_step; self.numerator_step += self.numerator_step; if (self.numerator_step >= self.denominator) { @@ -94,7 +94,7 @@ const Iterator = struct { return (self.decimal_step < self.size); } - fn length(self: &Iterator) usize { + fn length(self: *Iterator) usize { return self.decimal_step; } }; @@ -108,7 +108,7 @@ const Pull = struct { /// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required). /// Currently implemented as block sort. -pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) void { +pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void { // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c var cache: [512]T = undefined; @@ -741,7 +741,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &con } // merge operation without a buffer -fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn (&const T, &const T) bool) void { +fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const Range, lessThan: fn (*const T, *const T) bool) void { if (A_arg.length() == 0 or B_arg.length() == 0) return; // this just repeatedly binary searches into B and rotates A into position. @@ -783,7 +783,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const } // merge operation using an internal buffer -fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, buffer: &const Range) void { +fn mergeInternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, buffer: *const Range) void { // whenever we find a value to add to the final array, swap it with the value that's already in that spot // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order var A_count: usize = 0; @@ -819,7 +819,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s // combine a linear search with a binary search to reduce the number of comparisons in situations // where have some idea as to how many unique values there are and where the next value might be -fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -833,7 +833,7 @@ fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan); } -fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -847,7 +847,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &cons return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan); } -fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -861,7 +861,7 @@ fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index - skip, index), lessThan); } -fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool, unique: usize) usize { +fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize { if (range.length() == 0) return range.start; const skip = math.max(range.length() / unique, usize(1)); @@ -875,7 +875,7 @@ fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const return binaryLast(T, items, value, Range.init(index, index + skip), lessThan); } -fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool) usize { +fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -893,7 +893,7 @@ fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Rang return start; } -fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn (&const T, &const T) bool) usize { +fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize { var start = range.start; var end = range.end - 1; if (range.start >= range.end) return range.end; @@ -911,7 +911,7 @@ fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range return start; } -fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, into: []T) void { +fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, into: []T) void { var A_index: usize = A.start; var B_index: usize = B.start; const A_last = A.end; @@ -941,7 +941,7 @@ fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, less } } -fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn (&const T, &const T) bool, cache: []T) void { +fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, cache: []T) void { // A fits into the cache, so use that instead of the internal buffer var A_index: usize = 0; var B_index: usize = B.start; @@ -969,26 +969,26 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, mem.copy(T, items[insert_index..], cache[A_index..A_last]); } -fn swap(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool, order: &[8]u8, x: usize, y: usize) void { +fn swap(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool, order: *[8]u8, x: usize, y: usize) void { if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) { mem.swap(T, &items[x], &items[y]); mem.swap(u8, &(order.*)[x], &(order.*)[y]); } } -fn i32asc(lhs: &const i32, rhs: &const i32) bool { +fn i32asc(lhs: *const i32, rhs: *const i32) bool { return lhs.* < rhs.*; } -fn i32desc(lhs: &const i32, rhs: &const i32) bool { +fn i32desc(lhs: *const i32, rhs: *const i32) bool { return rhs.* < lhs.*; } -fn u8asc(lhs: &const u8, rhs: &const u8) bool { +fn u8asc(lhs: *const u8, rhs: *const u8) bool { return lhs.* < rhs.*; } -fn u8desc(lhs: &const u8, rhs: &const u8) bool { +fn u8desc(lhs: *const u8, rhs: *const u8) bool { return rhs.* < lhs.*; } @@ -1125,7 +1125,7 @@ const IdAndValue = struct { id: usize, value: i32, }; -fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) bool { +fn cmpByValue(a: *const IdAndValue, b: *const IdAndValue) bool { return i32asc(a.value, b.value); } @@ -1324,7 +1324,7 @@ test "sort fuzz testing" { var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn fuzzTest(rng: &std.rand.Random) void { +fn fuzzTest(rng: *std.rand.Random) void { const array_size = rng.range(usize, 0, 1000); var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); var array = fixed_allocator.allocator.alloc(IdAndValue, array_size) catch unreachable; @@ -1345,7 +1345,7 @@ fn fuzzTest(rng: &std.rand.Random) void { } } -pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) T { +pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T { var i: usize = 0; var smallest = items[0]; for (items[1..]) |item| { @@ -1356,7 +1356,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &cons return smallest; } -pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: &const T, rhs: &const T) bool) T { +pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T { var i: usize = 0; var biggest = items[0]; for (items[1..]) |item| { diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index c10f4aa806..5ed7874ca5 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -5,7 +5,7 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); -var argc_ptr: &usize = undefined; +var argc_ptr: *usize = undefined; comptime { const strong_linkage = builtin.GlobalLinkage.Strong; @@ -28,12 +28,12 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> &usize) + : [argc] "=r" (-> *usize) ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" - : [argc] "=r" (-> &usize) + : [argc] "=r" (-> *usize) ); }, else => @compileError("unsupported arch"), @@ -51,13 +51,13 @@ extern fn WinMainCRTStartup() noreturn { fn posixCallMainAndExit() noreturn { const argc = argc_ptr.*; - const argv = @ptrCast(&&u8, &argc_ptr[1]); - const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]); + const argv = @ptrCast(**u8, &argc_ptr[1]); + const envp_nullable = @ptrCast(*?*u8, &argv[argc + 1]); var envp_count: usize = 0; while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count]; + const envp = @ptrCast(**u8, envp_nullable)[0..envp_count]; if (builtin.os == builtin.Os.linux) { - const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1]; + const auxv = &@ptrCast(*usize, envp.ptr)[envp_count + 1]; var i: usize = 0; while (auxv[i] != 0) : (i += 2) { if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i + 1]; @@ -68,16 +68,16 @@ fn posixCallMainAndExit() noreturn { std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 { +fn callMainWithArgs(argc: usize, argv: **u8, envp: []*u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; std.os.posix_environ_raw = envp; return callMain(); } -extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 { +extern fn main(c_argc: i32, c_argv: **u8, c_envp: *?*u8) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} - const envp = @ptrCast(&&u8, c_envp)[0..env_count]; + const envp = @ptrCast(**u8, c_envp)[0..env_count]; return callMainWithArgs(usize(c_argc), c_argv, envp); } diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig index 1c06c93cdc..1e3eb01136 100644 --- a/std/special/build_file_template.zig +++ b/std/special/build_file_template.zig @@ -1,10 +1,10 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const mode = b.standardReleaseOptions(); const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig"); exe.setBuildMode(mode); - b.default_step.dependOn(&exe.step); + b.default_step.dependOn(*exe.step); b.installArtifact(exe); } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 3ff11bbee4..3471d6ed21 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -129,7 +129,7 @@ pub fn main() !void { }; } -fn runBuild(builder: &Builder) error!void { +fn runBuild(builder: *Builder) error!void { switch (@typeId(@typeOf(root.build).ReturnType)) { builtin.TypeId.Void => root.build(builder), builtin.TypeId.ErrorUnion => try root.build(builder), @@ -137,7 +137,7 @@ fn runBuild(builder: &Builder) error!void { } } -fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void { +fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); @@ -195,7 +195,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void { ); } -fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: var) error { +fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: var) error { usage(builder, already_ran_build, out_stream) catch {}; return error.InvalidArgs; } diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 63149d5161..9c9cd35103 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { if (builtin.is_test) { @setCold(true); @import("std").debug.panic("{}", msg); @@ -14,7 +14,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn } } -export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 { +export fn memset(dest: ?*u8, c: u8, n: usize) ?*u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -24,7 +24,7 @@ export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 { return dest; } -export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 { +export fn memcpy(noalias dest: ?*u8, noalias src: ?*const u8, n: usize) ?*u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -34,7 +34,7 @@ export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 { return dest; } -export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 { +export fn memmove(dest: ?*u8, src: ?*const u8, n: usize) ?*u8 { @setRuntimeSafety(false); if (@ptrToInt(dest) < @ptrToInt(src)) { diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 3e014d4d16..d328324320 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -78,7 +78,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; // Avoid dragging in the runtime safety mechanisms into this .o file, // unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { std.debug.panic("{}", msg); @@ -284,7 +284,7 @@ nakedcc fn ___chkstk_ms() align(4) void { ); } -extern fn __udivmodsi4(a: u32, b: u32, rem: &u32) u32 { +extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { @setRuntimeSafety(is_test); const d = __udivsi3(a, b); diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 0dee5e45f6..894dd02239 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -7,15 +7,15 @@ const low = switch (builtin.endian) { }; const high = 1 - low; -pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?&DoubleInt) DoubleInt { +pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?*DoubleInt) DoubleInt { @setRuntimeSafety(is_test); const SingleInt = @IntType(false, @divExact(DoubleInt.bit_count, 2)); const SignedDoubleInt = @IntType(true, DoubleInt.bit_count); const Log2SingleInt = @import("std").math.Log2Int(SingleInt); - const n = @ptrCast(&const [2]SingleInt, &a).*; // TODO issue #421 - const d = @ptrCast(&const [2]SingleInt, &b).*; // TODO issue #421 + const n = @ptrCast(*const [2]SingleInt, &a).*; // TODO issue #421 + const d = @ptrCast(*const [2]SingleInt, &b).*; // TODO issue #421 var q: [2]SingleInt = undefined; var r: [2]SingleInt = undefined; var sr: c_uint = undefined; @@ -57,7 +57,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if (maybe_rem) |rem| { r[high] = n[high] % d[high]; r[low] = 0; - rem.* = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 + rem.* = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } return n[high] / d[high]; } @@ -69,7 +69,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: if (maybe_rem) |rem| { r[low] = n[low]; r[high] = n[high] & (d[high] - 1); - rem.* = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 + rem.* = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } return n[high] >> Log2SingleInt(@ctz(d[high])); } @@ -109,7 +109,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: sr = @ctz(d[low]); q[high] = n[high] >> Log2SingleInt(sr); q[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); - return @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 + return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 } // K X // --- @@ -183,13 +183,13 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // r.all -= b; // carry = 1; // } - r_all = @ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 + r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 const s: SignedDoubleInt = SignedDoubleInt(b -% r_all -% 1) >> (DoubleInt.bit_count - 1); carry = u32(s & 1); r_all -= b & @bitCast(DoubleInt, s); - r = @ptrCast(&[2]SingleInt, &r_all).*; // TODO issue #421 + r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421 } - const q_all = ((@ptrCast(&align(@alignOf(SingleInt)) DoubleInt, &q[0]).*) << 1) | carry; // TODO issue #421 + const q_all = ((@ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*) << 1) | carry; // TODO issue #421 if (maybe_rem) |rem| { rem.* = r_all; } diff --git a/std/special/compiler_rt/udivmoddi4.zig b/std/special/compiler_rt/udivmoddi4.zig index 6cc54bb6bf..de86c845e5 100644 --- a/std/special/compiler_rt/udivmoddi4.zig +++ b/std/special/compiler_rt/udivmoddi4.zig @@ -1,7 +1,7 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); -pub extern fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?&u64) u64 { +pub extern fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) u64 { @setRuntimeSafety(builtin.is_test); return udivmod(u64, a, b, maybe_rem); } diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig index 816f82b900..3fa596442f 100644 --- a/std/special/compiler_rt/udivmodti4.zig +++ b/std/special/compiler_rt/udivmodti4.zig @@ -2,12 +2,12 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); const compiler_rt = @import("index.zig"); -pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 { +pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { @setRuntimeSafety(builtin.is_test); return udivmod(u128, a, b, maybe_rem); } -pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void { +pub extern fn __udivmodti4_windows_x86_64(a: *const u128, b: *const u128, maybe_rem: ?*u128) void { @setRuntimeSafety(builtin.is_test); compiler_rt.setXmm0(u128, udivmod(u128, a.*, b.*, maybe_rem)); } diff --git a/std/special/compiler_rt/udivti3.zig b/std/special/compiler_rt/udivti3.zig index ad0f09e733..510e21ac1d 100644 --- a/std/special/compiler_rt/udivti3.zig +++ b/std/special/compiler_rt/udivti3.zig @@ -6,7 +6,7 @@ pub extern fn __udivti3(a: u128, b: u128) u128 { return udivmodti4.__udivmodti4(a, b, null); } -pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void { +pub extern fn __udivti3_windows_x86_64(a: *const u128, b: *const u128) void { @setRuntimeSafety(builtin.is_test); udivmodti4.__udivmodti4_windows_x86_64(a, b, null); } diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig index 11e2955bb3..9551e63a6f 100644 --- a/std/special/compiler_rt/umodti3.zig +++ b/std/special/compiler_rt/umodti3.zig @@ -9,7 +9,7 @@ pub extern fn __umodti3(a: u128, b: u128) u128 { return r; } -pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void { +pub extern fn __umodti3_windows_x86_64(a: *const u128, b: *const u128) void { @setRuntimeSafety(builtin.is_test); compiler_rt.setXmm0(u128, __umodti3(a.*, b.*)); } diff --git a/std/special/panic.zig b/std/special/panic.zig index 8f933ddd97..ca1caea73c 100644 --- a/std/special/panic.zig +++ b/std/special/panic.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const std = @import("std"); -pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn { +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); switch (builtin.os) { // TODO: fix panic in zen. diff --git a/std/unicode.zig b/std/unicode.zig index 36f04778f4..3d1bebdb55 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -196,7 +196,7 @@ pub const Utf8View = struct { } } - pub fn iterator(s: &const Utf8View) Utf8Iterator { + pub fn iterator(s: *const Utf8View) Utf8Iterator { return Utf8Iterator{ .bytes = s.bytes, .i = 0, @@ -208,7 +208,7 @@ const Utf8Iterator = struct { bytes: []const u8, i: usize, - pub fn nextCodepointSlice(it: &Utf8Iterator) ?[]const u8 { + pub fn nextCodepointSlice(it: *Utf8Iterator) ?[]const u8 { if (it.i >= it.bytes.len) { return null; } @@ -219,7 +219,7 @@ const Utf8Iterator = struct { return it.bytes[it.i - cp_len .. it.i]; } - pub fn nextCodepoint(it: &Utf8Iterator) ?u32 { + pub fn nextCodepoint(it: *Utf8Iterator) ?u32 { const slice = it.nextCodepointSlice() ?? return null; switch (slice.len) { diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 56d4f9c393..4d25ceb7db 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -9,26 +9,26 @@ pub const TokenIndex = usize; pub const Tree = struct { source: []const u8, tokens: TokenList, - root_node: &Node.Root, + root_node: *Node.Root, arena_allocator: std.heap.ArenaAllocator, errors: ErrorList, pub const TokenList = SegmentedList(Token, 64); pub const ErrorList = SegmentedList(Error, 0); - pub fn deinit(self: &Tree) void { + pub fn deinit(self: *Tree) void { self.arena_allocator.deinit(); } - pub fn renderError(self: &Tree, parse_error: &Error, stream: var) !void { + pub fn renderError(self: *Tree, parse_error: *Error, stream: var) !void { return parse_error.render(&self.tokens, stream); } - pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 { + pub fn tokenSlice(self: *Tree, token_index: TokenIndex) []const u8 { return self.tokenSlicePtr(self.tokens.at(token_index)); } - pub fn tokenSlicePtr(self: &Tree, token: &const Token) []const u8 { + pub fn tokenSlicePtr(self: *Tree, token: *const Token) []const u8 { return self.source[token.start..token.end]; } @@ -39,7 +39,7 @@ pub const Tree = struct { line_end: usize, }; - pub fn tokenLocationPtr(self: &Tree, start_index: usize, token: &const Token) Location { + pub fn tokenLocationPtr(self: *Tree, start_index: usize, token: *const Token) Location { var loc = Location{ .line = 0, .column = 0, @@ -64,24 +64,24 @@ pub const Tree = struct { return loc; } - pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location { + pub fn tokenLocation(self: *Tree, start_index: usize, token_index: TokenIndex) Location { return self.tokenLocationPtr(start_index, self.tokens.at(token_index)); } - pub fn tokensOnSameLine(self: &Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool { + pub fn tokensOnSameLine(self: *Tree, token1_index: TokenIndex, token2_index: TokenIndex) bool { return self.tokensOnSameLinePtr(self.tokens.at(token1_index), self.tokens.at(token2_index)); } - pub fn tokensOnSameLinePtr(self: &Tree, token1: &const Token, token2: &const Token) bool { + pub fn tokensOnSameLinePtr(self: *Tree, token1: *const Token, token2: *const Token) bool { return mem.indexOfScalar(u8, self.source[token1.end..token2.start], '\n') == null; } - pub fn dump(self: &Tree) void { + pub fn dump(self: *Tree) void { self.root_node.base.dump(0); } /// Skips over comments - pub fn prevToken(self: &Tree, token_index: TokenIndex) TokenIndex { + pub fn prevToken(self: *Tree, token_index: TokenIndex) TokenIndex { var index = token_index - 1; while (self.tokens.at(index).id == Token.Id.LineComment) { index -= 1; @@ -90,7 +90,7 @@ pub const Tree = struct { } /// Skips over comments - pub fn nextToken(self: &Tree, token_index: TokenIndex) TokenIndex { + pub fn nextToken(self: *Tree, token_index: TokenIndex) TokenIndex { var index = token_index + 1; while (self.tokens.at(index).id == Token.Id.LineComment) { index += 1; @@ -120,7 +120,7 @@ pub const Error = union(enum) { ExpectedToken: ExpectedToken, ExpectedCommaOrEnd: ExpectedCommaOrEnd, - pub fn render(self: &const Error, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void { switch (self.*) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), @@ -145,7 +145,7 @@ pub const Error = union(enum) { } } - pub fn loc(self: &const Error) TokenIndex { + pub fn loc(self: *const Error) TokenIndex { switch (self.*) { // TODO https://github.com/ziglang/zig/issues/683 @TagType(Error).InvalidToken => |x| return x.token, @@ -188,17 +188,17 @@ pub const Error = union(enum) { pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); pub const ExpectedCall = struct { - node: &Node, + node: *Node, - pub fn render(self: &const ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedCall, tokens: *Tree.TokenList, stream: var) !void { return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); } }; pub const ExpectedCallOrFnProto = struct { - node: &Node, + node: *Node, - pub fn render(self: &const ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedCallOrFnProto, tokens: *Tree.TokenList, stream: var) !void { return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); } }; @@ -207,7 +207,7 @@ pub const Error = union(enum) { token: TokenIndex, expected_id: @TagType(Token.Id), - pub fn render(self: &const ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedToken, tokens: *Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); } @@ -217,7 +217,7 @@ pub const Error = union(enum) { token: TokenIndex, end_id: @TagType(Token.Id), - pub fn render(self: &const ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ExpectedCommaOrEnd, tokens: *Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); } @@ -229,7 +229,7 @@ pub const Error = union(enum) { token: TokenIndex, - pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void { const token_name = @tagName(tokens.at(self.token).id); return stream.print(msg, token_name); } @@ -242,7 +242,7 @@ pub const Error = union(enum) { token: TokenIndex, - pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { + pub fn render(self: *const ThisError, tokens: *Tree.TokenList, stream: var) !void { return stream.write(msg); } }; @@ -320,14 +320,14 @@ pub const Node = struct { FieldInitializer, }; - pub fn cast(base: &Node, comptime T: type) ?&T { + pub fn cast(base: *Node, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { return @fieldParentPtr(T, "base", base); } return null; } - pub fn iterate(base: &Node, index: usize) ?&Node { + pub fn iterate(base: *Node, index: usize) ?*Node { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -338,7 +338,7 @@ pub const Node = struct { unreachable; } - pub fn firstToken(base: &Node) TokenIndex { + pub fn firstToken(base: *Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -349,7 +349,7 @@ pub const Node = struct { unreachable; } - pub fn lastToken(base: &Node) TokenIndex { + pub fn lastToken(base: *Node) TokenIndex { comptime var i = 0; inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { @@ -370,7 +370,7 @@ pub const Node = struct { unreachable; } - pub fn requireSemiColon(base: &const Node) bool { + pub fn requireSemiColon(base: *const Node) bool { var n = base; while (true) { switch (n.id) { @@ -443,7 +443,7 @@ pub const Node = struct { } } - pub fn dump(self: &Node, indent: usize) void { + pub fn dump(self: *Node, indent: usize) void { { var i: usize = 0; while (i < indent) : (i += 1) { @@ -460,44 +460,44 @@ pub const Node = struct { pub const Root = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, decls: DeclList, eof_token: TokenIndex, - pub const DeclList = SegmentedList(&Node, 4); + pub const DeclList = SegmentedList(*Node, 4); - pub fn iterate(self: &Root, index: usize) ?&Node { + pub fn iterate(self: *Root, index: usize) ?*Node { if (index < self.decls.len) { return self.decls.at(index).*; } return null; } - pub fn firstToken(self: &Root) TokenIndex { + pub fn firstToken(self: *Root) TokenIndex { return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken(); } - pub fn lastToken(self: &Root) TokenIndex { + pub fn lastToken(self: *Root) TokenIndex { return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken(); } }; pub const VarDecl = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, name_token: TokenIndex, eq_token: TokenIndex, mut_token: TokenIndex, comptime_token: ?TokenIndex, extern_export_token: ?TokenIndex, - lib_name: ?&Node, - type_node: ?&Node, - align_node: ?&Node, - init_node: ?&Node, + lib_name: ?*Node, + type_node: ?*Node, + align_node: ?*Node, + init_node: ?*Node, semicolon_token: TokenIndex, - pub fn iterate(self: &VarDecl, index: usize) ?&Node { + pub fn iterate(self: *VarDecl, index: usize) ?*Node { var i = index; if (self.type_node) |type_node| { @@ -518,7 +518,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &VarDecl) TokenIndex { + pub fn firstToken(self: *VarDecl) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.comptime_token) |comptime_token| return comptime_token; if (self.extern_export_token) |extern_export_token| return extern_export_token; @@ -526,20 +526,20 @@ pub const Node = struct { return self.mut_token; } - pub fn lastToken(self: &VarDecl) TokenIndex { + pub fn lastToken(self: *VarDecl) TokenIndex { return self.semicolon_token; } }; pub const Use = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, use_token: TokenIndex, - expr: &Node, + expr: *Node, semicolon_token: TokenIndex, - pub fn iterate(self: &Use, index: usize) ?&Node { + pub fn iterate(self: *Use, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -548,12 +548,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Use) TokenIndex { + pub fn firstToken(self: *Use) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.use_token; } - pub fn lastToken(self: &Use) TokenIndex { + pub fn lastToken(self: *Use) TokenIndex { return self.semicolon_token; } }; @@ -564,9 +564,9 @@ pub const Node = struct { decls: DeclList, rbrace_token: TokenIndex, - pub const DeclList = SegmentedList(&Node, 2); + pub const DeclList = SegmentedList(*Node, 2); - pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node { + pub fn iterate(self: *ErrorSetDecl, index: usize) ?*Node { var i = index; if (i < self.decls.len) return self.decls.at(i).*; @@ -575,11 +575,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ErrorSetDecl) TokenIndex { + pub fn firstToken(self: *ErrorSetDecl) TokenIndex { return self.error_token; } - pub fn lastToken(self: &ErrorSetDecl) TokenIndex { + pub fn lastToken(self: *ErrorSetDecl) TokenIndex { return self.rbrace_token; } }; @@ -597,11 +597,11 @@ pub const Node = struct { const InitArg = union(enum) { None, - Enum: ?&Node, - Type: &Node, + Enum: ?*Node, + Type: *Node, }; - pub fn iterate(self: &ContainerDecl, index: usize) ?&Node { + pub fn iterate(self: *ContainerDecl, index: usize) ?*Node { var i = index; switch (self.init_arg_expr) { @@ -618,26 +618,26 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ContainerDecl) TokenIndex { + pub fn firstToken(self: *ContainerDecl) TokenIndex { if (self.layout_token) |layout_token| { return layout_token; } return self.kind_token; } - pub fn lastToken(self: &ContainerDecl) TokenIndex { + pub fn lastToken(self: *ContainerDecl) TokenIndex { return self.rbrace_token; } }; pub const StructField = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, name_token: TokenIndex, - type_expr: &Node, + type_expr: *Node, - pub fn iterate(self: &StructField, index: usize) ?&Node { + pub fn iterate(self: *StructField, index: usize) ?*Node { var i = index; if (i < 1) return self.type_expr; @@ -646,24 +646,24 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &StructField) TokenIndex { + pub fn firstToken(self: *StructField) TokenIndex { if (self.visib_token) |visib_token| return visib_token; return self.name_token; } - pub fn lastToken(self: &StructField) TokenIndex { + pub fn lastToken(self: *StructField) TokenIndex { return self.type_expr.lastToken(); } }; pub const UnionTag = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, name_token: TokenIndex, - type_expr: ?&Node, - value_expr: ?&Node, + type_expr: ?*Node, + value_expr: ?*Node, - pub fn iterate(self: &UnionTag, index: usize) ?&Node { + pub fn iterate(self: *UnionTag, index: usize) ?*Node { var i = index; if (self.type_expr) |type_expr| { @@ -679,11 +679,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &UnionTag) TokenIndex { + pub fn firstToken(self: *UnionTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &UnionTag) TokenIndex { + pub fn lastToken(self: *UnionTag) TokenIndex { if (self.value_expr) |value_expr| { return value_expr.lastToken(); } @@ -697,11 +697,11 @@ pub const Node = struct { pub const EnumTag = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, name_token: TokenIndex, - value: ?&Node, + value: ?*Node, - pub fn iterate(self: &EnumTag, index: usize) ?&Node { + pub fn iterate(self: *EnumTag, index: usize) ?*Node { var i = index; if (self.value) |value| { @@ -712,11 +712,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &EnumTag) TokenIndex { + pub fn firstToken(self: *EnumTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &EnumTag) TokenIndex { + pub fn lastToken(self: *EnumTag) TokenIndex { if (self.value) |value| { return value.lastToken(); } @@ -727,25 +727,25 @@ pub const Node = struct { pub const ErrorTag = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, name_token: TokenIndex, - pub fn iterate(self: &ErrorTag, index: usize) ?&Node { + pub fn iterate(self: *ErrorTag, index: usize) ?*Node { var i = index; if (self.doc_comments) |comments| { - if (i < 1) return &comments.base; + if (i < 1) return *comments.base; i -= 1; } return null; } - pub fn firstToken(self: &ErrorTag) TokenIndex { + pub fn firstToken(self: *ErrorTag) TokenIndex { return self.name_token; } - pub fn lastToken(self: &ErrorTag) TokenIndex { + pub fn lastToken(self: *ErrorTag) TokenIndex { return self.name_token; } }; @@ -754,15 +754,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &Identifier, index: usize) ?&Node { + pub fn iterate(self: *Identifier, index: usize) ?*Node { return null; } - pub fn firstToken(self: &Identifier) TokenIndex { + pub fn firstToken(self: *Identifier) TokenIndex { return self.token; } - pub fn lastToken(self: &Identifier) TokenIndex { + pub fn lastToken(self: *Identifier) TokenIndex { return self.token; } }; @@ -770,10 +770,10 @@ pub const Node = struct { pub const AsyncAttribute = struct { base: Node, async_token: TokenIndex, - allocator_type: ?&Node, + allocator_type: ?*Node, rangle_bracket: ?TokenIndex, - pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node { + pub fn iterate(self: *AsyncAttribute, index: usize) ?*Node { var i = index; if (self.allocator_type) |allocator_type| { @@ -784,11 +784,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsyncAttribute) TokenIndex { + pub fn firstToken(self: *AsyncAttribute) TokenIndex { return self.async_token; } - pub fn lastToken(self: &AsyncAttribute) TokenIndex { + pub fn lastToken(self: *AsyncAttribute) TokenIndex { if (self.rangle_bracket) |rangle_bracket| { return rangle_bracket; } @@ -799,7 +799,7 @@ pub const Node = struct { pub const FnProto = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, visib_token: ?TokenIndex, fn_token: TokenIndex, name_token: ?TokenIndex, @@ -808,19 +808,19 @@ pub const Node = struct { var_args_token: ?TokenIndex, extern_export_inline_token: ?TokenIndex, cc_token: ?TokenIndex, - async_attr: ?&AsyncAttribute, - body_node: ?&Node, - lib_name: ?&Node, // populated if this is an extern declaration - align_expr: ?&Node, // populated if align(A) is present + async_attr: ?*AsyncAttribute, + body_node: ?*Node, + lib_name: ?*Node, // populated if this is an extern declaration + align_expr: ?*Node, // populated if align(A) is present - pub const ParamList = SegmentedList(&Node, 2); + pub const ParamList = SegmentedList(*Node, 2); pub const ReturnType = union(enum) { - Explicit: &Node, - InferErrorSet: &Node, + Explicit: *Node, + InferErrorSet: *Node, }; - pub fn iterate(self: &FnProto, index: usize) ?&Node { + pub fn iterate(self: *FnProto, index: usize) ?*Node { var i = index; if (self.lib_name) |lib_name| { @@ -856,7 +856,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FnProto) TokenIndex { + pub fn firstToken(self: *FnProto) TokenIndex { if (self.visib_token) |visib_token| return visib_token; if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token; assert(self.lib_name == null); @@ -864,7 +864,7 @@ pub const Node = struct { return self.fn_token; } - pub fn lastToken(self: &FnProto) TokenIndex { + pub fn lastToken(self: *FnProto) TokenIndex { if (self.body_node) |body_node| return body_node.lastToken(); switch (self.return_type) { // TODO allow this and next prong to share bodies since the types are the same @@ -881,10 +881,10 @@ pub const Node = struct { pub const Result = struct { arrow_token: TokenIndex, - return_type: &Node, + return_type: *Node, }; - pub fn iterate(self: &PromiseType, index: usize) ?&Node { + pub fn iterate(self: *PromiseType, index: usize) ?*Node { var i = index; if (self.result) |result| { @@ -895,11 +895,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PromiseType) TokenIndex { + pub fn firstToken(self: *PromiseType) TokenIndex { return self.promise_token; } - pub fn lastToken(self: &PromiseType) TokenIndex { + pub fn lastToken(self: *PromiseType) TokenIndex { if (self.result) |result| return result.return_type.lastToken(); return self.promise_token; } @@ -910,10 +910,10 @@ pub const Node = struct { comptime_token: ?TokenIndex, noalias_token: ?TokenIndex, name_token: ?TokenIndex, - type_node: &Node, + type_node: *Node, var_args_token: ?TokenIndex, - pub fn iterate(self: &ParamDecl, index: usize) ?&Node { + pub fn iterate(self: *ParamDecl, index: usize) ?*Node { var i = index; if (i < 1) return self.type_node; @@ -922,14 +922,14 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ParamDecl) TokenIndex { + pub fn firstToken(self: *ParamDecl) TokenIndex { if (self.comptime_token) |comptime_token| return comptime_token; if (self.noalias_token) |noalias_token| return noalias_token; if (self.name_token) |name_token| return name_token; return self.type_node.firstToken(); } - pub fn lastToken(self: &ParamDecl) TokenIndex { + pub fn lastToken(self: *ParamDecl) TokenIndex { if (self.var_args_token) |var_args_token| return var_args_token; return self.type_node.lastToken(); } @@ -944,7 +944,7 @@ pub const Node = struct { pub const StatementList = Root.DeclList; - pub fn iterate(self: &Block, index: usize) ?&Node { + pub fn iterate(self: *Block, index: usize) ?*Node { var i = index; if (i < self.statements.len) return self.statements.at(i).*; @@ -953,7 +953,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Block) TokenIndex { + pub fn firstToken(self: *Block) TokenIndex { if (self.label) |label| { return label; } @@ -961,7 +961,7 @@ pub const Node = struct { return self.lbrace; } - pub fn lastToken(self: &Block) TokenIndex { + pub fn lastToken(self: *Block) TokenIndex { return self.rbrace; } }; @@ -970,14 +970,14 @@ pub const Node = struct { base: Node, defer_token: TokenIndex, kind: Kind, - expr: &Node, + expr: *Node, const Kind = enum { Error, Unconditional, }; - pub fn iterate(self: &Defer, index: usize) ?&Node { + pub fn iterate(self: *Defer, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -986,22 +986,22 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Defer) TokenIndex { + pub fn firstToken(self: *Defer) TokenIndex { return self.defer_token; } - pub fn lastToken(self: &Defer) TokenIndex { + pub fn lastToken(self: *Defer) TokenIndex { return self.expr.lastToken(); } }; pub const Comptime = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, comptime_token: TokenIndex, - expr: &Node, + expr: *Node, - pub fn iterate(self: &Comptime, index: usize) ?&Node { + pub fn iterate(self: *Comptime, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1010,11 +1010,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Comptime) TokenIndex { + pub fn firstToken(self: *Comptime) TokenIndex { return self.comptime_token; } - pub fn lastToken(self: &Comptime) TokenIndex { + pub fn lastToken(self: *Comptime) TokenIndex { return self.expr.lastToken(); } }; @@ -1022,10 +1022,10 @@ pub const Node = struct { pub const Payload = struct { base: Node, lpipe: TokenIndex, - error_symbol: &Node, + error_symbol: *Node, rpipe: TokenIndex, - pub fn iterate(self: &Payload, index: usize) ?&Node { + pub fn iterate(self: *Payload, index: usize) ?*Node { var i = index; if (i < 1) return self.error_symbol; @@ -1034,11 +1034,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Payload) TokenIndex { + pub fn firstToken(self: *Payload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &Payload) TokenIndex { + pub fn lastToken(self: *Payload) TokenIndex { return self.rpipe; } }; @@ -1047,10 +1047,10 @@ pub const Node = struct { base: Node, lpipe: TokenIndex, ptr_token: ?TokenIndex, - value_symbol: &Node, + value_symbol: *Node, rpipe: TokenIndex, - pub fn iterate(self: &PointerPayload, index: usize) ?&Node { + pub fn iterate(self: *PointerPayload, index: usize) ?*Node { var i = index; if (i < 1) return self.value_symbol; @@ -1059,11 +1059,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerPayload) TokenIndex { + pub fn firstToken(self: *PointerPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerPayload) TokenIndex { + pub fn lastToken(self: *PointerPayload) TokenIndex { return self.rpipe; } }; @@ -1072,11 +1072,11 @@ pub const Node = struct { base: Node, lpipe: TokenIndex, ptr_token: ?TokenIndex, - value_symbol: &Node, - index_symbol: ?&Node, + value_symbol: *Node, + index_symbol: ?*Node, rpipe: TokenIndex, - pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node { + pub fn iterate(self: *PointerIndexPayload, index: usize) ?*Node { var i = index; if (i < 1) return self.value_symbol; @@ -1090,11 +1090,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PointerIndexPayload) TokenIndex { + pub fn firstToken(self: *PointerIndexPayload) TokenIndex { return self.lpipe; } - pub fn lastToken(self: &PointerIndexPayload) TokenIndex { + pub fn lastToken(self: *PointerIndexPayload) TokenIndex { return self.rpipe; } }; @@ -1102,10 +1102,10 @@ pub const Node = struct { pub const Else = struct { base: Node, else_token: TokenIndex, - payload: ?&Node, - body: &Node, + payload: ?*Node, + body: *Node, - pub fn iterate(self: &Else, index: usize) ?&Node { + pub fn iterate(self: *Else, index: usize) ?*Node { var i = index; if (self.payload) |payload| { @@ -1119,11 +1119,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Else) TokenIndex { + pub fn firstToken(self: *Else) TokenIndex { return self.else_token; } - pub fn lastToken(self: &Else) TokenIndex { + pub fn lastToken(self: *Else) TokenIndex { return self.body.lastToken(); } }; @@ -1131,15 +1131,15 @@ pub const Node = struct { pub const Switch = struct { base: Node, switch_token: TokenIndex, - expr: &Node, + expr: *Node, /// these must be SwitchCase nodes cases: CaseList, rbrace: TokenIndex, - pub const CaseList = SegmentedList(&Node, 2); + pub const CaseList = SegmentedList(*Node, 2); - pub fn iterate(self: &Switch, index: usize) ?&Node { + pub fn iterate(self: *Switch, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1151,11 +1151,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Switch) TokenIndex { + pub fn firstToken(self: *Switch) TokenIndex { return self.switch_token; } - pub fn lastToken(self: &Switch) TokenIndex { + pub fn lastToken(self: *Switch) TokenIndex { return self.rbrace; } }; @@ -1164,12 +1164,12 @@ pub const Node = struct { base: Node, items: ItemList, arrow_token: TokenIndex, - payload: ?&Node, - expr: &Node, + payload: ?*Node, + expr: *Node, - pub const ItemList = SegmentedList(&Node, 1); + pub const ItemList = SegmentedList(*Node, 1); - pub fn iterate(self: &SwitchCase, index: usize) ?&Node { + pub fn iterate(self: *SwitchCase, index: usize) ?*Node { var i = index; if (i < self.items.len) return self.items.at(i).*; @@ -1186,11 +1186,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SwitchCase) TokenIndex { + pub fn firstToken(self: *SwitchCase) TokenIndex { return (self.items.at(0).*).firstToken(); } - pub fn lastToken(self: &SwitchCase) TokenIndex { + pub fn lastToken(self: *SwitchCase) TokenIndex { return self.expr.lastToken(); } }; @@ -1199,15 +1199,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &SwitchElse, index: usize) ?&Node { + pub fn iterate(self: *SwitchElse, index: usize) ?*Node { return null; } - pub fn firstToken(self: &SwitchElse) TokenIndex { + pub fn firstToken(self: *SwitchElse) TokenIndex { return self.token; } - pub fn lastToken(self: &SwitchElse) TokenIndex { + pub fn lastToken(self: *SwitchElse) TokenIndex { return self.token; } }; @@ -1217,13 +1217,13 @@ pub const Node = struct { label: ?TokenIndex, inline_token: ?TokenIndex, while_token: TokenIndex, - condition: &Node, - payload: ?&Node, - continue_expr: ?&Node, - body: &Node, - @"else": ?&Else, + condition: *Node, + payload: ?*Node, + continue_expr: ?*Node, + body: *Node, + @"else": ?*Else, - pub fn iterate(self: &While, index: usize) ?&Node { + pub fn iterate(self: *While, index: usize) ?*Node { var i = index; if (i < 1) return self.condition; @@ -1243,14 +1243,14 @@ pub const Node = struct { i -= 1; if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; + if (i < 1) return *@"else".base; i -= 1; } return null; } - pub fn firstToken(self: &While) TokenIndex { + pub fn firstToken(self: *While) TokenIndex { if (self.label) |label| { return label; } @@ -1262,7 +1262,7 @@ pub const Node = struct { return self.while_token; } - pub fn lastToken(self: &While) TokenIndex { + pub fn lastToken(self: *While) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1276,12 +1276,12 @@ pub const Node = struct { label: ?TokenIndex, inline_token: ?TokenIndex, for_token: TokenIndex, - array_expr: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&Else, + array_expr: *Node, + payload: ?*Node, + body: *Node, + @"else": ?*Else, - pub fn iterate(self: &For, index: usize) ?&Node { + pub fn iterate(self: *For, index: usize) ?*Node { var i = index; if (i < 1) return self.array_expr; @@ -1296,14 +1296,14 @@ pub const Node = struct { i -= 1; if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; + if (i < 1) return *@"else".base; i -= 1; } return null; } - pub fn firstToken(self: &For) TokenIndex { + pub fn firstToken(self: *For) TokenIndex { if (self.label) |label| { return label; } @@ -1315,7 +1315,7 @@ pub const Node = struct { return self.for_token; } - pub fn lastToken(self: &For) TokenIndex { + pub fn lastToken(self: *For) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1327,12 +1327,12 @@ pub const Node = struct { pub const If = struct { base: Node, if_token: TokenIndex, - condition: &Node, - payload: ?&Node, - body: &Node, - @"else": ?&Else, + condition: *Node, + payload: ?*Node, + body: *Node, + @"else": ?*Else, - pub fn iterate(self: &If, index: usize) ?&Node { + pub fn iterate(self: *If, index: usize) ?*Node { var i = index; if (i < 1) return self.condition; @@ -1347,18 +1347,18 @@ pub const Node = struct { i -= 1; if (self.@"else") |@"else"| { - if (i < 1) return &@"else".base; + if (i < 1) return *@"else".base; i -= 1; } return null; } - pub fn firstToken(self: &If) TokenIndex { + pub fn firstToken(self: *If) TokenIndex { return self.if_token; } - pub fn lastToken(self: &If) TokenIndex { + pub fn lastToken(self: *If) TokenIndex { if (self.@"else") |@"else"| { return @"else".body.lastToken(); } @@ -1370,9 +1370,9 @@ pub const Node = struct { pub const InfixOp = struct { base: Node, op_token: TokenIndex, - lhs: &Node, + lhs: *Node, op: Op, - rhs: &Node, + rhs: *Node, pub const Op = union(enum) { Add, @@ -1401,7 +1401,7 @@ pub const Node = struct { BitXor, BoolAnd, BoolOr, - Catch: ?&Node, + Catch: ?*Node, Div, EqualEqual, ErrorUnion, @@ -1420,7 +1420,7 @@ pub const Node = struct { UnwrapMaybe, }; - pub fn iterate(self: &InfixOp, index: usize) ?&Node { + pub fn iterate(self: *InfixOp, index: usize) ?*Node { var i = index; if (i < 1) return self.lhs; @@ -1485,11 +1485,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &InfixOp) TokenIndex { + pub fn firstToken(self: *InfixOp) TokenIndex { return self.lhs.firstToken(); } - pub fn lastToken(self: &InfixOp) TokenIndex { + pub fn lastToken(self: *InfixOp) TokenIndex { return self.rhs.lastToken(); } }; @@ -1498,11 +1498,11 @@ pub const Node = struct { base: Node, op_token: TokenIndex, op: Op, - rhs: &Node, + rhs: *Node, pub const Op = union(enum) { AddrOf: AddrOfInfo, - ArrayType: &Node, + ArrayType: *Node, Await, BitNot, BoolNot, @@ -1523,17 +1523,17 @@ pub const Node = struct { volatile_token: ?TokenIndex, pub const Align = struct { - node: &Node, + node: *Node, bit_range: ?BitRange, pub const BitRange = struct { - start: &Node, - end: &Node, + start: *Node, + end: *Node, }; }; }; - pub fn iterate(self: &PrefixOp, index: usize) ?&Node { + pub fn iterate(self: *PrefixOp, index: usize) ?*Node { var i = index; switch (self.op) { @@ -1573,11 +1573,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &PrefixOp) TokenIndex { + pub fn firstToken(self: *PrefixOp) TokenIndex { return self.op_token; } - pub fn lastToken(self: &PrefixOp) TokenIndex { + pub fn lastToken(self: *PrefixOp) TokenIndex { return self.rhs.lastToken(); } }; @@ -1586,9 +1586,9 @@ pub const Node = struct { base: Node, period_token: TokenIndex, name_token: TokenIndex, - expr: &Node, + expr: *Node, - pub fn iterate(self: &FieldInitializer, index: usize) ?&Node { + pub fn iterate(self: *FieldInitializer, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1597,45 +1597,45 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &FieldInitializer) TokenIndex { + pub fn firstToken(self: *FieldInitializer) TokenIndex { return self.period_token; } - pub fn lastToken(self: &FieldInitializer) TokenIndex { + pub fn lastToken(self: *FieldInitializer) TokenIndex { return self.expr.lastToken(); } }; pub const SuffixOp = struct { base: Node, - lhs: &Node, + lhs: *Node, op: Op, rtoken: TokenIndex, pub const Op = union(enum) { Call: Call, - ArrayAccess: &Node, + ArrayAccess: *Node, Slice: Slice, ArrayInitializer: InitList, StructInitializer: InitList, Deref, - pub const InitList = SegmentedList(&Node, 2); + pub const InitList = SegmentedList(*Node, 2); pub const Call = struct { params: ParamList, - async_attr: ?&AsyncAttribute, + async_attr: ?*AsyncAttribute, - pub const ParamList = SegmentedList(&Node, 2); + pub const ParamList = SegmentedList(*Node, 2); }; pub const Slice = struct { - start: &Node, - end: ?&Node, + start: *Node, + end: ?*Node, }; }; - pub fn iterate(self: &SuffixOp, index: usize) ?&Node { + pub fn iterate(self: *SuffixOp, index: usize) ?*Node { var i = index; if (i < 1) return self.lhs; @@ -1673,7 +1673,7 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &SuffixOp) TokenIndex { + pub fn firstToken(self: *SuffixOp) TokenIndex { switch (self.op) { @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(), else => {}, @@ -1681,7 +1681,7 @@ pub const Node = struct { return self.lhs.firstToken(); } - pub fn lastToken(self: &SuffixOp) TokenIndex { + pub fn lastToken(self: *SuffixOp) TokenIndex { return self.rtoken; } }; @@ -1689,10 +1689,10 @@ pub const Node = struct { pub const GroupedExpression = struct { base: Node, lparen: TokenIndex, - expr: &Node, + expr: *Node, rparen: TokenIndex, - pub fn iterate(self: &GroupedExpression, index: usize) ?&Node { + pub fn iterate(self: *GroupedExpression, index: usize) ?*Node { var i = index; if (i < 1) return self.expr; @@ -1701,11 +1701,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &GroupedExpression) TokenIndex { + pub fn firstToken(self: *GroupedExpression) TokenIndex { return self.lparen; } - pub fn lastToken(self: &GroupedExpression) TokenIndex { + pub fn lastToken(self: *GroupedExpression) TokenIndex { return self.rparen; } }; @@ -1714,15 +1714,15 @@ pub const Node = struct { base: Node, ltoken: TokenIndex, kind: Kind, - rhs: ?&Node, + rhs: ?*Node, const Kind = union(enum) { - Break: ?&Node, - Continue: ?&Node, + Break: ?*Node, + Continue: ?*Node, Return, }; - pub fn iterate(self: &ControlFlowExpression, index: usize) ?&Node { + pub fn iterate(self: *ControlFlowExpression, index: usize) ?*Node { var i = index; switch (self.kind) { @@ -1749,11 +1749,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &ControlFlowExpression) TokenIndex { + pub fn firstToken(self: *ControlFlowExpression) TokenIndex { return self.ltoken; } - pub fn lastToken(self: &ControlFlowExpression) TokenIndex { + pub fn lastToken(self: *ControlFlowExpression) TokenIndex { if (self.rhs) |rhs| { return rhs.lastToken(); } @@ -1780,10 +1780,10 @@ pub const Node = struct { base: Node, label: ?TokenIndex, suspend_token: TokenIndex, - payload: ?&Node, - body: ?&Node, + payload: ?*Node, + body: ?*Node, - pub fn iterate(self: &Suspend, index: usize) ?&Node { + pub fn iterate(self: *Suspend, index: usize) ?*Node { var i = index; if (self.payload) |payload| { @@ -1799,12 +1799,12 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &Suspend) TokenIndex { + pub fn firstToken(self: *Suspend) TokenIndex { if (self.label) |label| return label; return self.suspend_token; } - pub fn lastToken(self: &Suspend) TokenIndex { + pub fn lastToken(self: *Suspend) TokenIndex { if (self.body) |body| { return body.lastToken(); } @@ -1821,15 +1821,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node { + pub fn iterate(self: *IntegerLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &IntegerLiteral) TokenIndex { + pub fn firstToken(self: *IntegerLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &IntegerLiteral) TokenIndex { + pub fn lastToken(self: *IntegerLiteral) TokenIndex { return self.token; } }; @@ -1838,15 +1838,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &FloatLiteral, index: usize) ?&Node { + pub fn iterate(self: *FloatLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &FloatLiteral) TokenIndex { + pub fn firstToken(self: *FloatLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &FloatLiteral) TokenIndex { + pub fn lastToken(self: *FloatLiteral) TokenIndex { return self.token; } }; @@ -1857,9 +1857,9 @@ pub const Node = struct { params: ParamList, rparen_token: TokenIndex, - pub const ParamList = SegmentedList(&Node, 2); + pub const ParamList = SegmentedList(*Node, 2); - pub fn iterate(self: &BuiltinCall, index: usize) ?&Node { + pub fn iterate(self: *BuiltinCall, index: usize) ?*Node { var i = index; if (i < self.params.len) return self.params.at(i).*; @@ -1868,11 +1868,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &BuiltinCall) TokenIndex { + pub fn firstToken(self: *BuiltinCall) TokenIndex { return self.builtin_token; } - pub fn lastToken(self: &BuiltinCall) TokenIndex { + pub fn lastToken(self: *BuiltinCall) TokenIndex { return self.rparen_token; } }; @@ -1881,15 +1881,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &StringLiteral, index: usize) ?&Node { + pub fn iterate(self: *StringLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &StringLiteral) TokenIndex { + pub fn firstToken(self: *StringLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &StringLiteral) TokenIndex { + pub fn lastToken(self: *StringLiteral) TokenIndex { return self.token; } }; @@ -1900,15 +1900,15 @@ pub const Node = struct { pub const LineList = SegmentedList(TokenIndex, 4); - pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node { + pub fn iterate(self: *MultilineStringLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &MultilineStringLiteral) TokenIndex { + pub fn firstToken(self: *MultilineStringLiteral) TokenIndex { return self.lines.at(0).*; } - pub fn lastToken(self: &MultilineStringLiteral) TokenIndex { + pub fn lastToken(self: *MultilineStringLiteral) TokenIndex { return self.lines.at(self.lines.len - 1).*; } }; @@ -1917,15 +1917,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &CharLiteral, index: usize) ?&Node { + pub fn iterate(self: *CharLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &CharLiteral) TokenIndex { + pub fn firstToken(self: *CharLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &CharLiteral) TokenIndex { + pub fn lastToken(self: *CharLiteral) TokenIndex { return self.token; } }; @@ -1934,15 +1934,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &BoolLiteral, index: usize) ?&Node { + pub fn iterate(self: *BoolLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &BoolLiteral) TokenIndex { + pub fn firstToken(self: *BoolLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &BoolLiteral) TokenIndex { + pub fn lastToken(self: *BoolLiteral) TokenIndex { return self.token; } }; @@ -1951,15 +1951,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &NullLiteral, index: usize) ?&Node { + pub fn iterate(self: *NullLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &NullLiteral) TokenIndex { + pub fn firstToken(self: *NullLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &NullLiteral) TokenIndex { + pub fn lastToken(self: *NullLiteral) TokenIndex { return self.token; } }; @@ -1968,15 +1968,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node { + pub fn iterate(self: *UndefinedLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &UndefinedLiteral) TokenIndex { + pub fn firstToken(self: *UndefinedLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &UndefinedLiteral) TokenIndex { + pub fn lastToken(self: *UndefinedLiteral) TokenIndex { return self.token; } }; @@ -1985,15 +1985,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &ThisLiteral, index: usize) ?&Node { + pub fn iterate(self: *ThisLiteral, index: usize) ?*Node { return null; } - pub fn firstToken(self: &ThisLiteral) TokenIndex { + pub fn firstToken(self: *ThisLiteral) TokenIndex { return self.token; } - pub fn lastToken(self: &ThisLiteral) TokenIndex { + pub fn lastToken(self: *ThisLiteral) TokenIndex { return self.token; } }; @@ -2001,17 +2001,17 @@ pub const Node = struct { pub const AsmOutput = struct { base: Node, lbracket: TokenIndex, - symbolic_name: &Node, - constraint: &Node, + symbolic_name: *Node, + constraint: *Node, kind: Kind, rparen: TokenIndex, const Kind = union(enum) { - Variable: &Identifier, - Return: &Node, + Variable: *Identifier, + Return: *Node, }; - pub fn iterate(self: &AsmOutput, index: usize) ?&Node { + pub fn iterate(self: *AsmOutput, index: usize) ?*Node { var i = index; if (i < 1) return self.symbolic_name; @@ -2022,7 +2022,7 @@ pub const Node = struct { switch (self.kind) { Kind.Variable => |variable_name| { - if (i < 1) return &variable_name.base; + if (i < 1) return *variable_name.base; i -= 1; }, Kind.Return => |return_type| { @@ -2034,11 +2034,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmOutput) TokenIndex { + pub fn firstToken(self: *AsmOutput) TokenIndex { return self.lbracket; } - pub fn lastToken(self: &AsmOutput) TokenIndex { + pub fn lastToken(self: *AsmOutput) TokenIndex { return self.rparen; } }; @@ -2046,12 +2046,12 @@ pub const Node = struct { pub const AsmInput = struct { base: Node, lbracket: TokenIndex, - symbolic_name: &Node, - constraint: &Node, - expr: &Node, + symbolic_name: *Node, + constraint: *Node, + expr: *Node, rparen: TokenIndex, - pub fn iterate(self: &AsmInput, index: usize) ?&Node { + pub fn iterate(self: *AsmInput, index: usize) ?*Node { var i = index; if (i < 1) return self.symbolic_name; @@ -2066,11 +2066,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &AsmInput) TokenIndex { + pub fn firstToken(self: *AsmInput) TokenIndex { return self.lbracket; } - pub fn lastToken(self: &AsmInput) TokenIndex { + pub fn lastToken(self: *AsmInput) TokenIndex { return self.rparen; } }; @@ -2079,33 +2079,33 @@ pub const Node = struct { base: Node, asm_token: TokenIndex, volatile_token: ?TokenIndex, - template: &Node, + template: *Node, outputs: OutputList, inputs: InputList, clobbers: ClobberList, rparen: TokenIndex, - const OutputList = SegmentedList(&AsmOutput, 2); - const InputList = SegmentedList(&AsmInput, 2); + const OutputList = SegmentedList(*AsmOutput, 2); + const InputList = SegmentedList(*AsmInput, 2); const ClobberList = SegmentedList(TokenIndex, 2); - pub fn iterate(self: &Asm, index: usize) ?&Node { + pub fn iterate(self: *Asm, index: usize) ?*Node { var i = index; - if (i < self.outputs.len) return &(self.outputs.at(index).*).base; + if (i < self.outputs.len) return *(self.outputs.at(index).*).base; i -= self.outputs.len; - if (i < self.inputs.len) return &(self.inputs.at(index).*).base; + if (i < self.inputs.len) return *(self.inputs.at(index).*).base; i -= self.inputs.len; return null; } - pub fn firstToken(self: &Asm) TokenIndex { + pub fn firstToken(self: *Asm) TokenIndex { return self.asm_token; } - pub fn lastToken(self: &Asm) TokenIndex { + pub fn lastToken(self: *Asm) TokenIndex { return self.rparen; } }; @@ -2114,15 +2114,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &Unreachable, index: usize) ?&Node { + pub fn iterate(self: *Unreachable, index: usize) ?*Node { return null; } - pub fn firstToken(self: &Unreachable) TokenIndex { + pub fn firstToken(self: *Unreachable) TokenIndex { return self.token; } - pub fn lastToken(self: &Unreachable) TokenIndex { + pub fn lastToken(self: *Unreachable) TokenIndex { return self.token; } }; @@ -2131,15 +2131,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &ErrorType, index: usize) ?&Node { + pub fn iterate(self: *ErrorType, index: usize) ?*Node { return null; } - pub fn firstToken(self: &ErrorType) TokenIndex { + pub fn firstToken(self: *ErrorType) TokenIndex { return self.token; } - pub fn lastToken(self: &ErrorType) TokenIndex { + pub fn lastToken(self: *ErrorType) TokenIndex { return self.token; } }; @@ -2148,15 +2148,15 @@ pub const Node = struct { base: Node, token: TokenIndex, - pub fn iterate(self: &VarType, index: usize) ?&Node { + pub fn iterate(self: *VarType, index: usize) ?*Node { return null; } - pub fn firstToken(self: &VarType) TokenIndex { + pub fn firstToken(self: *VarType) TokenIndex { return self.token; } - pub fn lastToken(self: &VarType) TokenIndex { + pub fn lastToken(self: *VarType) TokenIndex { return self.token; } }; @@ -2167,27 +2167,27 @@ pub const Node = struct { pub const LineList = SegmentedList(TokenIndex, 4); - pub fn iterate(self: &DocComment, index: usize) ?&Node { + pub fn iterate(self: *DocComment, index: usize) ?*Node { return null; } - pub fn firstToken(self: &DocComment) TokenIndex { + pub fn firstToken(self: *DocComment) TokenIndex { return self.lines.at(0).*; } - pub fn lastToken(self: &DocComment) TokenIndex { + pub fn lastToken(self: *DocComment) TokenIndex { return self.lines.at(self.lines.len - 1).*; } }; pub const TestDecl = struct { base: Node, - doc_comments: ?&DocComment, + doc_comments: ?*DocComment, test_token: TokenIndex, - name: &Node, - body_node: &Node, + name: *Node, + body_node: *Node, - pub fn iterate(self: &TestDecl, index: usize) ?&Node { + pub fn iterate(self: *TestDecl, index: usize) ?*Node { var i = index; if (i < 1) return self.body_node; @@ -2196,11 +2196,11 @@ pub const Node = struct { return null; } - pub fn firstToken(self: &TestDecl) TokenIndex { + pub fn firstToken(self: *TestDecl) TokenIndex { return self.test_token; } - pub fn lastToken(self: &TestDecl) TokenIndex { + pub fn lastToken(self: *TestDecl) TokenIndex { return self.body_node.lastToken(); } }; diff --git a/std/zig/bench.zig b/std/zig/bench.zig index c3b6b0d3d3..59392889a6 100644 --- a/std/zig/bench.zig +++ b/std/zig/bench.zig @@ -24,15 +24,15 @@ pub fn main() !void { const mb_per_sec = bytes_per_sec / (1024 * 1024); var stdout_file = try std.io.getStdOut(); - const stdout = &std.io.FileOutStream.init(&stdout_file).stream; + const stdout = *std.io.FileOutStream.init(*stdout_file).stream; try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024); } fn testOnce() usize { var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); - var allocator = &fixed_buf_alloc.allocator; + var allocator = *fixed_buf_alloc.allocator; var tokenizer = Tokenizer.init(source); - var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); + var parser = Parser.init(*tokenizer, allocator, "(memory buffer)"); _ = parser.parse() catch @panic("parse failure"); return fixed_buf_alloc.end_index; } diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 05554f5d34..6d29300aed 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -9,7 +9,7 @@ const Error = ast.Error; /// Result should be freed with tree.deinit() when there are /// no more references to any of the tokens or nodes. -pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { +pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { var tree_arena = std.heap.ArenaAllocator.init(allocator); errdefer tree_arena.deinit(); @@ -2754,16 +2754,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree { } const AnnotatedToken = struct { - ptr: &Token, + ptr: *Token, index: TokenIndex, }; const TopLevelDeclCtx = struct { - decls: &ast.Node.Root.DeclList, + decls: *ast.Node.Root.DeclList, visib_token: ?TokenIndex, extern_export_inline_token: ?AnnotatedToken, - lib_name: ?&ast.Node, - comments: ?&ast.Node.DocComment, + lib_name: ?*ast.Node, + comments: ?*ast.Node.DocComment, }; const VarDeclCtx = struct { @@ -2771,21 +2771,21 @@ const VarDeclCtx = struct { visib_token: ?TokenIndex, comptime_token: ?TokenIndex, extern_export_token: ?TokenIndex, - lib_name: ?&ast.Node, - list: &ast.Node.Root.DeclList, - comments: ?&ast.Node.DocComment, + lib_name: ?*ast.Node, + list: *ast.Node.Root.DeclList, + comments: ?*ast.Node.DocComment, }; const TopLevelExternOrFieldCtx = struct { visib_token: TokenIndex, - container_decl: &ast.Node.ContainerDecl, - comments: ?&ast.Node.DocComment, + container_decl: *ast.Node.ContainerDecl, + comments: ?*ast.Node.DocComment, }; const ExternTypeCtx = struct { opt_ctx: OptionalCtx, extern_token: TokenIndex, - comments: ?&ast.Node.DocComment, + comments: ?*ast.Node.DocComment, }; const ContainerKindCtx = struct { @@ -2795,24 +2795,24 @@ const ContainerKindCtx = struct { const ExpectTokenSave = struct { id: @TagType(Token.Id), - ptr: &TokenIndex, + ptr: *TokenIndex, }; const OptionalTokenSave = struct { id: @TagType(Token.Id), - ptr: &?TokenIndex, + ptr: *?TokenIndex, }; const ExprListCtx = struct { - list: &ast.Node.SuffixOp.Op.InitList, + list: *ast.Node.SuffixOp.Op.InitList, end: Token.Id, - ptr: &TokenIndex, + ptr: *TokenIndex, }; fn ListSave(comptime List: type) type { return struct { - list: &List, - ptr: &TokenIndex, + list: *List, + ptr: *TokenIndex, }; } @@ -2841,7 +2841,7 @@ const LoopCtx = struct { const AsyncEndCtx = struct { ctx: OptionalCtx, - attribute: &ast.Node.AsyncAttribute, + attribute: *ast.Node.AsyncAttribute, }; const ErrorTypeOrSetDeclCtx = struct { @@ -2850,21 +2850,21 @@ const ErrorTypeOrSetDeclCtx = struct { }; const ParamDeclEndCtx = struct { - fn_proto: &ast.Node.FnProto, - param_decl: &ast.Node.ParamDecl, + fn_proto: *ast.Node.FnProto, + param_decl: *ast.Node.ParamDecl, }; const ComptimeStatementCtx = struct { comptime_token: TokenIndex, - block: &ast.Node.Block, + block: *ast.Node.Block, }; const OptionalCtx = union(enum) { - Optional: &?&ast.Node, - RequiredNull: &?&ast.Node, - Required: &&ast.Node, + Optional: *?*ast.Node, + RequiredNull: *?*ast.Node, + Required: **ast.Node, - pub fn store(self: &const OptionalCtx, value: &ast.Node) void { + pub fn store(self: *const OptionalCtx, value: *ast.Node) void { switch (self.*) { OptionalCtx.Optional => |ptr| ptr.* = value, OptionalCtx.RequiredNull => |ptr| ptr.* = value, @@ -2872,7 +2872,7 @@ const OptionalCtx = union(enum) { } } - pub fn get(self: &const OptionalCtx) ?&ast.Node { + pub fn get(self: *const OptionalCtx) ?*ast.Node { switch (self.*) { OptionalCtx.Optional => |ptr| return ptr.*, OptionalCtx.RequiredNull => |ptr| return ??ptr.*, @@ -2880,7 +2880,7 @@ const OptionalCtx = union(enum) { } } - pub fn toRequired(self: &const OptionalCtx) OptionalCtx { + pub fn toRequired(self: *const OptionalCtx) OptionalCtx { switch (self.*) { OptionalCtx.Optional => |ptr| { return OptionalCtx{ .RequiredNull = ptr }; @@ -2892,8 +2892,8 @@ const OptionalCtx = union(enum) { }; const AddCommentsCtx = struct { - node_ptr: &&ast.Node, - comments: ?&ast.Node.DocComment, + node_ptr: **ast.Node, + comments: ?*ast.Node.DocComment, }; const State = union(enum) { @@ -2904,67 +2904,67 @@ const State = union(enum) { TopLevelExternOrField: TopLevelExternOrFieldCtx, ContainerKind: ContainerKindCtx, - ContainerInitArgStart: &ast.Node.ContainerDecl, - ContainerInitArg: &ast.Node.ContainerDecl, - ContainerDecl: &ast.Node.ContainerDecl, + ContainerInitArgStart: *ast.Node.ContainerDecl, + ContainerInitArg: *ast.Node.ContainerDecl, + ContainerDecl: *ast.Node.ContainerDecl, VarDecl: VarDeclCtx, - VarDeclAlign: &ast.Node.VarDecl, - VarDeclEq: &ast.Node.VarDecl, - VarDeclSemiColon: &ast.Node.VarDecl, - - FnDef: &ast.Node.FnProto, - FnProto: &ast.Node.FnProto, - FnProtoAlign: &ast.Node.FnProto, - FnProtoReturnType: &ast.Node.FnProto, - - ParamDecl: &ast.Node.FnProto, - ParamDeclAliasOrComptime: &ast.Node.ParamDecl, - ParamDeclName: &ast.Node.ParamDecl, + VarDeclAlign: *ast.Node.VarDecl, + VarDeclEq: *ast.Node.VarDecl, + VarDeclSemiColon: *ast.Node.VarDecl, + + FnDef: *ast.Node.FnProto, + FnProto: *ast.Node.FnProto, + FnProtoAlign: *ast.Node.FnProto, + FnProtoReturnType: *ast.Node.FnProto, + + ParamDecl: *ast.Node.FnProto, + ParamDeclAliasOrComptime: *ast.Node.ParamDecl, + ParamDeclName: *ast.Node.ParamDecl, ParamDeclEnd: ParamDeclEndCtx, - ParamDeclComma: &ast.Node.FnProto, + ParamDeclComma: *ast.Node.FnProto, MaybeLabeledExpression: MaybeLabeledExpressionCtx, LabeledExpression: LabelCtx, Inline: InlineCtx, While: LoopCtx, - WhileContinueExpr: &?&ast.Node, + WhileContinueExpr: *?*ast.Node, For: LoopCtx, - Else: &?&ast.Node.Else, + Else: *?*ast.Node.Else, - Block: &ast.Node.Block, - Statement: &ast.Node.Block, + Block: *ast.Node.Block, + Statement: *ast.Node.Block, ComptimeStatement: ComptimeStatementCtx, - Semicolon: &&ast.Node, + Semicolon: **ast.Node, - AsmOutputItems: &ast.Node.Asm.OutputList, - AsmOutputReturnOrType: &ast.Node.AsmOutput, - AsmInputItems: &ast.Node.Asm.InputList, - AsmClobberItems: &ast.Node.Asm.ClobberList, + AsmOutputItems: *ast.Node.Asm.OutputList, + AsmOutputReturnOrType: *ast.Node.AsmOutput, + AsmInputItems: *ast.Node.Asm.InputList, + AsmClobberItems: *ast.Node.Asm.ClobberList, ExprListItemOrEnd: ExprListCtx, ExprListCommaOrEnd: ExprListCtx, FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), - FieldListCommaOrEnd: &ast.Node.ContainerDecl, + FieldListCommaOrEnd: *ast.Node.ContainerDecl, FieldInitValue: OptionalCtx, ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList), SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList), - SwitchCaseFirstItem: &ast.Node.SwitchCase, - SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase, - SwitchCaseItemOrEnd: &ast.Node.SwitchCase, + SwitchCaseFirstItem: *ast.Node.SwitchCase, + SwitchCaseItemCommaOrEnd: *ast.Node.SwitchCase, + SwitchCaseItemOrEnd: *ast.Node.SwitchCase, - SuspendBody: &ast.Node.Suspend, - AsyncAllocator: &ast.Node.AsyncAttribute, + SuspendBody: *ast.Node.Suspend, + AsyncAllocator: *ast.Node.AsyncAttribute, AsyncEnd: AsyncEndCtx, ExternType: ExternTypeCtx, - SliceOrArrayAccess: &ast.Node.SuffixOp, - SliceOrArrayType: &ast.Node.PrefixOp, - AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo, - AlignBitRange: &ast.Node.PrefixOp.AddrOfInfo.Align, + SliceOrArrayAccess: *ast.Node.SuffixOp, + SliceOrArrayType: *ast.Node.PrefixOp, + AddrOfModifiers: *ast.Node.PrefixOp.AddrOfInfo, + AlignBitRange: *ast.Node.PrefixOp.AddrOfInfo.Align, Payload: OptionalCtx, PointerPayload: OptionalCtx, @@ -3007,7 +3007,7 @@ const State = union(enum) { ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, StringLiteral: OptionalCtx, Identifier: OptionalCtx, - ErrorTag: &&ast.Node, + ErrorTag: **ast.Node, IfToken: @TagType(Token.Id), IfTokenSave: ExpectTokenSave, @@ -3016,7 +3016,7 @@ const State = union(enum) { OptionalTokenSave: OptionalTokenSave, }; -fn pushDocComment(arena: &mem.Allocator, line_comment: TokenIndex, result: &?&ast.Node.DocComment) !void { +fn pushDocComment(arena: *mem.Allocator, line_comment: TokenIndex, result: *?*ast.Node.DocComment) !void { const node = blk: { if (result.*) |comment_node| { break :blk comment_node; @@ -3032,8 +3032,8 @@ fn pushDocComment(arena: &mem.Allocator, line_comment: TokenIndex, result: &?&as try node.lines.push(line_comment); } -fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.DocComment { - var result: ?&ast.Node.DocComment = null; +fn eatDocComments(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) !?*ast.Node.DocComment { + var result: ?*ast.Node.DocComment = null; while (true) { if (eatToken(tok_it, tree, Token.Id.DocComment)) |line_comment| { try pushDocComment(arena, line_comment, &result); @@ -3044,7 +3044,7 @@ fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, t return result; } -fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node { +fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterator, token_ptr: *const Token, token_index: TokenIndex, tree: *ast.Tree) !?*ast.Node { switch (token_ptr.id) { Token.Id.StringLiteral => { return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; @@ -3071,11 +3071,11 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato }, // TODO: We shouldn't need a cast, but: // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed. - else => return (?&ast.Node)(null), + else => return (?*ast.Node)(null), } } -fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token_ptr: &const Token, token_index: TokenIndex) !bool { +fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *const OptionalCtx, token_ptr: *const Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { const node = try arena.construct(ast.Node.Suspend{ @@ -3189,7 +3189,7 @@ const ExpectCommaOrEndResult = union(enum) { parse_error: Error, }; -fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult { +fn expectCommaOrEnd(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult { const token = nextToken(tok_it, tree); const token_index = token.index; const token_ptr = token.ptr; @@ -3212,7 +3212,7 @@ fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: } } -fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op { +fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op { // TODO: We have to cast all cases because of this: // error: expected type '?InfixOp', found '?@TagType(InfixOp)' return switch (id.*) { @@ -3307,21 +3307,21 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { }; } -fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T { +fn createLiteral(arena: *mem.Allocator, comptime T: type, token_index: TokenIndex) !*T { return arena.construct(T{ .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, }); } -fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T { +fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, comptime T: type, token_index: TokenIndex) !*T { const node = try createLiteral(arena, T, token_index); opt_ctx.store(&node.base); return node; } -fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { +fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { const token = ??tok_it.peek(); if (token.id == id) { @@ -3331,7 +3331,7 @@ fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType( return null; } -fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedToken { +fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken { const result = AnnotatedToken{ .index = tok_it.index, .ptr = ??tok_it.next(), @@ -3345,7 +3345,7 @@ fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedTok } } -fn prevToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void { +fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void { while (true) { const prev_tok = tok_it.prev() ?? return; if (prev_tok.id == Token.Id.LineComment) continue; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 69903bc3fd..8507470bcc 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1803,7 +1803,7 @@ const io = std.io; var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn testParse(source: []const u8, allocator: &mem.Allocator, anything_changed: &bool) ![]u8 { +fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { var stderr_file = try io.getStdErr(); var stderr = &io.FileOutStream.init(&stderr_file).stream; diff --git a/std/zig/render.zig b/std/zig/render.zig index ac07917ff1..07e01241b7 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -13,7 +13,7 @@ pub const Error = error{ }; /// Returns whether anything changed -pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf(stream).Child.Error || Error)!bool { +pub fn render(allocator: *mem.Allocator, stream: var, tree: *ast.Tree) (@typeOf(stream).Child.Error || Error)!bool { comptime assert(@typeId(@typeOf(stream)) == builtin.TypeId.Pointer); var anything_changed: bool = false; @@ -24,13 +24,13 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( const StreamError = @typeOf(stream).Child.Error; const Stream = std.io.OutStream(StreamError); - anything_changed_ptr: &bool, + anything_changed_ptr: *bool, child_stream: @typeOf(stream), stream: Stream, source_index: usize, source: []const u8, - fn write(iface_stream: &Stream, bytes: []const u8) StreamError!void { + fn write(iface_stream: *Stream, bytes: []const u8) StreamError!void { const self = @fieldParentPtr(MyStream, "stream", iface_stream); if (!self.anything_changed_ptr.*) { @@ -63,9 +63,9 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) (@typeOf( } fn renderRoot( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, ) (@typeOf(stream).Child.Error || Error)!void { // render all the line comments at the beginning of the file var tok_it = tree.tokens.iterator(0); @@ -90,7 +90,7 @@ fn renderRoot( } } -fn renderExtraNewline(tree: &ast.Tree, stream: var, start_col: &usize, node: &ast.Node) !void { +fn renderExtraNewline(tree: *ast.Tree, stream: var, start_col: *usize, node: *ast.Node) !void { const first_token = node.firstToken(); var prev_token = first_token; while (tree.tokens.at(prev_token - 1).id == Token.Id.DocComment) { @@ -104,7 +104,7 @@ fn renderExtraNewline(tree: &ast.Tree, stream: var, start_col: &usize, node: &as } } -fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, indent: usize, start_col: &usize, decl: &ast.Node) (@typeOf(stream).Child.Error || Error)!void { +fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node) (@typeOf(stream).Child.Error || Error)!void { switch (decl.id) { ast.Node.Id.FnProto => { const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); @@ -214,12 +214,12 @@ fn renderTopLevelDecl(allocator: &mem.Allocator, stream: var, tree: &ast.Tree, i } fn renderExpression( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - base: &ast.Node, + start_col: *usize, + base: *ast.Node, space: Space, ) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { @@ -1640,12 +1640,12 @@ fn renderExpression( } fn renderVarDecl( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - var_decl: &ast.Node.VarDecl, + start_col: *usize, + var_decl: *ast.Node.VarDecl, ) (@typeOf(stream).Child.Error || Error)!void { if (var_decl.visib_token) |visib_token| { try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub @@ -1696,12 +1696,12 @@ fn renderVarDecl( } fn renderParamDecl( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - base: &ast.Node, + start_col: *usize, + base: *ast.Node, space: Space, ) (@typeOf(stream).Child.Error || Error)!void { const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); @@ -1724,12 +1724,12 @@ fn renderParamDecl( } fn renderStatement( - allocator: &mem.Allocator, + allocator: *mem.Allocator, stream: var, - tree: &ast.Tree, + tree: *ast.Tree, indent: usize, - start_col: &usize, - base: &ast.Node, + start_col: *usize, + base: *ast.Node, ) (@typeOf(stream).Child.Error || Error)!void { switch (base.id) { ast.Node.Id.VarDecl => { @@ -1761,7 +1761,7 @@ const Space = enum { BlockStart, }; -fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: &usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { +fn renderToken(tree: *ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, start_col: *usize, space: Space) (@typeOf(stream).Child.Error || Error)!void { if (space == Space.BlockStart) { if (start_col.* < indent + indent_delta) return renderToken(tree, stream, token_index, indent, start_col, Space.Space); @@ -1928,11 +1928,11 @@ fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent } fn renderDocComments( - tree: &ast.Tree, + tree: *ast.Tree, stream: var, node: var, indent: usize, - start_col: &usize, + start_col: *usize, ) (@typeOf(stream).Child.Error || Error)!void { const comment = node.doc_comments ?? return; var it = comment.lines.iterator(0); @@ -1949,7 +1949,7 @@ fn renderDocComments( } } -fn nodeIsBlock(base: &const ast.Node) bool { +fn nodeIsBlock(base: *const ast.Node) bool { return switch (base.id) { ast.Node.Id.Block, ast.Node.Id.If, @@ -1961,7 +1961,7 @@ fn nodeIsBlock(base: &const ast.Node) bool { }; } -fn nodeCausesSliceOpSpace(base: &ast.Node) bool { +fn nodeCausesSliceOpSpace(base: *ast.Node) bool { const infix_op = base.cast(ast.Node.InfixOp) ?? return false; return switch (infix_op.op) { ast.Node.InfixOp.Op.Period => false, diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 7c3b3210fb..8378a9011d 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -200,7 +200,7 @@ pub const Tokenizer = struct { pending_invalid_token: ?Token, /// For debugging purposes - pub fn dump(self: &Tokenizer, token: &const Token) void { + pub fn dump(self: *Tokenizer, token: *const Token) void { std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]); } @@ -265,7 +265,7 @@ pub const Tokenizer = struct { SawAtSign, }; - pub fn next(self: &Tokenizer) Token { + pub fn next(self: *Tokenizer) Token { if (self.pending_invalid_token) |token| { self.pending_invalid_token = null; return token; @@ -1089,7 +1089,7 @@ pub const Tokenizer = struct { return result; } - fn checkLiteralCharacter(self: &Tokenizer) void { + fn checkLiteralCharacter(self: *Tokenizer) void { if (self.pending_invalid_token != null) return; const invalid_length = self.getInvalidCharacterLength(); if (invalid_length == 0) return; @@ -1100,7 +1100,7 @@ pub const Tokenizer = struct { }; } - fn getInvalidCharacterLength(self: &Tokenizer) u3 { + fn getInvalidCharacterLength(self: *Tokenizer) u3 { const c0 = self.buffer[self.index]; if (c0 < 0x80) { if (c0 < 0x20 or c0 == 0x7f) { diff --git a/test/assemble_and_link.zig b/test/assemble_and_link.zig index 2593f3306a..8c727e87b5 100644 --- a/test/assemble_and_link.zig +++ b/test/assemble_and_link.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompareOutputContext) void { +pub fn addCases(cases: *tests.CompareOutputContext) void { if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) { cases.addAsm("hello world linux x86_64", \\.text diff --git a/test/build_examples.zig b/test/build_examples.zig index 7a4c0f35d9..1ba0ca46cf 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); const is_windows = builtin.os == builtin.Os.windows; -pub fn addCases(cases: &tests.BuildExamplesContext) void { +pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.add("example/hello_world/hello.zig"); cases.addC("example/hello_world/hello_libc.zig"); cases.add("example/cat/main.zig"); diff --git a/test/cases/align.zig b/test/cases/align.zig index 582063766f..99bdcdf940 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -5,7 +5,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == &align(4) u8); + assert(@typeOf(&foo) == *align(4) u8); const slice = (&foo)[0..1]; assert(@typeOf(slice) == []align(4) u8); } @@ -30,7 +30,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - assert(@typeOf(&baz.b) == &align(1) u32); + assert(@typeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -40,11 +40,11 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - assert(@typeOf(&blah.b) == &align(1:3:6) const u3); + assert(@typeOf(&blah.b) == *align(1:3:6) const u3); } test "default alignment allows unspecified in type syntax" { - assert(&u32 == &align(@alignOf(u32)) u32); + assert(*u32 == *align(@alignOf(u32)) u32); } test "implicitly decreasing pointer alignment" { @@ -53,7 +53,7 @@ test "implicitly decreasing pointer alignment" { assert(addUnaligned(&a, &b) == 7); } -fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) u32 { +fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { return a.* + b.*; } @@ -76,7 +76,7 @@ fn testBytesAlign(b: u8) void { b, b, }; - const ptr = @ptrCast(&u32, &bytes[0]); + const ptr = @ptrCast(*u32, &bytes[0]); assert(ptr.* == 0x33333333); } @@ -99,10 +99,10 @@ test "@alignCast pointers" { expectsOnly1(&x); assert(x == 2); } -fn expectsOnly1(x: &align(1) u32) void { +fn expectsOnly1(x: *align(1) u32) void { expects4(@alignCast(4, x)); } -fn expects4(x: &align(4) u32) void { +fn expects4(x: *align(4) u32) void { x.* += 1; } @@ -163,8 +163,8 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; - const ptr = @ptrCast(&u8, &x); - assert(@typeOf(ptr) == &align(16) u8); + const ptr = @ptrCast(*u8, &x); + assert(@typeOf(ptr) == *align(16) u8); } test "compile-time known array index has best alignment possible" { @@ -175,10 +175,10 @@ test "compile-time known array index has best alignment possible" { 3, 4, }; - assert(@typeOf(&array[0]) == &align(4) u8); - assert(@typeOf(&array[1]) == &u8); - assert(@typeOf(&array[2]) == &align(2) u8); - assert(@typeOf(&array[3]) == &u8); + assert(@typeOf(&array[0]) == *align(4) u8); + assert(@typeOf(&array[1]) == *u8); + assert(@typeOf(&array[2]) == *align(2) u8); + assert(@typeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 var bigger align(2) = []u64{ @@ -187,10 +187,10 @@ test "compile-time known array index has best alignment possible" { 3, 4, }; - assert(@typeOf(&bigger[0]) == &align(2) u64); - assert(@typeOf(&bigger[1]) == &align(2) u64); - assert(@typeOf(&bigger[2]) == &align(2) u64); - assert(@typeOf(&bigger[3]) == &align(2) u64); + assert(@typeOf(&bigger[0]) == *align(2) u64); + assert(@typeOf(&bigger[1]) == *align(2) u64); + assert(@typeOf(&bigger[2]) == *align(2) u64); + assert(@typeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = []u32{ @@ -199,21 +199,21 @@ test "compile-time known array index has best alignment possible" { 3, 4, }; - testIndex(&smaller[0], 0, &align(2) u32); - testIndex(&smaller[0], 1, &align(2) u32); - testIndex(&smaller[0], 2, &align(2) u32); - testIndex(&smaller[0], 3, &align(2) u32); + testIndex(&smaller[0], 0, *align(2) u32); + testIndex(&smaller[0], 1, *align(2) u32); + testIndex(&smaller[0], 2, *align(2) u32); + testIndex(&smaller[0], 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - testIndex2(&array[0], 0, &u8); - testIndex2(&array[0], 1, &u8); - testIndex2(&array[0], 2, &u8); - testIndex2(&array[0], 3, &u8); + testIndex2(&array[0], 0, *u8); + testIndex2(&array[0], 1, *u8); + testIndex2(&array[0], 2, *u8); + testIndex2(&array[0], 3, *u8); } -fn testIndex(smaller: &align(2) u32, index: usize, comptime T: type) void { +fn testIndex(smaller: *align(2) u32, index: usize, comptime T: type) void { assert(@typeOf(&smaller[index]) == T); } -fn testIndex2(ptr: &align(4) u8, index: usize, comptime T: type) void { +fn testIndex2(ptr: *align(4) u8, index: usize, comptime T: type) void { assert(@typeOf(&ptr[index]) == T); } diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index d406285d29..67c9ab3dd1 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -34,7 +34,7 @@ test "atomicrmw and atomicload" { testAtomicLoad(&data); } -fn testAtomicRmw(ptr: &u8) void { +fn testAtomicRmw(ptr: *u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); assert(prev_value == 200); comptime { @@ -45,7 +45,7 @@ fn testAtomicRmw(ptr: &u8) void { } } -fn testAtomicLoad(ptr: &u8) void { +fn testAtomicLoad(ptr: *u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); assert(x == 42); } @@ -54,18 +54,18 @@ test "cmpxchg with ptr" { var data1: i32 = 1234; var data2: i32 = 5678; var data3: i32 = 9101; - var x: &i32 = &data1; - if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + var x: *i32 = &data1; + if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { assert(x1 == &data1); } else { @panic("cmpxchg should have failed"); } - while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { assert(x1 == &data1); } assert(x == &data3); - assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); assert(x == &data2); } diff --git a/test/cases/bugs/655.zig b/test/cases/bugs/655.zig index 4431767d5c..50374d4e6d 100644 --- a/test/cases/bugs/655.zig +++ b/test/cases/bugs/655.zig @@ -3,10 +3,10 @@ const other_file = @import("655_other_file.zig"); test "function with &const parameter with type dereferenced by namespace" { const x: other_file.Integer = 1234; - comptime std.debug.assert(@typeOf(&x) == &const other_file.Integer); + comptime std.debug.assert(@typeOf(&x) == *const other_file.Integer); foo(x); } -fn foo(x: &const other_file.Integer) void { +fn foo(x: *const other_file.Integer) void { std.debug.assert(x.* == 1234); } diff --git a/test/cases/bugs/828.zig b/test/cases/bugs/828.zig index 10d7370b90..50ae0fd279 100644 --- a/test/cases/bugs/828.zig +++ b/test/cases/bugs/828.zig @@ -3,7 +3,7 @@ const CountBy = struct { const One = CountBy{ .a = 1 }; - pub fn counter(self: &const CountBy) Counter { + pub fn counter(self: *const CountBy) Counter { return Counter{ .i = 0 }; } }; @@ -11,13 +11,13 @@ const CountBy = struct { const Counter = struct { i: usize, - pub fn count(self: &Counter) bool { + pub fn count(self: *Counter) bool { self.i += 1; return self.i <= 10; } }; -fn constCount(comptime cb: &const CountBy, comptime unused: u32) void { +fn constCount(comptime cb: *const CountBy, comptime unused: u32) void { comptime { var cnt = cb.counter(); if (cnt.i != 0) @compileError("Counter instance reused!"); diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig index c315206072..2903f05a29 100644 --- a/test/cases/bugs/920.zig +++ b/test/cases/bugs/920.zig @@ -9,10 +9,10 @@ const ZigTable = struct { pdf: fn (f64) f64, is_symmetric: bool, - zero_case: fn (&Random, f64) f64, + zero_case: fn (*Random, f64) f64, }; -fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (&Random, f64) f64) ZigTable { +fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn (f64) f64, comptime f_inv: fn (f64) f64, comptime zero_case: fn (*Random, f64) f64) ZigTable { var tables: ZigTable = undefined; tables.is_symmetric = is_symmetric; @@ -45,7 +45,7 @@ fn norm_f(x: f64) f64 { fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); } -fn norm_zero_case(random: &Random, u: f64) f64 { +fn norm_zero_case(random: *Random, u: f64) f64 { return 0.0; } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index e37451ea93..7358a4ffd8 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -3,20 +3,20 @@ const mem = @import("std").mem; test "int to ptr cast" { const x = usize(13); - const y = @intToPtr(&u8, x); + const y = @intToPtr(*u8, x); const z = @ptrToInt(y); assert(z == 13); } test "integer literal to pointer cast" { - const vga_mem = @intToPtr(&u16, 0xB8000); + const vga_mem = @intToPtr(*u16, 0xB8000); assert(@ptrToInt(vga_mem) == 0xB8000); } test "pointer reinterpret const float to int" { const float: f64 = 5.99999999999994648725e-01; const float_ptr = &float; - const int_ptr = @ptrCast(&const i32, float_ptr); + const int_ptr = @ptrCast(*const i32, float_ptr); const int_val = int_ptr.*; assert(int_val == 858993411); } @@ -28,7 +28,7 @@ test "implicitly cast a pointer to a const pointer of it" { assert(x == 2); } -fn funcWithConstPtrPtr(x: &const &i32) void { +fn funcWithConstPtrPtr(x: *const *i32) void { x.*.* += 1; } @@ -66,11 +66,11 @@ fn Struct(comptime T: type) type { const Self = this; x: T, - fn pointer(self: &const Self) Self { + fn pointer(self: *const Self) Self { return self.*; } - fn maybePointer(self: ?&const Self) Self { + fn maybePointer(self: ?*const Self) Self { const none = Self{ .x = if (T == void) void{} else 0 }; return (self ?? &none).*; } @@ -80,11 +80,11 @@ fn Struct(comptime T: type) type { const Union = union { x: u8, - fn pointer(self: &const Union) Union { + fn pointer(self: *const Union) Union { return self.*; } - fn maybePointer(self: ?&const Union) Union { + fn maybePointer(self: ?*const Union) Union { const none = Union{ .x = 0 }; return (self ?? &none).*; } @@ -94,11 +94,11 @@ const Enum = enum { None, Some, - fn pointer(self: &const Enum) Enum { + fn pointer(self: *const Enum) Enum { return self.*; } - fn maybePointer(self: ?&const Enum) Enum { + fn maybePointer(self: ?*const Enum) Enum { return (self ?? &Enum.None).*; } }; @@ -107,16 +107,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const S = struct { const Self = this; x: u8, - fn constConst(p: &const &const Self) u8 { + fn constConst(p: *const *const Self) u8 { return (p.*).x; } - fn maybeConstConst(p: ?&const &const Self) u8 { + fn maybeConstConst(p: ?*const *const Self) u8 { return ((??p).*).x; } - fn constConstConst(p: &const &const &const Self) u8 { + fn constConstConst(p: *const *const *const Self) u8 { return (p.*.*).x; } - fn maybeConstConstConst(p: ?&const &const &const Self) u8 { + fn maybeConstConstConst(p: ?*const *const *const Self) u8 { return ((??p).*.*).x; } }; @@ -166,12 +166,12 @@ fn testPeerResolveArrayConstSlice(b: bool) void { } test "integer literal to &const int" { - const x: &const i32 = 3; + const x: *const i32 = 3; assert(x.* == 3); } test "string literal to &const []const u8" { - const x: &const []const u8 = "hello"; + const x: *const []const u8 = "hello"; assert(mem.eql(u8, x.*, "hello")); } @@ -209,11 +209,11 @@ test "return null from fn() error!?&T" { const b = returnNullLitFromMaybeTypeErrorRef(); assert((try a) == null and (try b) == null); } -fn returnNullFromMaybeTypeErrorRef() error!?&A { - const a: ?&A = null; +fn returnNullFromMaybeTypeErrorRef() error!?*A { + const a: ?*A = null; return a; } -fn returnNullLitFromMaybeTypeErrorRef() error!?&A { +fn returnNullLitFromMaybeTypeErrorRef() error!?*A { return null; } @@ -312,7 +312,7 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == &const [4]u8); + assert(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; assert(mem.eql(u8, slice, "aoeu")); } @@ -322,7 +322,7 @@ test "var args implicitly casts by value arg to const ref" { } fn foo(args: ...) void { - assert(@typeOf(args[0]) == &const [5]u8); + assert(@typeOf(args[0]) == *const [5]u8); } test "peer type resolution: error and [N]T" { diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index a92c589186..e012c729a0 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -1,10 +1,10 @@ const debug = @import("std").debug; const assert = debug.assert; -var argv: &const &const u8 = undefined; +var argv: *const *const u8 = undefined; test "const slice child" { - const strs = ([]&const u8){ + const strs = ([]*const u8){ c"one", c"two", c"three", @@ -29,7 +29,7 @@ fn bar(argc: usize) void { foo(args); } -fn strlen(ptr: &const u8) usize { +fn strlen(ptr: *const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 8a071c6aad..4d2aa54a69 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -154,7 +154,7 @@ test "async function with dot syntax" { test "async fn pointer in a struct field" { var data: i32 = 1; const Foo = struct { - bar: async<&std.mem.Allocator> fn (&i32) void, + bar: async<*std.mem.Allocator> fn (*i32) void, }; var foo = Foo{ .bar = simpleAsyncFn2 }; const p = (async foo.bar(&data)) catch unreachable; @@ -162,7 +162,7 @@ test "async fn pointer in a struct field" { cancel p; assert(data == 4); } -async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void { +async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; y.* += 1; suspend; @@ -220,7 +220,7 @@ test "break from suspend" { cancel p; std.debug.assert(my_result == 2); } -async fn testBreakFromSuspend(my_result: &i32) void { +async fn testBreakFromSuspend(my_result: *i32) void { s: suspend |p| { break :s; } diff --git a/test/cases/enum.zig b/test/cases/enum.zig index cbcbc5e306..ae9f04869b 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -56,14 +56,14 @@ test "constant enum with payload" { shouldBeNotEmpty(full); } -fn shouldBeEmpty(x: &const AnEnumWithPayload) void { +fn shouldBeEmpty(x: *const AnEnumWithPayload) void { switch (x.*) { AnEnumWithPayload.Empty => {}, else => unreachable, } } -fn shouldBeNotEmpty(x: &const AnEnumWithPayload) void { +fn shouldBeNotEmpty(x: *const AnEnumWithPayload) void { switch (x.*) { AnEnumWithPayload.Empty => unreachable, else => {}, @@ -750,15 +750,15 @@ test "bit field access with enum fields" { assert(data.b == B.Four3); } -fn getA(data: &const BitFieldOfEnums) A { +fn getA(data: *const BitFieldOfEnums) A { return data.a; } -fn getB(data: &const BitFieldOfEnums) B { +fn getB(data: *const BitFieldOfEnums) B { return data.b; } -fn getC(data: &const BitFieldOfEnums) C { +fn getC(data: *const BitFieldOfEnums) C { return data.c; } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 8fafa70b02..18174186a9 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -6,7 +6,7 @@ const ET = union(enum) { SINT: i32, UINT: u32, - pub fn print(a: &const ET, buf: []u8) error!usize { + pub fn print(a: *const ET, buf: []u8) error!usize { return switch (a.*) { ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 8a6dc25bd8..6c8bcfdbab 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -282,7 +282,7 @@ fn fnWithFloatMode() f32 { const SimpleStruct = struct { field: i32, - fn method(self: &const SimpleStruct) i32 { + fn method(self: *const SimpleStruct) i32 { return self.field + 3; } }; @@ -367,7 +367,7 @@ test "const global shares pointer with other same one" { assertEqualPtrs(&hi1[0], &hi2[0]); comptime assert(&hi1[0] == &hi2[0]); } -fn assertEqualPtrs(ptr1: &const u8, ptr2: &const u8) void { +fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { assert(ptr1 == ptr2); } @@ -418,9 +418,9 @@ test "string literal used as comptime slice is memoized" { } test "comptime slice of undefined pointer of length 0" { - const slice1 = (&i32)(undefined)[0..0]; + const slice1 = (*i32)(undefined)[0..0]; assert(slice1.len == 0); - const slice2 = (&i32)(undefined)[100..100]; + const slice2 = (*i32)(undefined)[100..100]; assert(slice2.len == 0); } @@ -472,7 +472,7 @@ test "comptime function with mutable pointer is not memoized" { } } -fn increment(value: &i32) void { +fn increment(value: *i32) void { value.* += 1; } @@ -517,7 +517,7 @@ test "comptime slice of pointer preserves comptime var" { const SingleFieldStruct = struct { x: i32, - fn read_x(self: &const SingleFieldStruct) i32 { + fn read_x(self: *const SingleFieldStruct) i32 { return self.x; } }; diff --git a/test/cases/field_parent_ptr.zig b/test/cases/field_parent_ptr.zig index 1a7de9ce35..00d4e0f367 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/cases/field_parent_ptr.zig @@ -24,7 +24,7 @@ const foo = Foo{ .d = -10, }; -fn testParentFieldPtr(c: &const i32) void { +fn testParentFieldPtr(c: *const i32) void { assert(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); @@ -32,7 +32,7 @@ fn testParentFieldPtr(c: &const i32) void { assert(&base.c == c); } -fn testParentFieldPtrFirst(a: &const bool) void { +fn testParentFieldPtrFirst(a: *const bool) void { assert(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/cases/fn_in_struct_in_comptime.zig index c22da71940..fabb57e9cb 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/cases/fn_in_struct_in_comptime.zig @@ -1,9 +1,9 @@ const assert = @import("std").debug.assert; -fn get_foo() fn (&u8) usize { +fn get_foo() fn (*u8) usize { comptime { return struct { - fn func(ptr: &u8) usize { + fn func(ptr: *u8) usize { var u = @ptrToInt(ptr); return u; } @@ -13,5 +13,5 @@ fn get_foo() fn (&u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(&u8, 12345)) == 12345); + assert(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 37cd1b89e4..a76990e2a1 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -96,8 +96,8 @@ test "generic struct" { fn GenNode(comptime T: type) type { return struct { value: T, - next: ?&GenNode(T), - fn getVal(n: &const GenNode(T)) T { + next: ?*GenNode(T), + fn getVal(n: *const GenNode(T)) T { return n.value; } }; @@ -126,11 +126,11 @@ test "generic fn with implicit cast" { 13, }) == 0); } -fn getByte(ptr: ?&const u8) u8 { +fn getByte(ptr: ?*const u8) u8 { return (??ptr).*; } fn getFirstByte(comptime T: type, mem: []const T) u8 { - return getByte(@ptrCast(&const u8, &mem[0])); + return getByte(@ptrCast(*const u8, &mem[0])); } const foos = []fn (var) bool{ diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/cases/incomplete_struct_param_tld.zig index a2f57743d0..552d6ef185 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/cases/incomplete_struct_param_tld.zig @@ -11,12 +11,12 @@ const B = struct { const C = struct { x: i32, - fn d(c: &const C) i32 { + fn d(c: *const C) i32 { return c.x; } }; -fn foo(a: &const A) i32 { +fn foo(a: *const A) i32 { return a.b.c.d(); } diff --git a/test/cases/math.zig b/test/cases/math.zig index 0b4622702f..5f16e903b2 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -28,13 +28,13 @@ fn testDivision() void { assert(divTrunc(f32, -5.0, 3.0) == -1.0); comptime { - assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600); - assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600); - assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2); - assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2); - assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2); - assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1); + assert(1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,); + assert(@rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,); + assert(1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,); + assert(@divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,); + assert(@divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,); + assert(4126227191251978491697987544882340798050766755606969681711 % 10 == 1,); } } fn div(comptime T: type, a: T, b: T) T { @@ -324,8 +324,8 @@ test "big number addition" { test "big number multiplication" { comptime { - assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567); - assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016); + assert(45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,); + assert(594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,); } } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index b6b2da8de5..919b978f9f 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -252,20 +252,20 @@ test "multiline C string" { } test "type equality" { - assert(&const u8 != &u8); + assert(*const u8 != *u8); } const global_a: i32 = 1234; -const global_b: &const i32 = &global_a; -const global_c: &const f32 = @ptrCast(&const f32, global_b); +const global_b: *const i32 = &global_a; +const global_c: *const f32 = @ptrCast(*const f32, global_b); test "compile time global reinterpret" { - const d = @ptrCast(&const i32, global_c); + const d = @ptrCast(*const i32, global_c); assert(d.* == 1234); } test "explicit cast maybe pointers" { - const a: ?&i32 = undefined; - const b: ?&f32 = @ptrCast(?&f32, a); + const a: ?*i32 = undefined; + const b: ?*f32 = @ptrCast(?*f32, a); } test "generic malloc free" { @@ -274,7 +274,7 @@ test "generic malloc free" { } var some_mem: [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { - return @ptrCast(&T, &some_mem[0])[0..n]; + return @ptrCast(*T, &some_mem[0])[0..n]; } fn memFree(comptime T: type, memory: []T) void {} @@ -357,7 +357,7 @@ const test3_foo = Test3Foo{ }, }; const test3_bar = Test3Foo{ .Two = 13 }; -fn test3_1(f: &const Test3Foo) void { +fn test3_1(f: *const Test3Foo) void { switch (f.*) { Test3Foo.Three => |pt| { assert(pt.x == 3); @@ -366,7 +366,7 @@ fn test3_1(f: &const Test3Foo) void { else => unreachable, } } -fn test3_2(f: &const Test3Foo) void { +fn test3_2(f: *const Test3Foo) void { switch (f.*) { Test3Foo.Two => |x| { assert(x == 13); @@ -393,7 +393,7 @@ test "pointer comparison" { const b = &a; assert(ptrEql(b, b)); } -fn ptrEql(a: &const []const u8, b: &const []const u8) bool { +fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; } @@ -446,13 +446,13 @@ fn testPointerToVoidReturnType() error!void { return a.*; } const test_pointer_to_void_return_type_x = void{}; -fn testPointerToVoidReturnType2() &const void { +fn testPointerToVoidReturnType2() *const void { return &test_pointer_to_void_return_type_x; } test "non const ptr to aliased type" { const int = i32; - assert(?&int == ?&i32); + assert(?*int == ?*i32); } test "array 2D const double ptr" { @@ -463,7 +463,7 @@ test "array 2D const double ptr" { testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]); } -fn testArray2DConstDoublePtr(ptr: &const f32) void { +fn testArray2DConstDoublePtr(ptr: *const f32) void { assert(ptr[0] == 1.0); assert(ptr[1] == 2.0); } @@ -497,7 +497,7 @@ test "@typeId" { assert(@typeId(u64) == Tid.Int); assert(@typeId(f32) == Tid.Float); assert(@typeId(f64) == Tid.Float); - assert(@typeId(&f32) == Tid.Pointer); + assert(@typeId(*f32) == Tid.Pointer); assert(@typeId([2]u8) == Tid.Array); assert(@typeId(AStruct) == Tid.Struct); assert(@typeId(@typeOf(1)) == Tid.IntLiteral); @@ -540,7 +540,7 @@ test "@typeName" { }; comptime { assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(&usize), "&usize")); + assert(mem.eql(u8, @typeName(*usize), "*usize")); // https://github.com/ziglang/zig/issues/675 assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); assert(mem.eql(u8, @typeName(Struct), "Struct")); @@ -555,7 +555,7 @@ fn TypeFromFn(comptime T: type) type { test "volatile load and store" { var number: i32 = 1234; - const ptr = (&volatile i32)(&number); + const ptr = (*volatile i32)(&number); ptr.* += 1; assert(ptr.* == 1235); } @@ -587,28 +587,28 @@ var global_ptr = &gdt[0]; // can't really run this test but we can make sure it has no compile error // and generates code -const vram = @intToPtr(&volatile u8, 0x20000000)[0..0x8000]; +const vram = @intToPtr(*volatile u8, 0x20000000)[0..0x8000]; export fn writeToVRam() void { vram[0] = 'X'; } test "pointer child field" { - assert((&u32).Child == u32); + assert((*u32).Child == u32); } const OpaqueA = @OpaqueType(); const OpaqueB = @OpaqueType(); test "@OpaqueType" { - assert(&OpaqueA != &OpaqueB); + assert(*OpaqueA != *OpaqueB); assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } test "variable is allowed to be a pointer to an opaque type" { var x: i32 = 1234; - _ = hereIsAnOpaqueType(@ptrCast(&OpaqueA, &x)); + _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); } -fn hereIsAnOpaqueType(ptr: &OpaqueA) &OpaqueA { +fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { var a = ptr; return a; } @@ -692,7 +692,7 @@ test "packed struct, enum, union parameters in extern function" { }, PackedUnion{ .a = 1 }, PackedEnum.A); } -export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void {} +export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: PackedEnum) void {} test "slicing zero length array" { const s1 = ""[0..]; @@ -703,8 +703,8 @@ test "slicing zero length array" { assert(mem.eql(u32, s2, []u32{})); } -const addr1 = @ptrCast(&const u8, emptyFn); +const addr1 = @ptrCast(*const u8, emptyFn); test "comptime cast fn to ptr" { - const addr2 = @ptrCast(&const u8, emptyFn); + const addr2 = @ptrCast(*const u8, emptyFn); comptime assert(addr1 == addr2); } diff --git a/test/cases/null.zig b/test/cases/null.zig index 936e5fafbd..bd78990ff4 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -65,7 +65,7 @@ test "if var maybe pointer" { .d = 1, }) == 15); } -fn shouldBeAPlus1(p: &const Particle) u64 { +fn shouldBeAPlus1(p: *const Particle) u64 { var maybe_particle: ?Particle = p.*; if (maybe_particle) |*particle| { particle.a += 1; diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index b82ce6340f..48fcc9ef03 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -5,7 +5,7 @@ const reflection = this; test "reflection: array, pointer, nullable, error union type child" { comptime { assert(([10]u8).Child == u8); - assert((&u8).Child == u8); + assert((*u8).Child == u8); assert((error!u8).Payload == u8); assert((?u8).Child == u8); } diff --git a/test/cases/slice.zig b/test/cases/slice.zig index eae6fa895e..24e5239e2d 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -const x = @intToPtr(&i32, 0x1000)[0..0x500]; +const x = @intToPtr(*i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { assert(@ptrToInt(x.ptr) == 0x1000); diff --git a/test/cases/struct.zig b/test/cases/struct.zig index d4a1c7fbe3..0712e508de 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -43,7 +43,7 @@ const VoidStructFieldsFoo = struct { test "structs" { var foo: StructFoo = undefined; - @memset(@ptrCast(&u8, &foo), 0, @sizeOf(StructFoo)); + @memset(@ptrCast(*u8, &foo), 0, @sizeOf(StructFoo)); foo.a += 1; foo.b = foo.a == 1; testFoo(foo); @@ -55,16 +55,16 @@ const StructFoo = struct { b: bool, c: f32, }; -fn testFoo(foo: &const StructFoo) void { +fn testFoo(foo: *const StructFoo) void { assert(foo.b); } -fn testMutation(foo: &StructFoo) void { +fn testMutation(foo: *StructFoo) void { foo.c = 100; } const Node = struct { val: Val, - next: &Node, + next: *Node, }; const Val = struct { @@ -112,7 +112,7 @@ fn aFunc() i32 { return 13; } -fn callStructField(foo: &const Foo) i32 { +fn callStructField(foo: *const Foo) i32 { return foo.ptr(); } @@ -124,7 +124,7 @@ test "store member function in variable" { } const MemberFnTestFoo = struct { x: i32, - fn member(foo: &const MemberFnTestFoo) i32 { + fn member(foo: *const MemberFnTestFoo) i32 { return foo.x; } }; @@ -141,7 +141,7 @@ test "member functions" { } const MemberFnRand = struct { seed: u32, - pub fn getSeed(r: &const MemberFnRand) u32 { + pub fn getSeed(r: *const MemberFnRand) u32 { return r.seed; } }; @@ -166,7 +166,7 @@ test "empty struct method call" { assert(es.method() == 1234); } const EmptyStruct = struct { - fn method(es: &const EmptyStruct) i32 { + fn method(es: *const EmptyStruct) i32 { return 1234; } }; @@ -228,15 +228,15 @@ test "bit field access" { assert(data.b == 3); } -fn getA(data: &const BitField1) u3 { +fn getA(data: *const BitField1) u3 { return data.a; } -fn getB(data: &const BitField1) u3 { +fn getB(data: *const BitField1) u3 { return data.b; } -fn getC(data: &const BitField1) u2 { +fn getC(data: *const BitField1) u2 { return data.c; } @@ -396,8 +396,8 @@ const Bitfields = packed struct { test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; - @memcpy(&bytes[0], @ptrCast(&u8, &all), 8); - var bitfields = @ptrCast(&Bitfields, &bytes[0]).*; + @memcpy(&bytes[0], @ptrCast(*u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, &bytes[0]).*; assert(bitfields.f1 == 0x1111); assert(bitfields.f2 == 0x2222); @@ -415,7 +415,7 @@ test "align 1 field before self referential align 8 field as slice return type" const Expr = union(enum) { Literal: u8, - Question: &Expr, + Question: *Expr, }; fn alloc(comptime T: type) []T { diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/cases/struct_contains_null_ptr_itself.zig index b6cb1a94cc..21175974b3 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/cases/struct_contains_null_ptr_itself.zig @@ -2,13 +2,13 @@ const std = @import("std"); const assert = std.debug.assert; test "struct contains null pointer which contains original struct" { - var x: ?&NodeLineComment = null; + var x: ?*NodeLineComment = null; assert(x == null); } pub const Node = struct { id: Id, - comment: ?&NodeLineComment, + comment: ?*NodeLineComment, pub const Id = enum { Root, diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 495fa9f3ed..c6a4b60f09 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -90,7 +90,7 @@ const SwitchProngWithVarEnum = union(enum) { Two: f32, Meh: void, }; -fn switchProngWithVarFn(a: &const SwitchProngWithVarEnum) void { +fn switchProngWithVarFn(a: *const SwitchProngWithVarEnum) void { switch (a.*) { SwitchProngWithVarEnum.One => |x| { assert(x == 13); diff --git a/test/cases/this.zig b/test/cases/this.zig index 5e433b5037..ba51d0ac90 100644 --- a/test/cases/this.zig +++ b/test/cases/this.zig @@ -8,7 +8,7 @@ fn Point(comptime T: type) type { x: T, y: T, - fn addOne(self: &Self) void { + fn addOne(self: *Self) void { self.x += 1; self.y += 1; } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 2561d70865..921ff785a7 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -37,7 +37,7 @@ test "type info: pointer type info" { } fn testPointer() void { - const u32_ptr_info = @typeInfo(&u32); + const u32_ptr_info = @typeInfo(*u32); assert(TypeId(u32_ptr_info) == TypeId.Pointer); assert(u32_ptr_info.Pointer.is_const == false); assert(u32_ptr_info.Pointer.is_volatile == false); @@ -169,14 +169,14 @@ fn testUnion() void { assert(notag_union_info.Union.fields[1].field_type == u32); const TestExternUnion = extern union { - foo: &c_void, + foo: *c_void, }; const extern_union_info = @typeInfo(TestExternUnion); assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); assert(extern_union_info.Union.tag_type == @typeOf(undefined)); assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == &c_void); + assert(extern_union_info.Union.fields[0].field_type == *c_void); } test "type info: struct info" { @@ -190,13 +190,13 @@ fn testStruct() void { assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); assert(struct_info.Struct.fields.len == 3); assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == &TestStruct); + assert(struct_info.Struct.fields[2].field_type == *TestStruct); assert(struct_info.Struct.defs.len == 2); assert(struct_info.Struct.defs[0].is_pub); assert(!struct_info.Struct.defs[0].data.Fn.is_extern); assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (&const TestStruct) void); + assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); } const TestStruct = packed struct { @@ -204,9 +204,9 @@ const TestStruct = packed struct { fieldA: usize, fieldB: void, - fieldC: &Self, + fieldC: *Self, - pub fn foo(self: &const Self) void {} + pub fn foo(self: *const Self) void {} }; test "type info: function type info" { @@ -227,7 +227,7 @@ fn testFunction() void { const test_instance: TestStruct = undefined; const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct); + assert(bound_fn_info.BoundFn.args[0].arg_type == *const TestStruct); } fn foo(comptime a: usize, b: bool, args: ...) usize { diff --git a/test/cases/undefined.zig b/test/cases/undefined.zig index f1af10e532..83c620d211 100644 --- a/test/cases/undefined.zig +++ b/test/cases/undefined.zig @@ -27,12 +27,12 @@ test "init static array to undefined" { const Foo = struct { x: i32, - fn setFooXMethod(foo: &Foo) void { + fn setFooXMethod(foo: *Foo) void { foo.x = 3; } }; -fn setFooX(foo: &Foo) void { +fn setFooX(foo: *Foo) void { foo.x = 2; } diff --git a/test/cases/union.zig b/test/cases/union.zig index 005ad08e6a..bdcbbdb452 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -68,11 +68,11 @@ test "init union with runtime value" { assert(foo.int == 42); } -fn setFloat(foo: &Foo, x: f64) void { +fn setFloat(foo: *Foo, x: f64) void { foo.* = Foo{ .float = x }; } -fn setInt(foo: &Foo, x: i32) void { +fn setInt(foo: *Foo, x: i32) void { foo.* = Foo{ .int = x }; } @@ -108,7 +108,7 @@ fn doTest() void { assert(bar(Payload{ .A = 1234 }) == -10); } -fn bar(value: &const Payload) i32 { +fn bar(value: *const Payload) i32 { assert(Letter(value.*) == Letter.A); return switch (value.*) { Payload.A => |x| return x - 1244, @@ -147,7 +147,7 @@ test "union(enum(u32)) with specified and unspecified tag values" { comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } -fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void { +fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: *const MultipleChoice2) void { assert(u32(@TagType(MultipleChoice2)(x.*)) == 60); assert(1123 == switch (x.*) { MultipleChoice2.A => 1, @@ -163,7 +163,7 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: &const MultipleChoice2) void } const ExternPtrOrInt = extern union { - ptr: &u8, + ptr: *u8, int: u64, }; test "extern union size" { @@ -171,7 +171,7 @@ test "extern union size" { } const PackedPtrOrInt = packed union { - ptr: &u8, + ptr: *u8, int: u64, }; test "extern union size" { @@ -206,7 +206,7 @@ test "cast union to tag type of union" { comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); } -fn testCastUnionToTagType(x: &const TheUnion) void { +fn testCastUnionToTagType(x: *const TheUnion) void { assert(TheTag(x.*) == TheTag.B); } @@ -243,7 +243,7 @@ const TheUnion2 = union(enum) { Item2: i32, }; -fn assertIsTheUnion2Item1(value: &const TheUnion2) void { +fn assertIsTheUnion2Item1(value: *const TheUnion2) void { assert(value.* == TheUnion2.Item1); } diff --git a/test/compare_output.zig b/test/compare_output.zig index 0170477b8b..00ad4a709b 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -3,10 +3,10 @@ const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompareOutputContext) void { +pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("hello world with libc", \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: &&u8) c_int { + \\export fn main(argc: c_int, argv: **u8) c_int { \\ _ = c.puts(c"Hello, world!"); \\ return 0; \\} @@ -139,7 +139,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: &&u8) c_int { + \\export fn main(argc: c_int, argv: **u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -284,9 +284,9 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ - \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) c_int { - \\ const a_int = @ptrCast(&align(1) const i32, a ?? unreachable); - \\ const b_int = @ptrCast(&align(1) const i32, b ?? unreachable); + \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int { + \\ const a_int = @ptrCast(*align(1) const i32, a ?? unreachable); + \\ const b_int = @ptrCast(*align(1) const i32, b ?? unreachable); \\ if (a_int.* < b_int.*) { \\ return -1; \\ } else if (a_int.* > b_int.*) { @@ -299,7 +299,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\export fn main() c_int { \\ var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(&c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(*c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { @@ -324,7 +324,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: &&u8) c_int { + \\export fn main(argc: c_int, argv: **u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -344,13 +344,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const Foo = struct { \\ field1: Bar, \\ - \\ fn method(a: &const Foo) bool { return true; } + \\ fn method(a: *const Foo) bool { return true; } \\}; \\ \\const Bar = struct { \\ field2: i32, \\ - \\ fn method(b: &const Bar) bool { return true; } + \\ fn method(b: *const Bar) bool { return true; } \\}; \\ \\pub fn main() void { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5215953d0a..1297ed29ab 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompileErrorContext) void { +pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "invalid deref on switch target", \\comptime { @@ -109,7 +109,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "@ptrCast discards const qualifier", \\export fn entry() void { \\ const x: i32 = 1234; - \\ const y = @ptrCast(&i32, &x); + \\ const y = @ptrCast(*i32, &x); \\} , ".tmp_source.zig:3:15: error: cast discards const qualifier", @@ -118,7 +118,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "comptime slice of undefined pointer non-zero len", \\export fn entry() void { - \\ const slice = (&i32)(undefined)[0..1]; + \\ const slice = (*i32)(undefined)[0..1]; \\} , ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer", @@ -126,7 +126,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "type checking function pointers", - \\fn a(b: fn (&const u8) void) void { + \\fn a(b: fn (*const u8) void) void { \\ b('a'); \\} \\fn c(d: u8) void { @@ -136,7 +136,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ a(c); \\} , - ".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'", + ".tmp_source.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", ); cases.add( @@ -594,15 +594,15 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "attempt to use 0 bit type in extern fn", - \\extern fn foo(ptr: extern fn(&void) void) void; + \\extern fn foo(ptr: extern fn(*void) void) void; \\ \\export fn entry() void { \\ foo(bar); \\} \\ - \\extern fn bar(x: &void) void { } + \\extern fn bar(x: *void) void { } , - ".tmp_source.zig:7:18: error: parameter of type '&void' has 0 bits; not allowed in function with calling convention 'ccc'", + ".tmp_source.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", ); cases.add( @@ -911,10 +911,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "pointer to noreturn", - \\fn a() &noreturn {} + \\fn a() *noreturn {} \\export fn entry() void { _ = a(); } , - ".tmp_source.zig:1:9: error: pointer to noreturn not allowed", + ".tmp_source.zig:1:8: error: pointer to noreturn not allowed", ); cases.add( @@ -985,7 +985,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected type 'i32', found '&const u8'", + ".tmp_source.zig:3:12: error: expected type 'i32', found '*const u8'", ); cases.add( @@ -1446,7 +1446,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "switch expression - switch on pointer type with no else", - \\fn foo(x: &u8) void { + \\fn foo(x: *u8) void { \\ switch (x) { \\ &y => {}, \\ } @@ -1454,7 +1454,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const y: u8 = 100; \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:2:5: error: else prong required when switching on type '&u8'", + ".tmp_source.zig:2:5: error: else prong required when switching on type '*u8'", ); cases.add( @@ -1501,10 +1501,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "address of number literal", \\const x = 3; \\const y = &x; - \\fn foo() &const i32 { return y; } + \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected type '&const i32', found '&const (integer literal)'", + ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const (integer literal)'", ); cases.add( @@ -1529,10 +1529,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ a: i32, \\ b: i32, \\ - \\ fn member_a(foo: &const Foo) i32 { + \\ fn member_a(foo: *const Foo) i32 { \\ return foo.a; \\ } - \\ fn member_b(foo: &const Foo) i32 { + \\ fn member_b(foo: *const Foo) i32 { \\ return foo.b; \\ } \\}; @@ -1543,7 +1543,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ Foo.member_b, \\}; \\ - \\fn f(foo: &const Foo, index: usize) void { + \\fn f(foo: *const Foo, index: usize) void { \\ const result = members[index](); \\} \\ @@ -1692,11 +1692,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "assign null to non-nullable pointer", - \\const a: &u8 = null; + \\const a: *u8 = null; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:1:16: error: expected type '&u8', found '(null)'", + ".tmp_source.zig:1:16: error: expected type '*u8', found '(null)'", ); cases.add( @@ -1806,7 +1806,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ One: void, \\ Two: i32, \\}; - \\fn bad_eql_2(a: &const EnumWithData, b: &const EnumWithData) bool { + \\fn bad_eql_2(a: *const EnumWithData, b: *const EnumWithData) bool { \\ return a.* == b.*; \\} \\ @@ -2011,9 +2011,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "wrong number of arguments for method fn call", \\const Foo = struct { - \\ fn method(self: &const Foo, a: i32) void {} + \\ fn method(self: *const Foo, a: i32) void {} \\}; - \\fn f(foo: &const Foo) void { + \\fn f(foo: *const Foo) void { \\ \\ foo.method(1, 2); \\} @@ -2062,7 +2062,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "misspelled type with pointer only reference", \\const JasonHM = u8; - \\const JasonList = &JsonNode; + \\const JasonList = *JsonNode; \\ \\const JsonOA = union(enum) { \\ JSONArray: JsonList, @@ -2113,16 +2113,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected type 'i32', found '&const Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found '*const Foo'", ); cases.add( "method call with first arg type wrong container", \\pub const List = struct { \\ len: usize, - \\ allocator: &Allocator, + \\ allocator: *Allocator, \\ - \\ pub fn init(allocator: &Allocator) List { + \\ pub fn init(allocator: *Allocator) List { \\ return List { \\ .len = 0, \\ .allocator = allocator, @@ -2143,7 +2143,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ x.init(); \\} , - ".tmp_source.zig:23:5: error: expected type '&Allocator', found '&List'", + ".tmp_source.zig:23:5: error: expected type '*Allocator', found '*List'", ); cases.add( @@ -2308,17 +2308,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ c: u2, \\}; \\ - \\fn foo(bit_field: &const BitField) u3 { + \\fn foo(bit_field: *const BitField) u3 { \\ return bar(&bit_field.b); \\} \\ - \\fn bar(x: &const u3) u3 { + \\fn bar(x: *const u3) u3 { \\ return x.*; \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:8:26: error: expected type '&const u3', found '&align(1:3:6) const u3'", + ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(1:3:6) const u3'", ); cases.add( @@ -2441,13 +2441,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const b = &a; \\ return ptrEql(b, b); \\} - \\fn ptrEql(a: &[]const u8, b: &[]const u8) bool { + \\fn ptrEql(a: *[]const u8, b: *[]const u8) bool { \\ return true; \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:4:19: error: expected type '&[]const u8', found '&const []const u8'", + ".tmp_source.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", ); cases.addCase(x: { @@ -2493,7 +2493,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "ptrcast to non-pointer", - \\export fn entry(a: &i32) usize { + \\export fn entry(a: *i32) usize { \\ return @ptrCast(usize, a); \\} , @@ -2542,16 +2542,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "int to ptr of 0 bits", \\export fn foo() void { \\ var x: usize = 0x1000; - \\ var y: &void = @intToPtr(&void, x); + \\ var y: *void = @intToPtr(*void, x); \\} , - ".tmp_source.zig:3:31: error: type '&void' has 0 bits and cannot store information", + ".tmp_source.zig:3:30: error: type '*void' has 0 bits and cannot store information", ); cases.add( "@fieldParentPtr - non struct", \\const Foo = i32; - \\export fn foo(a: &i32) &Foo { + \\export fn foo(a: *i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} , @@ -2563,7 +2563,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const Foo = extern struct { \\ derp: i32, \\}; - \\export fn foo(a: &i32) &Foo { + \\export fn foo(a: *i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} , @@ -2575,7 +2575,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const Foo = extern struct { \\ a: i32, \\}; - \\export fn foo(a: i32) &Foo { + \\export fn foo(a: i32) *Foo { \\ return @fieldParentPtr(Foo, "a", a); \\} , @@ -2591,7 +2591,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const foo = Foo { .a = 1, .b = 2, }; \\ \\comptime { - \\ const field_ptr = @intToPtr(&i32, 0x1234); + \\ const field_ptr = @intToPtr(*i32, 0x1234); \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); \\} , @@ -2682,7 +2682,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "returning address of local variable - simple", - \\export fn foo() &i32 { + \\export fn foo() *i32 { \\ var a: i32 = undefined; \\ return &a; \\} @@ -2692,7 +2692,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "returning address of local variable - phi", - \\export fn foo(c: bool) &i32 { + \\export fn foo(c: bool) *i32 { \\ var a: i32 = undefined; \\ var b: i32 = undefined; \\ return if (c) &a else &b; @@ -3086,11 +3086,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ bar(&foo.b); \\} \\ - \\fn bar(x: &u32) void { + \\fn bar(x: *u32) void { \\ x.* += 1; \\} , - ".tmp_source.zig:8:13: error: expected type '&u32', found '&align(1) u32'", + ".tmp_source.zig:8:13: error: expected type '*u32', found '*align(1) u32'", ); cases.add( @@ -3117,13 +3117,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { "increase pointer alignment in @ptrCast", \\export fn entry() u32 { \\ var bytes: [4]u8 = []u8{0x01, 0x02, 0x03, 0x04}; - \\ const ptr = @ptrCast(&u32, &bytes[0]); + \\ const ptr = @ptrCast(*u32, &bytes[0]); \\ return ptr.*; \\} , ".tmp_source.zig:3:17: error: cast increases pointer alignment", - ".tmp_source.zig:3:38: note: '&u8' has alignment 1", - ".tmp_source.zig:3:27: note: '&u32' has alignment 4", + ".tmp_source.zig:3:38: note: '*u8' has alignment 1", + ".tmp_source.zig:3:26: note: '*u32' has alignment 4", ); cases.add( @@ -3169,7 +3169,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ return x == 5678; \\} , - ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'", + ".tmp_source.zig:4:32: error: expected type '*i32', found '*align(1) i32'", ); cases.add( @@ -3198,20 +3198,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add( "wrong pointer implicitly casted to pointer to @OpaqueType()", \\const Derp = @OpaqueType(); - \\extern fn bar(d: &Derp) void; + \\extern fn bar(d: *Derp) void; \\export fn foo() void { \\ var x = u8(1); - \\ bar(@ptrCast(&c_void, &x)); + \\ bar(@ptrCast(*c_void, &x)); \\} , - ".tmp_source.zig:5:9: error: expected type '&Derp', found '&c_void'", + ".tmp_source.zig:5:9: error: expected type '*Derp', found '*c_void'", ); cases.add( "non-const variables of things that require const variables", \\const Opaque = @OpaqueType(); \\ - \\export fn entry(opaque: &Opaque) void { + \\export fn entry(opaque: *Opaque) void { \\ var m2 = &2; \\ const y: u32 = m2.*; \\ @@ -3229,10 +3229,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\} \\ \\const Foo = struct { - \\ fn bar(self: &const Foo) void {} + \\ fn bar(self: *const Foo) void {} \\}; , - ".tmp_source.zig:4:4: error: variable of type '&const (integer literal)' must be const or comptime", + ".tmp_source.zig:4:4: error: variable of type '*const (integer literal)' must be const or comptime", ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime", ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime", @@ -3241,7 +3241,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", - ".tmp_source.zig:15:4: error: variable of type '(bound fn(&const Foo) void)' must be const or comptime", + ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", ".tmp_source.zig:17:4: error: unreachable code", ); @@ -3397,14 +3397,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ \\export fn entry() bool { \\ var x: i32 = 1; - \\ return bar(@ptrCast(&MyType, &x)); + \\ return bar(@ptrCast(*MyType, &x)); \\} \\ - \\fn bar(x: &MyType) bool { + \\fn bar(x: *MyType) bool { \\ return x.blah; \\} , - ".tmp_source.zig:9:13: error: type '&MyType' does not support field access", + ".tmp_source.zig:9:13: error: type '*MyType' does not support field access", ); cases.add( @@ -3535,9 +3535,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn entry() void { \\ foo("hello",); \\} - \\pub extern fn foo(format: &const u8, ...) void; + \\pub extern fn foo(format: *const u8, ...) void; , - ".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'", + ".tmp_source.zig:2:9: error: expected type '*const u8', found '[5]u8'", ); cases.add( @@ -3902,7 +3902,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const a = Payload { .A = 1234 }; \\ foo(a); \\} - \\fn foo(a: &const Payload) void { + \\fn foo(a: *const Payload) void { \\ switch (a.*) { \\ Payload.A => {}, \\ else => unreachable, diff --git a/test/gen_h.zig b/test/gen_h.zig index 2def39bed7..9559c3395c 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.GenHContext) void { +pub fn addCases(cases: *tests.GenHContext) void { cases.add("declare enum", \\const Foo = extern enum { A, B, C }; \\export fn entry(foo: Foo) void { } diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 1fea6347ab..71d1d68764 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.CompareOutputContext) void { +pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("calling panic", \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig index 7752f599df..64f3c08583 100644 --- a/test/standalone/brace_expansion/build.zig +++ b/test/standalone/brace_expansion/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const main = b.addTest("main.zig"); main.setBuildMode(b.standardReleaseOptions()); diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index c96cc2cbb9..ccb4f6dd45 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -14,7 +14,7 @@ const Token = union(enum) { Eof, }; -var global_allocator: &mem.Allocator = undefined; +var global_allocator: *mem.Allocator = undefined; fn tokenize(input: []const u8) !ArrayList(Token) { const State = enum { @@ -73,7 +73,7 @@ const ParseError = error{ OutOfMemory, }; -fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node { +fn parse(tokens: *const ArrayList(Token), token_index: *usize) ParseError!Node { const first_token = tokens.items[token_index.*]; token_index.* += 1; @@ -109,7 +109,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node { } } -fn expandString(input: []const u8, output: &Buffer) !void { +fn expandString(input: []const u8, output: *Buffer) !void { const tokens = try tokenize(input); if (tokens.len == 1) { return output.resize(0); @@ -139,7 +139,7 @@ fn expandString(input: []const u8, output: &Buffer) !void { const ExpandNodeError = error{OutOfMemory}; -fn expandNode(node: &const Node, output: &ArrayList(Buffer)) ExpandNodeError!void { +fn expandNode(node: *const Node, output: *ArrayList(Buffer)) ExpandNodeError!void { assert(output.len == 0); switch (node.*) { Node.Scalar => |scalar| { diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index f3ab327006..733b3729c1 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const obj = b.addObject("test", "test.zig"); const test_step = b.step("test", "Test the program"); diff --git a/test/standalone/issue_339/test.zig b/test/standalone/issue_339/test.zig index da0747b8e6..f4068dcfac 100644 --- a/test/standalone/issue_339/test.zig +++ b/test/standalone/issue_339/test.zig @@ -1,5 +1,5 @@ const StackTrace = @import("builtin").StackTrace; -pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { +pub fn panic(msg: []const u8, stack_trace: ?*StackTrace) noreturn { @breakpoint(); while (true) {} } diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig index 4f5dcd7ff4..06c37a83a3 100644 --- a/test/standalone/issue_794/build.zig +++ b/test/standalone/issue_794/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const test_artifact = b.addTest("main.zig"); test_artifact.addIncludeDir("a_directory"); diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index bb9416d3c4..e0b3885dc3 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { const exe = b.addExecutable("test", "test.zig"); exe.addPackagePath("my_pkg", "pkg.zig"); diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index ecbba297d8..c700d43db9 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) void { +pub fn build(b: *Builder) void { b.addCIncludePath("."); const main = b.addTest("main.zig"); diff --git a/test/tests.zig b/test/tests.zig index b59b954122..cc562331fe 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -47,7 +47,7 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB -pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ .b = b, @@ -61,7 +61,7 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build return cases.step; } -pub fn addRuntimeSafetyTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ .b = b, @@ -75,7 +75,7 @@ pub fn addRuntimeSafetyTests(b: &build.Builder, test_filter: ?[]const u8) &build return cases.step; } -pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompileErrorContext) catch unreachable; cases.* = CompileErrorContext{ .b = b, @@ -89,7 +89,7 @@ pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) &build. return cases.step; } -pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(BuildExamplesContext) catch unreachable; cases.* = BuildExamplesContext{ .b = b, @@ -103,7 +103,7 @@ pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) &build. return cases.step; } -pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ .b = b, @@ -117,7 +117,7 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) &bui return cases.step; } -pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(TranslateCContext) catch unreachable; cases.* = TranslateCContext{ .b = b, @@ -131,7 +131,7 @@ pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) &build.St return cases.step; } -pub fn addGenHTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { +pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { const cases = b.allocator.create(GenHContext) catch unreachable; cases.* = GenHContext{ .b = b, @@ -145,7 +145,7 @@ pub fn addGenHTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { return cases.step; } -pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, with_lldb: bool) &build.Step { +pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, with_lldb: bool) *build.Step { const step = b.step(b.fmt("test-{}", name), desc); for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); @@ -193,8 +193,8 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons } pub const CompareOutputContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -217,28 +217,28 @@ pub const CompareOutputContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn setCommandLineArgs(self: &TestCase, args: []const []const u8) void { + pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { self.cli_args = args; } }; const RunCompareOutputStep = struct { step: build.Step, - context: &CompareOutputContext, + context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, test_index: usize, cli_args: []const []const u8, - pub fn create(context: &CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) &RunCompareOutputStep { + pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) *RunCompareOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(RunCompareOutputStep) catch unreachable; ptr.* = RunCompareOutputStep{ @@ -254,7 +254,7 @@ pub const CompareOutputContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(RunCompareOutputStep, "step", step); const b = self.context.b; @@ -321,12 +321,12 @@ pub const CompareOutputContext = struct { const RuntimeSafetyRunStep = struct { step: build.Step, - context: &CompareOutputContext, + context: *CompareOutputContext, exe_path: []const u8, name: []const u8, test_index: usize, - pub fn create(context: &CompareOutputContext, exe_path: []const u8, name: []const u8) &RuntimeSafetyRunStep { + pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8) *RuntimeSafetyRunStep { const allocator = context.b.allocator; const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; ptr.* = RuntimeSafetyRunStep{ @@ -340,7 +340,7 @@ pub const CompareOutputContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step); const b = self.context.b; @@ -382,7 +382,7 @@ pub const CompareOutputContext = struct { } }; - pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { + pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { var tc = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), @@ -396,32 +396,32 @@ pub const CompareOutputContext = struct { return tc; } - pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { + pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { return createExtra(self, name, source, expected_output, Special.None); } - pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { var tc = self.create(name, source, expected_output); tc.link_libc = true; self.addCase(tc); } - pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { const tc = self.create(name, source, expected_output); self.addCase(tc); } - pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { + pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { const tc = self.createExtra(name, source, expected_output, Special.Asm); self.addCase(tc); } - pub fn addRuntimeSafety(self: &CompareOutputContext, name: []const u8, source: []const u8) void { + pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); self.addCase(tc); } - pub fn addCase(self: &CompareOutputContext, case: &const TestCase) void { + pub fn addCase(self: *CompareOutputContext, case: *const TestCase) void { const b = self.b; const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; @@ -504,8 +504,8 @@ pub const CompareOutputContext = struct { }; pub const CompileErrorContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -521,27 +521,27 @@ pub const CompileErrorContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn addExpectedError(self: &TestCase, text: []const u8) void { + pub fn addExpectedError(self: *TestCase, text: []const u8) void { self.expected_errors.append(text) catch unreachable; } }; const CompileCmpOutputStep = struct { step: build.Step, - context: &CompileErrorContext, + context: *CompileErrorContext, name: []const u8, test_index: usize, - case: &const TestCase, + case: *const TestCase, build_mode: Mode, - pub fn create(context: &CompileErrorContext, name: []const u8, case: &const TestCase, build_mode: Mode) &CompileCmpOutputStep { + pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; ptr.* = CompileCmpOutputStep{ @@ -556,7 +556,7 @@ pub const CompileErrorContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); const b = self.context.b; @@ -661,7 +661,7 @@ pub const CompileErrorContext = struct { warn("\n"); } - pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) &TestCase { + pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -678,24 +678,24 @@ pub const CompileErrorContext = struct { return tc; } - pub fn addC(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addC(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { var tc = self.create(name, source, expected_lines); tc.link_libc = true; self.addCase(tc); } - pub fn addExe(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addExe(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { var tc = self.create(name, source, expected_lines); tc.is_exe = true; self.addCase(tc); } - pub fn add(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(name, source, expected_lines); self.addCase(tc); } - pub fn addCase(self: &CompileErrorContext, case: &const TestCase) void { + pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; for ([]Mode{ @@ -720,20 +720,20 @@ pub const CompileErrorContext = struct { }; pub const BuildExamplesContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, - pub fn addC(self: &BuildExamplesContext, root_src: []const u8) void { + pub fn addC(self: *BuildExamplesContext, root_src: []const u8) void { self.addAllArgs(root_src, true); } - pub fn add(self: &BuildExamplesContext, root_src: []const u8) void { + pub fn add(self: *BuildExamplesContext, root_src: []const u8) void { self.addAllArgs(root_src, false); } - pub fn addBuildFile(self: &BuildExamplesContext, build_file: []const u8) void { + pub fn addBuildFile(self: *BuildExamplesContext, build_file: []const u8) void { const b = self.b; const annotated_case_name = b.fmt("build {} (Debug)", build_file); @@ -763,7 +763,7 @@ pub const BuildExamplesContext = struct { self.step.dependOn(&log_step.step); } - pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) void { + pub fn addAllArgs(self: *BuildExamplesContext, root_src: []const u8, link_libc: bool) void { const b = self.b; for ([]Mode{ @@ -792,8 +792,8 @@ pub const BuildExamplesContext = struct { }; pub const TranslateCContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -808,26 +808,26 @@ pub const TranslateCContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn addExpectedLine(self: &TestCase, text: []const u8) void { + pub fn addExpectedLine(self: *TestCase, text: []const u8) void { self.expected_lines.append(text) catch unreachable; } }; const TranslateCCmpOutputStep = struct { step: build.Step, - context: &TranslateCContext, + context: *TranslateCContext, name: []const u8, test_index: usize, - case: &const TestCase, + case: *const TestCase, - pub fn create(context: &TranslateCContext, name: []const u8, case: &const TestCase) &TranslateCCmpOutputStep { + pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; ptr.* = TranslateCCmpOutputStep{ @@ -841,7 +841,7 @@ pub const TranslateCContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); const b = self.context.b; @@ -935,7 +935,7 @@ pub const TranslateCContext = struct { warn("\n"); } - pub fn create(self: &TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) &TestCase { + pub fn create(self: *TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -951,22 +951,22 @@ pub const TranslateCContext = struct { return tc; } - pub fn add(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(false, "source.h", name, source, expected_lines); self.addCase(tc); } - pub fn addC(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addC(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(false, "source.c", name, source, expected_lines); self.addCase(tc); } - pub fn addAllowWarnings(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn addAllowWarnings(self: *TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create(true, "source.h", name, source, expected_lines); self.addCase(tc); } - pub fn addCase(self: &TranslateCContext, case: &const TestCase) void { + pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { const b = self.b; const annotated_case_name = fmt.allocPrint(self.b.allocator, "translate-c {}", case.name) catch unreachable; @@ -986,8 +986,8 @@ pub const TranslateCContext = struct { }; pub const GenHContext = struct { - b: &build.Builder, - step: &build.Step, + b: *build.Builder, + step: *build.Step, test_index: usize, test_filter: ?[]const u8, @@ -1001,27 +1001,27 @@ pub const GenHContext = struct { source: []const u8, }; - pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) void { + pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { self.sources.append(SourceFile{ .filename = filename, .source = source, }) catch unreachable; } - pub fn addExpectedLine(self: &TestCase, text: []const u8) void { + pub fn addExpectedLine(self: *TestCase, text: []const u8) void { self.expected_lines.append(text) catch unreachable; } }; const GenHCmpOutputStep = struct { step: build.Step, - context: &GenHContext, + context: *GenHContext, h_path: []const u8, name: []const u8, test_index: usize, - case: &const TestCase, + case: *const TestCase, - pub fn create(context: &GenHContext, h_path: []const u8, name: []const u8, case: &const TestCase) &GenHCmpOutputStep { + pub fn create(context: *GenHContext, h_path: []const u8, name: []const u8, case: *const TestCase) *GenHCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; ptr.* = GenHCmpOutputStep{ @@ -1036,7 +1036,7 @@ pub const GenHContext = struct { return ptr; } - fn make(step: &build.Step) !void { + fn make(step: *build.Step) !void { const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; @@ -1069,7 +1069,7 @@ pub const GenHContext = struct { warn("\n"); } - pub fn create(self: &GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) &TestCase { + pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { const tc = self.b.allocator.create(TestCase) catch unreachable; tc.* = TestCase{ .name = name, @@ -1084,12 +1084,12 @@ pub const GenHContext = struct { return tc; } - pub fn add(self: &GenHContext, name: []const u8, source: []const u8, expected_lines: ...) void { + pub fn add(self: *GenHContext, name: []const u8, source: []const u8, expected_lines: ...) void { const tc = self.create("test.zig", name, source, expected_lines); self.addCase(tc); } - pub fn addCase(self: &GenHContext, case: &const TestCase) void { + pub fn addCase(self: *GenHContext, case: *const TestCase) void { const b = self.b; const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable; diff --git a/test/translate_c.zig b/test/translate_c.zig index 4cf1e047fa..9a07bc343d 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,6 @@ const tests = @import("tests.zig"); -pub fn addCases(cases: &tests.TranslateCContext) void { +pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("double define struct", \\typedef struct Bar Bar; \\typedef struct Foo Foo; @@ -14,11 +14,11 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ a: ?&Foo, + \\ a: ?*Foo, \\}; \\pub const Foo = struct_Foo; \\pub const struct_Bar = extern struct { - \\ a: ?&Foo, + \\ a: ?*Foo, \\}; ); @@ -99,7 +99,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void) void; + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; ); cases.add("simple struct", @@ -110,7 +110,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\const struct_Foo = extern struct { \\ x: c_int, - \\ y: ?&u8, + \\ y: ?*u8, \\}; , \\pub const Foo = struct_Foo; @@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub const BarB = enum_Bar.B; , - \\pub extern fn func(a: ?&struct_Foo, b: ?&(?&enum_Bar)) void; + \\pub extern fn func(a: ?*struct_Foo, b: ?*(?*enum_Bar)) void; , \\pub const Foo = struct_Foo; , @@ -151,7 +151,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.add("constant size array", \\void func(int array[20]); , - \\pub extern fn func(array: ?&c_int) void; + \\pub extern fn func(array: ?*c_int) void; ); cases.add("self referential struct with function pointer", @@ -160,7 +160,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn(?&struct_Foo) void, + \\ derp: ?extern fn(?*struct_Foo) void, \\}; , \\pub const Foo = struct_Foo; @@ -172,7 +172,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?&struct_Foo, x: c_int) ?&struct_Foo; + \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -219,11 +219,11 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\}; , \\pub const struct_Bar = extern struct { - \\ next: ?&struct_Foo, + \\ next: ?*struct_Foo, \\}; , \\pub const struct_Foo = extern struct { - \\ next: ?&struct_Bar, + \\ next: ?*struct_Bar, \\}; ); @@ -233,7 +233,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?&Foo) Foo; + \\pub extern fn fun(a: ?*Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -505,7 +505,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -607,7 +607,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub const struct_Foo = extern struct { \\ field: c_int, \\}; - \\pub export fn read_field(foo: ?&struct_Foo) c_int { + \\pub export fn read_field(foo: ?*struct_Foo) c_int { \\ return (??foo).field; \\} ); @@ -653,8 +653,8 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?&c_ushort) ?&c_void { - \\ return @ptrCast(?&c_void, x); + \\pub export fn foo(x: ?*c_ushort) ?*c_void { + \\ return @ptrCast(?*c_void, x); \\} ); @@ -674,7 +674,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 0; \\} , - \\pub export fn foo() ?&c_int { + \\pub export fn foo() ?*c_int { \\ return null; \\} ); @@ -983,7 +983,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ *x = 1; \\} , - \\pub export fn foo(x: ?&c_int) void { + \\pub export fn foo(x: ?*c_int) void { \\ (??x).* = 1; \\} ); @@ -1011,7 +1011,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { , \\pub fn foo() c_int { \\ var x: c_int = 1234; - \\ var ptr: ?&c_int = &x; + \\ var ptr: ?*c_int = &x; \\ return (??ptr).*; \\} ); @@ -1021,7 +1021,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return "bar"; \\} , - \\pub fn foo() ?&const u8 { + \\pub fn foo() ?*const u8 { \\ return c"bar"; \\} ); @@ -1150,8 +1150,8 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return (float *)a; \\} , - \\fn ptrcast(a: ?&c_int) ?&f32 { - \\ return @ptrCast(?&f32, a); + \\fn ptrcast(a: ?*c_int) ?*f32 { + \\ return @ptrCast(?*f32, a); \\} ); @@ -1173,7 +1173,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1194,7 +1194,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.add("const ptr initializer", \\static const char *v0 = "0.0.0"; , - \\pub var v0: ?&const u8 = c"0.0.0"; + \\pub var v0: ?*const u8 = c"0.0.0"; ); cases.add("static incomplete array inside function", @@ -1203,14 +1203,14 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ const v2: &const u8 = c"2.2.2"; + \\ const v2: *const u8 = c"2.2.2"; \\} ); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE); + \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(*NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(*NRF_GPIO_Type, NRF_GPIO_BASE) else (*NRF_GPIO_Type)(NRF_GPIO_BASE); ); cases.add("if on none bool", @@ -1231,7 +1231,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?&c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1248,7 +1248,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1264,7 +1264,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; -- cgit v1.2.3 From 2f614c42fe4f28e5adda8163bd50d6d3507d6353 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 May 2018 18:14:35 -0400 Subject: ir: rip out special logic for using addr-of instruction for types See #588 --- src/all_types.hpp | 2 - src/ir.cpp | 155 ++++++++++++++---------------------------------- std/zig/parser_test.zig | 4 +- 3 files changed, 46 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index b9199c2757..d5906cae95 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2275,8 +2275,6 @@ struct IrInstructionVarPtr { IrInstruction base; VariableTableEntry *var; - bool is_const; - bool is_volatile; }; struct IrInstructionCall { diff --git a/src/ir.cpp b/src/ir.cpp index b1fac9f485..984cfd78d3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -104,8 +104,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg); static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type); -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var); static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); @@ -1000,13 +999,9 @@ static IrInstruction *ir_build_bin_op_from(IrBuilder *irb, IrInstruction *old_in return new_instruction; } -static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, - VariableTableEntry *var, bool is_const, bool is_volatile) -{ +static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, VariableTableEntry *var) { IrInstructionVarPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->var = var; - instruction->is_const = is_const; - instruction->is_volatile = is_volatile; ir_ref_var(var); @@ -3515,8 +3510,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); if (var) { - IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var, - !lval.is_ptr || lval.is_const, lval.is_ptr && lval.is_volatile); + IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); if (lval.is_ptr) return var_ptr; else @@ -5140,7 +5134,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); - IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var, false, false); + IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; VariableTableEntry *index_var; @@ -5158,7 +5152,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); - IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var, false, false); + IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond"); @@ -6387,7 +6381,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type); ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type); ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value); - IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var, false, false); + IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var); ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, @@ -6708,15 +6702,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); - coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false); + coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); - irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, - await_handle_var, false, false); + irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); @@ -6856,7 +6849,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); - IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var, true, false); + IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false); size_t arg_count = 2; @@ -8958,35 +8951,15 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstExprValue *pointee, TypeTableEntry *pointee_type, ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { - // TODO remove this special case for types - 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 noreturn not allowed")); - return ira->codegen->invalid_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); - if (type_is_invalid(type_entry)) { - return ira->codegen->invalid_instruction; - } - const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry, - ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0); - return const_instr; - } else { - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); - 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.mut = ptr_mut; - const_val->data.x_ptr.data.ref.pointee = pointee; - return const_instr; - } + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, + ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); + 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.mut = ptr_mut; + const_val->data.x_ptr.data.ref.pointee = pointee; + return const_instr; } static TypeTableEntry *ir_analyze_const_ptr(IrAnalyze *ira, IrInstruction *instruction, @@ -9314,9 +9287,8 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) return ira->codegen->invalid_instruction; - bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true; return ir_get_const_ptr(ira, source_instruction, val, value->value.type, - ConstPtrMutComptimeConst, final_is_const, is_volatile, + ConstPtrMutComptimeConst, is_const, is_volatile, get_abi_alignment(ira->codegen, value->value.type)); } @@ -10245,21 +10217,6 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; return load_ptr_instruction; - } else if (type_entry->id == TypeTableEntryIdMetaType) { - ConstExprValue *ptr_val = ir_resolve_const(ira, ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - - TypeTableEntry *ptr_type = ptr_val->data.x_type; - if (ptr_type->id == TypeTableEntryIdPointer) { - TypeTableEntry *child_type = ptr_type->data.pointer.child_type; - return ir_create_const_type(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); - } else { - ir_add_error(ira, source_instruction, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_instruction; - } } else { ir_add_error_node(ira, source_instruction->source_node, buf_sprintf("attempt to dereference non pointer type '%s'", @@ -11966,7 +11923,7 @@ IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_i { VariableTableEntry *coro_allocator_var = ira->old_irb.exec->coro_allocator_var; assert(coro_allocator_var != nullptr); - IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var, true, false); + IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var); IrInstruction *result = ir_get_deref(ira, source_instr, var_ptr_inst); assert(result->value.type != nullptr); return result; @@ -12147,7 +12104,7 @@ static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t in } static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr) + VariableTableEntry *var) { if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) { assert(ira->codegen->errors.length != 0); @@ -12173,8 +12130,8 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, } } - 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; + bool is_const = var->src_is_const; + bool is_volatile = false; if (mem_slot != nullptr) { switch (mem_slot->special) { case ConstValSpecialRuntime: @@ -12200,7 +12157,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, - instruction->scope, instruction->source_node, var, is_const, is_volatile); + instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, var->src_is_const, is_volatile, var->align_bytes, 0, 0); type_ensure_zero_bits_known(ira->codegen, var->value->type); @@ -12486,7 +12443,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557")); return ira->codegen->builtin_types.entry_invalid; } - IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var, true, false); + IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var); if (type_is_invalid(arg_var_ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -13120,17 +13077,16 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP } static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr) + VariableTableEntry *var) { - IrInstruction *result = ir_get_var_ptr(ira, instruction, var, is_const_ptr, is_volatile_ptr); + IrInstruction *result = ir_get_var_ptr(ira, instruction, var); ir_link_new_instruction(result, instruction); return result->value.type; } static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) { VariableTableEntry *var = var_ptr_instruction->var; - return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var, var_ptr_instruction->is_const, - var_ptr_instruction->is_volatile); + return ir_analyze_var_ptr(ira, &var_ptr_instruction->base, var); } static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align) { @@ -13152,11 +13108,6 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = array_ptr->value.type; - if (ptr_type->id == TypeTableEntryIdMetaType) { - ir_add_error(ira, &elem_ptr_instruction->base, - buf_sprintf("array access of non-array type '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *array_type = ptr_type->data.pointer.child_type; @@ -13218,8 +13169,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc bool is_const = true; bool is_volatile = false; if (var) { - return ir_analyze_var_ptr(ira, &elem_ptr_instruction->base, var, - is_const, is_volatile); + return ir_analyze_var_ptr(ira, &elem_ptr_instruction->base, var); } else { return ir_analyze_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val, ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile); @@ -13603,7 +13553,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node); } - return ir_analyze_var_ptr(ira, source_instruction, var, false, false); + return ir_analyze_var_ptr(ira, source_instruction, var); } case TldIdFn: { @@ -13652,14 +13602,8 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru if (type_is_invalid(container_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *container_type; - if (container_ptr->value.type->id == TypeTableEntryIdPointer) { - container_type = container_ptr->value.type->data.pointer.child_type; - } else if (container_ptr->value.type->id == TypeTableEntryIdMetaType) { - container_type = container_ptr->value.type; - } else { - zig_unreachable(); - } + TypeTableEntry *container_type = container_ptr->value.type->data.pointer.child_type; + assert(container_ptr->value.type->id == TypeTableEntryIdPointer); Buf *field_name = field_ptr_instruction->field_name_buffer; if (!field_name) { @@ -13732,17 +13676,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru if (!container_ptr_val) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *child_type; - if (container_ptr->value.type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *ptr_type = container_ptr_val->data.x_type; - assert(ptr_type->id == TypeTableEntryIdPointer); - child_type = ptr_type->data.pointer.child_type; - } else if (container_ptr->value.type->id == TypeTableEntryIdPointer) { - ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val); - child_type = child_val->data.x_type; - } else { - zig_unreachable(); - } + assert(container_ptr->value.type->id == TypeTableEntryIdPointer); + ConstExprValue *child_val = const_ptr_pointee(ira->codegen, container_ptr_val); + TypeTableEntry *child_type = child_val->data.x_type; if (type_is_invalid(child_type)) { return ira->codegen->builtin_types.entry_invalid; @@ -17337,8 +17273,11 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio if (array_type->data.array.len == 0 && byte_alignment == 0) { byte_alignment = get_abi_alignment(ira->codegen, array_type->data.array.child_type); } + bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && + ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.is_const || is_comptime_const, + ptr_type->data.pointer.is_volatile, byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { @@ -17525,6 +17464,10 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *container_type = ir_resolve_type(ira, container); + ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + uint64_t result; if (type_is_invalid(container_type)) { return ira->codegen->builtin_types.entry_invalid; @@ -17912,15 +17855,6 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = value->value.type; - // Because we don't have Pointer Reform yet, we can't have a pointer to a 'type'. - // Therefor, we have to check for type 'type' here, so we can output a correct error - // without asserting the assert below. - if (ptr_type->id == TypeTableEntryIdMetaType) { - ir_add_error(ira, value, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == TypeTableEntryIdPointer); @@ -18697,8 +18631,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; - IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var, - !lval.is_ptr || lval.is_const, lval.is_ptr && lval.is_volatile); + IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var); if (type_is_invalid(var_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 8507470bcc..d90cc9ec22 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1298,8 +1298,8 @@ test "zig fmt: struct declaration" { \\ f1: u8, \\ pub f3: u8, \\ - \\ fn method(self: &Self) Self { - \\ return *self; + \\ fn method(self: *Self) Self { + \\ return self.*; \\ } \\ \\ f2: u8, -- cgit v1.2.3 From 019217d7a23bee69bd5ceb38aeeb5f689d5c2a9c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Jun 2018 00:07:40 -0400 Subject: fix regressions --- src/ir.cpp | 105 ++++++++++++++++++++++++++++++++++++++---------- test/compile_errors.zig | 2 +- test/gen_h.zig | 4 +- test/runtime_safety.zig | 46 ++++++++++----------- 4 files changed, 110 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 984cfd78d3..d996b4a2be 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9433,6 +9433,8 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); assert(union_field != nullptr); type_ensure_zero_bits_known(ira->codegen, union_field->type_entry); + if (type_is_invalid(union_field->type_entry)) + return ira->codegen->invalid_instruction; if (!union_field->type_entry->zero_bits) { AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( union_field->enum_field->decl_index); @@ -10015,6 +10017,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) { + ensure_complete_type(ira->codegen, wanted_type); + if (type_is_invalid(wanted_type)) + return ira->codegen->invalid_instruction; if (wanted_type->id == TypeTableEntryIdEnum) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value); if (type_is_invalid(cast1->value.type)) @@ -12766,6 +12771,10 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op TypeTableEntry *type_entry = ir_resolve_type(ira, value); if (type_is_invalid(type_entry)) return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, type_entry); + if (type_is_invalid(type_entry)) + return ira->codegen->builtin_types.entry_invalid; + switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); @@ -13187,6 +13196,9 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc bool safety_check_on = elem_ptr_instruction->safety_check_on; ensure_complete_type(ira->codegen, return_type->data.pointer.child_type); + if (type_is_invalid(return_type->data.pointer.child_type)) + return ira->codegen->builtin_types.entry_invalid; + uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); uint64_t ptr_align = return_type->data.pointer.alignment; @@ -13696,7 +13708,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } if (child_type->id == TypeTableEntryIdEnum) { ensure_complete_type(ira->codegen, child_type); - if (child_type->data.enumeration.is_invalid) + if (type_is_invalid(child_type)) return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_type_field(child_type, field_name); @@ -14569,27 +14581,27 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; TypeTableEntry *ptr_type = value->value.type; - if (ptr_type->id == TypeTableEntryIdMetaType) { + assert(ptr_type->id == TypeTableEntryIdPointer); + + TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; + if (type_is_invalid(type_entry)) { + return ira->codegen->builtin_types.entry_invalid; + } else if (type_entry->id == TypeTableEntryIdMetaType) { // surprise! actually this is just ??T not an unwrap maybe instruction - TypeTableEntry *ptr_type_ptr = ir_resolve_type(ira, value); - assert(ptr_type_ptr->id == TypeTableEntryIdPointer); - TypeTableEntry *child_type = ptr_type_ptr->data.pointer.child_type; + ConstExprValue *ptr_val = const_ptr_pointee(ira->codegen, &value->value); + assert(ptr_val->type->id == TypeTableEntryIdMetaType); + TypeTableEntry *child_type = ptr_val->data.x_type; + type_ensure_zero_bits_known(ira->codegen, child_type); TypeTableEntry *layer1 = get_maybe_type(ira->codegen, child_type); TypeTableEntry *layer2 = get_maybe_type(ira->codegen, layer1); - TypeTableEntry *result_type = get_pointer_to_type(ira->codegen, layer2, true); IrInstruction *const_instr = ir_build_const_type(&ira->new_irb, unwrap_maybe_instruction->base.scope, - unwrap_maybe_instruction->base.source_node, result_type); - ir_link_new_instruction(const_instr, &unwrap_maybe_instruction->base); - return const_instr->value.type; - } - - assert(ptr_type->id == TypeTableEntryIdPointer); - - TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { - return ira->codegen->builtin_types.entry_invalid; + unwrap_maybe_instruction->base.source_node, layer2); + IrInstruction *result_instr = ir_get_ref(ira, &unwrap_maybe_instruction->base, const_instr, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile); + ir_link_new_instruction(result_instr, &unwrap_maybe_instruction->base); + return result_instr->value.type; } else if (type_entry->id != TypeTableEntryIdMaybe) { ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); @@ -15115,6 +15127,8 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir assert(container_type->id == TypeTableEntryIdUnion); ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; if (instr_field_count != 1) { ir_add_error(ira, instruction, @@ -15182,6 +15196,8 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru } ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; size_t actual_field_count = container_type->data.structure.src_field_count; @@ -15687,6 +15703,8 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; IrInstruction *field_name_value = instruction->field_name->other; Buf *field_name = ir_resolve_str(ira, field_name_value); @@ -15740,6 +15758,9 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na assert(type_info_var->type->id == TypeTableEntryIdMetaType); ensure_complete_type(ira->codegen, type_info_var->data.x_type); + if (type_is_invalid(type_info_var->data.x_type)) + return ira->codegen->builtin_types.entry_invalid; + type_info_type = type_info_var->data.x_type; assert(type_info_type->id == TypeTableEntryIdUnion); } @@ -15765,26 +15786,37 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na VariableTableEntry *var = tld->var; ensure_complete_type(ira->codegen, var->value->type); + if (type_is_invalid(var->value->type)) + return ira->codegen->builtin_types.entry_invalid; assert(var->value->type->id == TypeTableEntryIdMetaType); return var->value->data.x_type; } -static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) +static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition"); ensure_complete_type(ira->codegen, type_info_definition_type); + if (type_is_invalid(type_info_definition_type)) + return false; + ensure_field_index(type_info_definition_type, "name", 0); ensure_field_index(type_info_definition_type, "is_pub", 1); ensure_field_index(type_info_definition_type, "data", 2); TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); ensure_complete_type(ira->codegen, type_info_definition_data_type); + if (type_is_invalid(type_info_definition_data_type)) + return false; TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); ensure_complete_type(ira->codegen, type_info_fn_def_type); + if (type_is_invalid(type_info_fn_def_type)) + return false; TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); ensure_complete_type(ira->codegen, type_info_fn_def_inline_type); + if (type_is_invalid(type_info_fn_def_inline_type)) + return false; // Loop through our definitions once to figure out how many definitions we will generate info for. auto decl_it = decls_scope->decl_table.entry_iterator(); @@ -15799,7 +15831,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); if (curr_entry->value->resolution != TldResolutionOk) { - return; + return false; } } @@ -15864,6 +15896,9 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop { VariableTableEntry *var = ((TldVar *)curr_entry->value)->var; ensure_complete_type(ira->codegen, var->value->type); + if (type_is_invalid(var->value->type)) + return false; + if (var->value->type->id == TypeTableEntryIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. @@ -15991,6 +16026,9 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop { TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry; ensure_complete_type(ira->codegen, type_entry); + if (type_is_invalid(type_entry)) + return false; + // This is a type. bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); @@ -16011,6 +16049,7 @@ static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop } assert(definition_index == definition_count); + return true; } static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) @@ -16019,6 +16058,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t assert(!type_is_invalid(type_entry)); ensure_complete_type(ira->codegen, type_entry); + if (type_is_invalid(type_entry)) + return nullptr; const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, TypeTableEntry *type_info_enum_field_type) { @@ -16246,7 +16287,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); - ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope); + if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope)) + return nullptr; break; } @@ -16401,7 +16443,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); - ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope); + if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope)) + return nullptr; break; } @@ -16412,6 +16455,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t buf_init_from_str(&ptr_field_name, "ptr"); TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; ensure_complete_type(ira->codegen, ptr_type); + if (type_is_invalid(ptr_type)) + return nullptr; buf_deinit(&ptr_field_name); result = create_ptr_like_type_info("Slice", ptr_type); @@ -16482,7 +16527,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); - ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope); + if (!ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope)) + return nullptr; break; } @@ -17502,6 +17548,11 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + + uint64_t member_index; IrInstruction *index_value = instruction->member_index->other; if (!ir_resolve_usize(ira, index_value, &member_index)) @@ -17544,6 +17595,10 @@ static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInst if (type_is_invalid(container_type)) return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, container_type); + if (type_is_invalid(container_type)) + return ira->codegen->builtin_types.entry_invalid; + uint64_t member_index; IrInstruction *index_value = instruction->member_index->other; if (!ir_resolve_usize(ira, index_value, &member_index)) @@ -18485,7 +18540,12 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; ensure_complete_type(ira->codegen, dest_type); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + ensure_complete_type(ira->codegen, src_type); + if (type_is_invalid(src_type)) + return ira->codegen->builtin_types.entry_invalid; if (get_codegen_ptr_type(src_type) != nullptr) { ir_add_error(ira, value, @@ -18724,6 +18784,9 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes)) return ira->codegen->builtin_types.entry_invalid; } else { + type_ensure_zero_bits_known(ira->codegen, child_type); + if (type_is_invalid(child_type)) + return ira->codegen->builtin_types.entry_invalid; align_bytes = get_abi_alignment(ira->codegen, child_type); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1297ed29ab..132dc8cd80 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3232,7 +3232,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ fn bar(self: *const Foo) void {} \\}; , - ".tmp_source.zig:4:4: error: variable of type '*const (integer literal)' must be const or comptime", + ".tmp_source.zig:4:4: error: variable of type '*(integer literal)' must be const or comptime", ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime", ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime", diff --git a/test/gen_h.zig b/test/gen_h.zig index 9559c3395c..e6a757ea6d 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -54,7 +54,7 @@ pub fn addCases(cases: *tests.GenHContext) void { cases.add("declare opaque type", \\export const Foo = @OpaqueType(); \\ - \\export fn entry(foo: ?&Foo) void { } + \\export fn entry(foo: ?*Foo) void { } , \\struct Foo; \\ @@ -64,7 +64,7 @@ pub fn addCases(cases: *tests.GenHContext) void { cases.add("array field-type", \\const Foo = extern struct { \\ A: [2]i32, - \\ B: [4]&u32, + \\ B: [4]*u32, \\}; \\export fn entry(foo: Foo, bar: [3]u8) void { } , diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 71d1d68764..61eba9458e 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -2,7 +2,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("calling panic", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -11,7 +11,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("out of bounds slice access", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -25,7 +25,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer addition overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -38,7 +38,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer subtraction overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -51,7 +51,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer multiplication overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -64,7 +64,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer negation overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -77,7 +77,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed integer division overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -90,7 +90,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed shift left overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("unsigned shift left overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed shift right overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -129,7 +129,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("unsigned shift right overflow", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -142,7 +142,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("integer division by zero", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -154,7 +154,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("exact division failure", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -167,7 +167,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("cast []u8 to bigger slice of wrong size", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -180,7 +180,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("value does not fit in shortening cast", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -193,7 +193,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("signed integer not fitting in cast to unsigned integer", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -206,7 +206,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("unwrap error", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ if (@import("std").mem.eql(u8, message, "attempt to unwrap error: Whatever")) { \\ @import("std").os.exit(126); // good \\ } @@ -221,7 +221,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("cast integer to global error and no code matches", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() void { @@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("cast integer to non-global error set and no match", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\const Set1 = error{A, B}; @@ -247,7 +247,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("@alignCast misaligned", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\pub fn main() !void { @@ -263,7 +263,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("bad union field access", - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} \\ @@ -277,7 +277,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ bar(&f); \\} \\ - \\fn bar(f: &Foo) void { + \\fn bar(f: *Foo) void { \\ f.float = 12.34; \\} ); @@ -287,7 +287,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("error return trace across suspend points", \\const std = @import("std"); \\ - \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ std.os.exit(126); \\} \\ -- cgit v1.2.3 From f06bce5ddaea368040560f584170aee2864fa399 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Jun 2018 04:03:25 -0400 Subject: introduce [*] for unknown length pointers See #770 Currently it does not have any different behavior than `*` but it is now recommended to use `[*]` for unknown length pointers to be future-proof. Instead of [ * ] being separate tokens as the proposal suggested, this commit implements `[*]` as a single token. --- doc/langref.html.in | 2 +- src/all_types.hpp | 1 + src/parser.cpp | 5 +++-- src/tokenizer.cpp | 31 ++++++++++++++++++++++++++++++- src/tokenizer.hpp | 1 + std/cstr.zig | 8 ++++---- std/zig/parse.zig | 2 +- std/zig/parser_test.zig | 7 +++++++ std/zig/tokenizer.zig | 42 +++++++++++++++++++++++++++++++++++++++--- 9 files changed, 87 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index fb02e6277d..217f02777f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6450,7 +6450,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/src/all_types.hpp b/src/all_types.hpp index d5906cae95..8e65cfc789 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -625,6 +625,7 @@ struct AstNodePrefixOpExpr { }; struct AstNodePointerType { + Token *star_token; AstNode *align_expr; BigInt *bit_offset_start; BigInt *bit_offset_end; diff --git a/src/parser.cpp b/src/parser.cpp index ef390a3a2e..6c900c3bfa 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1174,6 +1174,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) { AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok); + node->data.pointer_type.star_token = star_tok; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdKeywordAlign) { @@ -1211,11 +1212,11 @@ static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, To /* PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression -PrefixOp = "!" | "-" | "~" | ("*" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdStar) { + if (token->id == TokenIdStar || token->id == TokenIdBracketStarBracket) { *token_index += 1; return ast_parse_pointer_type(pc, token_index, token); } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 365b35cdfd..badbd695ec 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -219,6 +219,8 @@ enum TokenizeState { TokenizeStateSawAtSign, TokenizeStateCharCode, TokenizeStateError, + TokenizeStateLBracket, + TokenizeStateLBracketStar, }; @@ -539,8 +541,8 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); break; case '[': + t.state = TokenizeStateLBracket; begin_token(&t, TokenIdLBracket); - end_token(&t); break; case ']': begin_token(&t, TokenIdRBracket); @@ -852,6 +854,30 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; + case TokenizeStateLBracket: + switch (c) { + case '*': + t.state = TokenizeStateLBracketStar; + set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket); + break; + default: + // reinterpret as just an lbracket + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateLBracketStar: + switch (c) { + case ']': + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + invalid_char_error(&t, c); + } + break; case TokenizeStateSawPlusPercent: switch (c) { case '=': @@ -1467,12 +1493,14 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateLineString: case TokenizeStateLineStringEnd: case TokenizeStateSawBarBar: + case TokenizeStateLBracket: end_token(&t); break; case TokenizeStateSawDotDot: case TokenizeStateSawBackslash: case TokenizeStateLineStringContinue: case TokenizeStateLineStringContinueC: + case TokenizeStateLBracketStar: tokenize_error(&t, "unexpected EOF"); break; case TokenizeStateLineComment: @@ -1509,6 +1537,7 @@ const char * token_name(TokenId id) { case TokenIdBitShiftRight: return ">>"; case TokenIdBitShiftRightEq: return ">>="; case TokenIdBitXorEq: return "^="; + case TokenIdBracketStarBracket: return "[*]"; case TokenIdCharLiteral: return "CharLiteral"; case TokenIdCmpEq: return "=="; case TokenIdCmpGreaterOrEq: return ">="; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index b719293704..d659c0a772 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -28,6 +28,7 @@ enum TokenId { TokenIdBitShiftRight, TokenIdBitShiftRightEq, TokenIdBitXorEq, + TokenIdBracketStarBracket, TokenIdCharLiteral, TokenIdCmpEq, TokenIdCmpGreaterOrEq, diff --git a/std/cstr.zig b/std/cstr.zig index dfbfb8047f..d60adf8faa 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -9,13 +9,13 @@ pub const line_sep = switch (builtin.os) { else => "\n", }; -pub fn len(ptr: *const u8) usize { +pub fn len(ptr: [*]const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; } -pub fn cmp(a: *const u8, b: *const u8) i8 { +pub fn cmp(a: [*]const u8, b: [*]const u8) i8 { var index: usize = 0; while (a[index] == b[index] and a[index] != 0) : (index += 1) {} if (a[index] > b[index]) { @@ -27,11 +27,11 @@ pub fn cmp(a: *const u8, b: *const u8) i8 { } } -pub fn toSliceConst(str: *const u8) []const u8 { +pub fn toSliceConst(str: [*]const u8) []const u8 { return str[0..len(str)]; } -pub fn toSlice(str: *u8) []u8 { +pub fn toSlice(str: [*]u8) []u8 { return str[0..len(str)]; } diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 6adcf34c95..7faca8e11b 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -3292,7 +3292,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddressOf = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op{ + Token.Id.Asterisk, Token.Id.AsteriskAsterisk, Token.Id.BracketStarBracket => ast.Node.PrefixOp.Op{ .PtrType = ast.Node.PrefixOp.PtrInfo{ .align_info = null, .const_token = null, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index bad677580c..c28a70b770 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,10 @@ +test "zig fmt: pointer of unknown length" { + try testCanonical( + \\fn foo(ptr: [*]u8) void {} + \\ + ); +} + test "zig fmt: spaces around slice operator" { try testCanonical( \\var a = b[c..d]; diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 8378a9011d..b288a3adb7 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -143,6 +143,7 @@ pub const Token = struct { FloatLiteral, LineComment, DocComment, + BracketStarBracket, Keyword_align, Keyword_and, Keyword_asm, @@ -263,6 +264,8 @@ pub const Tokenizer = struct { Period, Period2, SawAtSign, + LBracket, + LBracketStar, }; pub fn next(self: *Tokenizer) Token { @@ -325,9 +328,7 @@ pub const Tokenizer = struct { break; }, '[' => { - result.id = Token.Id.LBracket; - self.index += 1; - break; + state = State.LBracket; }, ']' => { result.id = Token.Id.RBracket; @@ -429,6 +430,28 @@ pub const Tokenizer = struct { }, }, + State.LBracket => switch (c) { + '*' => { + state = State.LBracketStar; + }, + else => { + result.id = Token.Id.LBracket; + break; + }, + }, + + State.LBracketStar => switch (c) { + ']' => { + result.id = Token.Id.BracketStarBracket; + self.index += 1; + break; + }, + else => { + result.id = Token.Id.Invalid; + break; + }, + }, + State.Ampersand => switch (c) { '=' => { result.id = Token.Id.AmpersandEqual; @@ -1008,6 +1031,7 @@ pub const Tokenizer = struct { State.CharLiteralEscape2, State.CharLiteralEnd, State.StringLiteralBackslash, + State.LBracketStar, => { result.id = Token.Id.Invalid; }, @@ -1024,6 +1048,9 @@ pub const Tokenizer = struct { State.Slash => { result.id = Token.Id.Slash; }, + State.LBracket => { + result.id = Token.Id.LBracket; + }, State.Zero => { result.id = Token.Id.IntegerLiteral; }, @@ -1142,6 +1169,15 @@ test "tokenizer" { testTokenize("test", []Token.Id{Token.Id.Keyword_test}); } +test "tokenizer - unknown length pointer" { + testTokenize( + \\[*]u8 + , []Token.Id{ + Token.Id.BracketStarBracket, + Token.Id.Identifier, + }); +} + test "tokenizer - char literal with hex escape" { testTokenize( \\'\x1b' -- cgit v1.2.3 From b85b68a7fd175169e0f07ab733bde6d5654b1044 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Jun 2018 15:20:51 -0400 Subject: better compile error for error sets behind nullable --- src/ir.cpp | 17 +++++++++++++---- test/compile_errors.zig | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index d996b4a2be..5cada29076 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7934,11 +7934,20 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultReportedError; } + // implicit conversion from ?T to ?U + if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, + actual_type->data.maybe.child_type, value); + if (res != ImplicitCastMatchResultNo) + return res; + } + // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdMaybe && - ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value)) - { - return ImplicitCastMatchResultYes; + if (expected_type->id == TypeTableEntryIdMaybe) { + ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, + actual_type, value); + if (res != ImplicitCastMatchResultNo) + return res; } // implicit conversion from null literal to maybe type diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 132dc8cd80..ea1357f5bb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,23 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "invalid deref on switch target", + \\const NextError = error{NextError}; + \\const OtherError = error{OutOfMemory}; + \\ + \\export fn entry() void { + \\ const a: ?NextError!i32 = foo(); + \\} + \\ + \\fn foo() ?OtherError!i32 { + \\ return null; + \\} + , + ".tmp_source.zig:5:34: error: expected 'NextError!i32', found 'OtherError!i32'", + ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", + ); + cases.add( "invalid deref on switch target", \\comptime { -- cgit v1.2.3 From 4c273126dfc44cf4fcf9d5d97bf1cb1da07d7bd7 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Sun, 3 Jun 2018 18:30:43 +1200 Subject: Add context to zig_unreachable calls (#1039) This greatly aids debugging on platforms with no stack-traces. --- src/util.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/util.hpp b/src/util.hpp index ae33cb84af..25141d8435 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -38,11 +38,11 @@ ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF(1, 2) void zig_panic(const char *format, ...); -ATTRIBUTE_COLD -ATTRIBUTE_NORETURN -static inline void zig_unreachable(void) { - zig_panic("unreachable"); -} +#ifdef WIN32 +#define __func__ __FUNCTION__ +#endif + +#define zig_unreachable() zig_panic("unreachable: %s:%s:%d", __FILE__, __func__, __LINE__) #if defined(_MSC_VER) static inline int clzll(unsigned long long mask) { -- cgit v1.2.3 From 96164ce61377b36bcaf0c4087ca9b1ab822b9457 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 01:09:15 -0400 Subject: disallow single-item pointer indexing add pointer arithmetic for unknown length pointer --- doc/langref.html.in | 48 ++++++----- src/all_types.hpp | 9 ++ src/analyze.cpp | 53 ++++++++---- src/analyze.hpp | 2 +- src/ast_render.cpp | 8 +- src/codegen.cpp | 49 +++++++---- src/ir.cpp | 177 ++++++++++++++++++++++++++++----------- src/parser.cpp | 1 + std/buffer.zig | 2 +- std/c/darwin.zig | 4 +- std/c/index.zig | 50 +++++------ std/c/linux.zig | 2 +- std/cstr.zig | 10 +-- std/heap.zig | 18 ++-- std/os/child_process.zig | 2 +- std/os/darwin.zig | 45 +++++----- std/os/file.zig | 4 +- std/os/index.zig | 101 +++++++--------------- std/os/linux/index.zig | 123 ++++++++++++++++----------- std/os/linux/test.zig | 3 +- std/os/linux/vdso.zig | 26 +++--- std/os/windows/index.zig | 30 +++---- std/os/windows/util.zig | 2 +- std/segmented_list.zig | 12 +-- std/special/bootstrap.zig | 22 ++--- std/special/builtin.zig | 6 +- test/cases/align.zig | 49 ++++------- test/cases/const_slice_child.zig | 9 +- test/cases/for.zig | 26 +----- test/cases/misc.zig | 11 +-- test/cases/pointers.zig | 30 +++++++ test/cases/struct.zig | 6 +- test/compare_output.zig | 16 ++-- test/compile_errors.zig | 15 +++- test/translate_c.zig | 56 ++++++------- 35 files changed, 584 insertions(+), 443 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 217f02777f..32481ade50 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -458,7 +458,7 @@ test "string literals" { // A C string literal is a null terminated pointer. const null_terminated_bytes = c"hello"; - assert(@typeOf(null_terminated_bytes) == *const u8); + assert(@typeOf(null_terminated_bytes) == [*]const u8); assert(null_terminated_bytes[5] == 0); } {#code_end#} @@ -547,7 +547,7 @@ const c_string_literal = ; {#code_end#}

    - In this example the variable c_string_literal has type *const char and + In this example the variable c_string_literal has type [*]const char and has a terminating null byte.

    {#see_also|@embedFile#} @@ -1288,7 +1288,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; // array literal -const message = []u8{'h', 'e', 'l', 'l', 'o'}; +const message = []u8{ 'h', 'e', 'l', 'l', 'o' }; // get the size of an array comptime { @@ -1324,11 +1324,11 @@ test "modify an array" { // array concatenation works if the values are known // at compile time -const part_one = []i32{1, 2, 3, 4}; -const part_two = []i32{5, 6, 7, 8}; +const part_one = []i32{ 1, 2, 3, 4 }; +const part_two = []i32{ 5, 6, 7, 8 }; const all_of_it = part_one ++ part_two; comptime { - assert(mem.eql(i32, all_of_it, []i32{1,2,3,4,5,6,7,8})); + assert(mem.eql(i32, all_of_it, []i32{ 1, 2, 3, 4, 5, 6, 7, 8 })); } // remember that string literals are arrays @@ -1357,7 +1357,7 @@ comptime { var fancy_array = init: { var initial_value: [10]Point = undefined; for (initial_value) |*pt, i| { - pt.* = Point { + pt.* = Point{ .x = i32(i), .y = i32(i) * 2, }; @@ -1377,7 +1377,7 @@ test "compile-time array initalization" { // call a function to initialize an array var more_points = []Point{makePoint(3)} ** 10; fn makePoint(x: i32) Point { - return Point { + return Point{ .x = x, .y = x * 2, }; @@ -1414,25 +1414,24 @@ test "address of syntax" { } test "pointer array access" { - // Pointers do not support pointer arithmetic. If you - // need such a thing, use array index syntax: + // Taking an address of an individual element gives a + // pointer to a single item. This kind of pointer + // does not support pointer arithmetic. var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - const ptr = &array[1]; + const ptr = &array[2]; + assert(@typeOf(ptr) == *u8); assert(array[2] == 3); - ptr[1] += 1; + ptr.* += 1; assert(array[2] == 4); } test "pointer slicing" { // In Zig, we prefer using slices over null-terminated pointers. - // You can turn a pointer into a slice using slice syntax: + // You can turn an array into a slice using slice syntax: var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - const ptr = &array[1]; - const slice = ptr[1..3]; - - assert(slice.ptr == &ptr[1]); + const slice = array[2..4]; assert(slice.len == 2); // Slices have bounds checking and are therefore protected @@ -1622,18 +1621,27 @@ fn foo(bytes: []u8) u32 { const assert = @import("std").debug.assert; test "basic slices" { - var array = []i32{1, 2, 3, 4}; + var array = []i32{ 1, 2, 3, 4 }; // A slice is a pointer and a length. The difference between an array and // a slice is that the array's length is part of the type and known at // compile-time, whereas the slice's length is known at runtime. // Both can be accessed with the `len` field. const slice = array[0..array.len]; - assert(slice.ptr == &array[0]); + assert(&slice[0] == &array[0]); assert(slice.len == array.len); + // Using the address-of operator on a slice gives a pointer to a single + // item, while using the `ptr` field gives an unknown length pointer. + assert(@typeOf(slice.ptr) == [*]i32); + assert(@typeOf(&slice[0]) == *i32); + assert(@ptrToInt(slice.ptr) == @ptrToInt(&slice[0])); + // Slices have array bounds checking. If you try to access something out // of bounds, you'll get a safety check failure: slice[10] += 1; + + // Note that `slice.ptr` does not invoke safety checking, while `&slice[0]` + // asserts that the slice has len >= 1. } {#code_end#}

    This is one reason we prefer slices to pointers.

    @@ -5937,7 +5945,7 @@ pub const __zig_test_fn_slice = {}; // overwritten later {#header_open|C String Literals#} {#code_begin|exe#} {#link_libc#} -extern fn puts(*const u8) void; +extern fn puts([*]const u8) void; pub fn main() void { puts(c"this has a null terminator"); diff --git a/src/all_types.hpp b/src/all_types.hpp index 8e65cfc789..f1cf96238f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -974,8 +974,14 @@ struct FnTypeId { uint32_t fn_type_id_hash(FnTypeId*); bool fn_type_id_eql(FnTypeId *a, FnTypeId *b); +enum PtrLen { + PtrLenUnknown, + PtrLenSingle, +}; + struct TypeTableEntryPointer { TypeTableEntry *child_type; + PtrLen ptr_len; bool is_const; bool is_volatile; uint32_t alignment; @@ -1397,6 +1403,7 @@ struct TypeId { union { struct { TypeTableEntry *child_type; + PtrLen ptr_len; bool is_const; bool is_volatile; uint32_t alignment; @@ -2268,6 +2275,7 @@ struct IrInstructionElemPtr { IrInstruction *array_ptr; IrInstruction *elem_index; + PtrLen ptr_len; bool is_const; bool safety_check_on; }; @@ -2419,6 +2427,7 @@ struct IrInstructionPtrType { IrInstruction *child_type; uint32_t bit_offset_start; uint32_t bit_offset_end; + PtrLen ptr_len; bool is_const; bool is_volatile; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index a5011035c5..2b9d776e78 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -381,14 +381,14 @@ TypeTableEntry *get_promise_type(CodeGen *g, TypeTableEntry *result_type) { } TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, - bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(!type_is_invalid(child_type)); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; uint32_t abi_alignment = get_abi_alignment(g, child_type); - if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment) { + if (unaligned_bit_count != 0 || is_volatile || byte_alignment != abi_alignment || ptr_len != PtrLenSingle) { type_id.id = TypeTableEntryIdPointer; type_id.data.pointer.child_type = child_type; type_id.data.pointer.is_const = is_const; @@ -396,6 +396,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type type_id.data.pointer.alignment = byte_alignment; type_id.data.pointer.bit_offset = bit_offset; type_id.data.pointer.unaligned_bit_count = unaligned_bit_count; + type_id.data.pointer.ptr_len = ptr_len; auto existing_entry = g->type_table.maybe_get(type_id); if (existing_entry) @@ -414,16 +415,17 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); entry->is_copyable = true; + const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]"; const char *const_str = is_const ? "const " : ""; const char *volatile_str = is_volatile ? "volatile " : ""; buf_resize(&entry->name, 0); if (unaligned_bit_count == 0 && byte_alignment == abi_alignment) { - buf_appendf(&entry->name, "*%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name)); } else if (unaligned_bit_count == 0) { - buf_appendf(&entry->name, "*align(%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment, const_str, volatile_str, buf_ptr(&child_type->name)); } else { - buf_appendf(&entry->name, "*align(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", byte_alignment, + buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment, bit_offset, bit_offset + unaligned_bit_count, const_str, volatile_str, buf_ptr(&child_type->name)); } @@ -433,7 +435,9 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type if (!entry->zero_bits) { assert(byte_alignment > 0); - if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment) { + if (is_const || is_volatile || unaligned_bit_count != 0 || byte_alignment != abi_alignment || + ptr_len != PtrLenSingle) + { TypeTableEntry *peer_type = get_pointer_to_type(g, child_type, false); entry->type_ref = peer_type->type_ref; entry->di_type = peer_type->di_type; @@ -451,6 +455,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type entry->di_type = g->builtin_types.entry_void->di_type; } + entry->data.pointer.ptr_len = ptr_len; entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; entry->data.pointer.is_volatile = is_volatile; @@ -467,7 +472,8 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type } TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) { - return get_pointer_to_type_extra(g, child_type, is_const, false, get_abi_alignment(g, child_type), 0, 0); + return get_pointer_to_type_extra(g, child_type, is_const, false, PtrLenSingle, + get_abi_alignment(g, child_type), 0, 0); } TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) { @@ -757,6 +763,7 @@ static void slice_type_common_init(CodeGen *g, TypeTableEntry *pointer_type, Typ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { assert(ptr_type->id == TypeTableEntryIdPointer); + assert(ptr_type->data.pointer.ptr_len == PtrLenUnknown); TypeTableEntry **parent_pointer = &ptr_type->data.pointer.slice_parent; if (*parent_pointer) { @@ -768,14 +775,16 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { // replace the & with [] to go from a ptr type name to a slice type name buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + 1); + size_t name_offset = (ptr_type->data.pointer.ptr_len == PtrLenSingle) ? 1 : 3; + buf_appendf(&entry->name, "[]%s", buf_ptr(&ptr_type->name) + name_offset); TypeTableEntry *child_type = ptr_type->data.pointer.child_type; - uint32_t abi_alignment; + uint32_t abi_alignment = get_abi_alignment(g, child_type); if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || - ptr_type->data.pointer.alignment != (abi_alignment = get_abi_alignment(g, child_type))) + ptr_type->data.pointer.alignment != abi_alignment) { - TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, child_type, false); + TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, + PtrLenUnknown, abi_alignment, 0, 0); TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); slice_type_common_init(g, ptr_type, entry); @@ -799,9 +808,11 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) { if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile || child_ptr_type->data.pointer.alignment != get_abi_alignment(g, grand_child_type)) { - TypeTableEntry *bland_child_ptr_type = get_pointer_to_type(g, grand_child_type, false); + TypeTableEntry *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false, + PtrLenUnknown, get_abi_alignment(g, grand_child_type), 0, 0); TypeTableEntry *bland_child_slice = get_slice_type(g, bland_child_ptr_type); - TypeTableEntry *peer_ptr_type = get_pointer_to_type(g, bland_child_slice, false); + TypeTableEntry *peer_ptr_type = get_pointer_to_type_extra(g, bland_child_slice, false, false, + PtrLenUnknown, get_abi_alignment(g, bland_child_slice), 0, 0); TypeTableEntry *peer_slice_type = get_slice_type(g, peer_ptr_type); entry->type_ref = peer_slice_type->type_ref; @@ -1284,7 +1295,8 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_ } static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) { - TypeTableEntry *ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, ptr_type); IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); if (type_is_invalid(instr->value.type)) @@ -2954,7 +2966,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { if (fn_type_id->param_count != 2) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *const_u8_ptr = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *const_u8_slice = get_slice_type(g, const_u8_ptr); if (fn_type_id->param_info[0].type != const_u8_slice) { return wrong_panic_prototype(g, proto_node, fn_type); @@ -4994,7 +5007,9 @@ void init_const_c_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str) { // then make the pointer point to it const_val->special = ConstValSpecialStatic; - const_val->type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + // TODO make this `[*]null u8` instead of `[*]u8` + const_val->type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = 0; @@ -5135,7 +5150,9 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr { assert(array_val->type->id == TypeTableEntryIdArray); - TypeTableEntry *ptr_type = get_pointer_to_type(g, array_val->type->data.array.child_type, is_const); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, array_val->type->data.array.child_type, + is_const, false, PtrLenUnknown, get_abi_alignment(g, array_val->type->data.array.child_type), + 0, 0); const_val->special = ConstValSpecialStatic; const_val->type = get_slice_type(g, ptr_type); @@ -5759,6 +5776,7 @@ uint32_t type_id_hash(TypeId x) { return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); case TypeTableEntryIdPointer: return hash_ptr(x.data.pointer.child_type) + + ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + (((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) + @@ -5807,6 +5825,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdPointer: return a.data.pointer.child_type == b.data.pointer.child_type && + a.data.pointer.ptr_len == b.data.pointer.ptr_len && a.data.pointer.is_const == b.data.pointer.is_const && a.data.pointer.is_volatile == b.data.pointer.is_volatile && a.data.pointer.alignment == b.data.pointer.alignment && diff --git a/src/analyze.hpp b/src/analyze.hpp index d538f042ce..905bfa86dd 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -16,7 +16,7 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const, - bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); + bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f356f406b0..3785cb6ca1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -625,7 +625,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypePointerType: { if (!grouped) fprintf(ar->f, "("); - fprintf(ar->f, "*"); + const char *star = "[*]"; + if (node->data.pointer_type.star_token != nullptr && + (node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar)) + { + star = "*"; + } + fprintf(ar->f, "%s", star); if (node->data.pointer_type.align_expr != nullptr) { fprintf(ar->f, "align("); render_node_grouped(ar, node->data.pointer_type.align_expr); diff --git a/src/codegen.cpp b/src/codegen.cpp index d07d427729..64e29a4da4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -893,7 +893,8 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { assert(val->global_refs->llvm_global); } - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); return LLVMConstBitCast(val->global_refs->llvm_global, LLVMPointerType(str_type->type_ref, 0)); } @@ -1461,7 +1462,8 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMValueRef full_buf_ptr = LLVMConstInBoundsGEP(global_array, full_buf_ptr_indices, 2); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); LLVMValueRef global_slice_fields[] = { full_buf_ptr, @@ -2212,9 +2214,13 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, IrInstruction *op2 = bin_op_instruction->op2; assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy || - op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || - op_id == IrBinOpBitShiftRightExact || - (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet)); + op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || + op_id == IrBinOpBitShiftRightExact || + (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet) || + (op1->value.type->id == TypeTableEntryIdPointer && + (op_id == IrBinOpAdd || op_id == IrBinOpSub) && + op1->value.type->data.pointer.ptr_len == PtrLenUnknown) + ); TypeTableEntry *type_entry = op1->value.type; bool want_runtime_safety = bin_op_instruction->safety_check_on && @@ -2222,6 +2228,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, LLVMValueRef op1_value = ir_llvm_value(g, op1); LLVMValueRef op2_value = ir_llvm_value(g, op2); + + switch (op_id) { case IrBinOpInvalid: case IrBinOpArrayCat: @@ -2260,7 +2268,11 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } case IrBinOpAdd: case IrBinOpAddWrap: - if (type_entry->id == TypeTableEntryIdFloat) { + if (type_entry->id == TypeTableEntryIdPointer) { + assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); + // TODO runtime safety + return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, ""); + } else if (type_entry->id == TypeTableEntryIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdInt) { @@ -2323,7 +2335,12 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } case IrBinOpSub: case IrBinOpSubWrap: - if (type_entry->id == TypeTableEntryIdFloat) { + if (type_entry->id == TypeTableEntryIdPointer) { + assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); + // TODO runtime safety + LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); + return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); + } else if (type_entry->id == TypeTableEntryIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdInt) { @@ -2770,7 +2787,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, if (have_init_expr) { assert(var->value->type == init_value->value.type); TypeTableEntry *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, - var->align_bytes, 0, 0); + PtrLenSingle, var->align_bytes, 0, 0); gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value)); } else { bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); @@ -4172,7 +4189,7 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable, uint32_t field_align_bytes = get_abi_alignment(g, type_struct_field->type_entry); TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_struct_field->type_entry, - false, false, field_align_bytes, + false, false, PtrLenSingle, field_align_bytes, (uint32_t)type_struct_field->packed_bits_offset, (uint32_t)type_struct_field->unaligned_bit_count); gen_assign_raw(g, field_ptr, ptr_type, value); @@ -4188,7 +4205,7 @@ static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, I uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry); TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry, - false, false, field_align_bytes, + false, false, PtrLenSingle, field_align_bytes, 0, 0); LLVMValueRef uncasted_union_ptr; @@ -4435,7 +4452,8 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f LLVMPositionBuilderAtEnd(g->builder, ok_block); LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_payload_index, ""); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *slice_type = get_slice_type(g, u8_ptr_type); size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, payload_ptr, ptr_field_index, ""); @@ -5377,7 +5395,8 @@ static void generate_error_name_table(CodeGen *g) { assert(g->errors_by_index.length > 0); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); LLVMValueRef *values = allocate(g->errors_by_index.length); @@ -5415,7 +5434,8 @@ static void generate_error_name_table(CodeGen *g) { } static void generate_enum_name_tables(CodeGen *g) { - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); TypeTableEntry *usize = g->builtin_types.entry_usize; @@ -6869,7 +6889,8 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { exit(0); } - TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); TypeTableEntry *fn_type = get_test_fn_type(g); diff --git a/src/ir.cpp b/src/ir.cpp index 5cada29076..a230c60456 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1009,12 +1009,13 @@ static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *so } static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr, - IrInstruction *elem_index, bool safety_check_on) + IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len) { IrInstructionElemPtr *instruction = ir_build_instruction(irb, scope, source_node); instruction->array_ptr = array_ptr; instruction->elem_index = elem_index; instruction->safety_check_on = safety_check_on; + instruction->ptr_len = ptr_len; ir_ref_instruction(array_ptr, irb->current_basic_block); ir_ref_instruction(elem_index, irb->current_basic_block); @@ -1022,15 +1023,6 @@ static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, - IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on) -{ - IrInstruction *new_instruction = ir_build_elem_ptr(irb, old_instruction->scope, - old_instruction->source_node, array_ptr, elem_index, safety_check_on); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container_ptr, IrInstruction *field_name_expr) { @@ -1188,14 +1180,15 @@ static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instru } static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, - uint32_t bit_offset_start, uint32_t bit_offset_end) + IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, + IrInstruction *align_value, uint32_t bit_offset_start, uint32_t bit_offset_end) { IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction(irb, scope, source_node); ptr_type_of_instruction->align_value = align_value; ptr_type_of_instruction->child_type = child_type; ptr_type_of_instruction->is_const = is_const; ptr_type_of_instruction->is_volatile = is_volatile; + ptr_type_of_instruction->ptr_len = ptr_len; ptr_type_of_instruction->bit_offset_start = bit_offset_start; ptr_type_of_instruction->bit_offset_end = bit_offset_end; @@ -3547,7 +3540,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode return subscript_instruction; IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, - subscript_instruction, true); + subscript_instruction, true, PtrLenSingle); if (lval.is_ptr) return ptr_instruction; @@ -4626,6 +4619,11 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); + // The null check here is for C imports which don't set a token on the AST node. We could potentially + // update that code to create a fake token and then remove this check. + PtrLen ptr_len = (node->data.pointer_type.star_token != nullptr && + (node->data.pointer_type.star_token->id == TokenIdStar || + node->data.pointer_type.star_token->id == TokenIdStarStar)) ? PtrLenSingle : PtrLenUnknown; bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; AstNode *expr_node = node->data.pointer_type.op_expr; @@ -4675,7 +4673,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode } return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, - align_value, bit_offset_start, bit_offset_end); + ptr_len, align_value, bit_offset_start, bit_offset_end); } static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, @@ -5172,7 +5170,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false); + IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle); IrInstruction *elem_val; if (node->data.for_expr.elem_is_ptr) { elem_val = elem_ptr; @@ -6811,9 +6809,13 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final); if (type_has_bits(return_type)) { + IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, + get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8), + 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); @@ -7691,6 +7693,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry // pointer const if (expected_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && + (actual_type->data.pointer.ptr_len == expected_type->data.pointer.ptr_len) && (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && @@ -8644,7 +8647,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (convert_to_const_slice) { assert(prev_inst->value.type->id == TypeTableEntryIdArray); - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra( + ira->codegen, prev_inst->value.type->data.array.child_type, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, prev_inst->value.type->data.array.child_type), + 0, 0); TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type); if (err_set_type != nullptr) { return get_error_union_type(ira->codegen, err_set_type, slice_type); @@ -8961,7 +8968,7 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) { TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, ptr_align, 0, 0); + ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0); IrInstruction *const_instr = ir_get_const(ira, instruction); ConstExprValue *const_val = &const_instr->value; const_val->type = ptr_type; @@ -9302,7 +9309,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi } TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type, - is_const, is_volatile, get_abi_alignment(ira->codegen, value->value.type), 0, 0); + is_const, is_volatile, PtrLenSingle, get_abi_alignment(ira->codegen, value->value.type), 0, 0); 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; @@ -10399,7 +10406,9 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { if (type_is_invalid(value->value.type)) return nullptr; - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(ira->codegen, ptr_type); IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type); if (type_is_invalid(casted_value->value.type)) @@ -11054,11 +11063,27 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; + IrBinOp op_id = bin_op_instruction->op_id; + + // look for pointer math + if (op1->value.type->id == TypeTableEntryIdPointer && op1->value.type->data.pointer.ptr_len == PtrLenUnknown && + (op_id == IrBinOpAdd || op_id == IrBinOpSub)) + { + IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); + if (casted_op2 == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope, + bin_op_instruction->base.source_node, op_id, op1, casted_op2, true); + result->value.type = op1->value.type; + ir_link_new_instruction(result, &bin_op_instruction->base); + return result->value.type; + } + IrInstruction *instructions[] = {op1, op2}; TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; - IrBinOp op_id = bin_op_instruction->op_id; bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt; bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat; @@ -11331,7 +11356,8 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * out_array_val = out_val; } else if (is_slice(op1_type) || is_slice(op2_type)) { - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, child_type, true); + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, + true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0); result_type = get_slice_type(ira->codegen, ptr_type); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -11351,7 +11377,9 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * } else { new_len += 1; // null byte - result_type = get_pointer_to_type(ira->codegen, child_type, true); + // TODO make this `[*]null T` instead of `[*]T` + result_type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, + PtrLenUnknown, get_abi_alignment(ira->codegen, child_type), 0, 0); out_array_val = create_const_vals(1); out_array_val->special = ConstValSpecialStatic; @@ -12173,7 +12201,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, - var->src_is_const, is_volatile, var->align_bytes, 0, 0); + var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); type_ensure_zero_bits_known(ira->codegen, var->value->type); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -12352,7 +12380,9 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction *casted_new_stack = nullptr; if (call_instruction->new_stack != nullptr) { - TypeTableEntry *u8_ptr = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *new_stack = call_instruction->new_stack->other; if (type_is_invalid(new_stack->value.type)) @@ -13112,10 +13142,21 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui return get_pointer_to_type_extra(g, ptr_type->data.pointer.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, new_align, ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); } +static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) { + assert(ptr_type->id == TypeTableEntryIdPointer); + return get_pointer_to_type_extra(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_len, + ptr_type->data.pointer.alignment, + ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); +} + static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) { IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other; if (type_is_invalid(array_ptr->value.type)) @@ -13146,6 +13187,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc if (ptr_type->data.pointer.unaligned_bit_count == 0) { return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, ptr_type->data.pointer.alignment, 0, 0); } else { uint64_t elem_val_scalar; @@ -13157,12 +13199,19 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, 1, (uint32_t)bit_offset, (uint32_t)bit_width); } } else if (array_type->id == TypeTableEntryIdPointer) { - return_type = array_type; + if (array_type->data.pointer.ptr_len == PtrLenSingle) { + ir_add_error_node(ira, elem_ptr_instruction->base.source_node, + buf_sprintf("indexing not allowed on pointer to single item")); + return ira->codegen->builtin_types.entry_invalid; + } + return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); } else if (is_slice(array_type)) { - return_type = array_type->data.structure.fields[slice_ptr_index].type_entry; + return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry, + elem_ptr_instruction->ptr_len); } else if (array_type->id == TypeTableEntryIdArgTuple) { ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); if (!ptr_val) @@ -13304,8 +13353,10 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } else if (is_slice(array_type)) { ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index]; if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, - casted_elem_index, false); + IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, + array_ptr, casted_elem_index, false, elem_ptr_instruction->ptr_len); + result->value.type = return_type; + ir_link_new_instruction(result, &elem_ptr_instruction->base); return return_type; } ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index]; @@ -13373,8 +13424,10 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } } - ir_build_elem_ptr_from(&ira->new_irb, &elem_ptr_instruction->base, array_ptr, - casted_elem_index, safety_check_on); + IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, + array_ptr, casted_elem_index, safety_check_on, elem_ptr_instruction->ptr_len); + result->value.type = return_type; + ir_link_new_instruction(result, &elem_ptr_instruction->base); return return_type; } @@ -13449,7 +13502,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ira->codegen->invalid_instruction; ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_val->type, - is_const, is_volatile, align_bytes, + is_const, is_volatile, PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); IrInstruction *result = ir_get_const(ira, source_instr); @@ -13465,6 +13518,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, + PtrLenSingle, align_bytes, (uint32_t)(ptr_bit_offset + field->packed_bits_offset), (uint32_t)unaligned_bit_count_for_result_type); @@ -13511,7 +13565,9 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ payload_val->type = field_type; } - TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, + TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, + PtrLenSingle, get_abi_alignment(ira->codegen, field_type), 0, 0); IrInstruction *result = ir_get_const(ira, source_instr); @@ -13526,7 +13582,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, - get_abi_alignment(ira->codegen, field->type_entry), 0, 0); + PtrLenSingle, get_abi_alignment(ira->codegen, field->type_entry), 0, 0); return result; } else { return ir_analyze_container_member_access_inner(ira, bare_type, field_name, @@ -14119,7 +14175,7 @@ static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, if (type_entry->id == TypeTableEntryIdArray) { ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); } else if (is_slice(type_entry)) { - ptr_type = type_entry->data.structure.fields[0].type_entry; + ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle); } else if (type_entry->id == TypeTableEntryIdArgTuple) { ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad); if (!arg_tuple_val) @@ -14367,7 +14423,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, { type_ensure_zero_bits_known(ira->codegen, child_type); TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, - is_const, is_volatile, align_bytes, 0, 0); + is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0); TypeTableEntry *result_type = get_slice_type(ira->codegen, slice_ptr_type); ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base); out_val->data.x_type = result_type; @@ -14619,6 +14675,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, get_abi_alignment(ira->codegen, child_type), 0, 0); if (instr_is_comptime(value)) { @@ -15566,7 +15623,8 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { ErrorTableEntry *err = casted_value->value.data.x_err_set; @@ -15607,7 +15665,11 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); - TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), + 0, 0); result->value.type = get_slice_type(ira->codegen, u8_ptr_type); return result->value.type; } @@ -15660,6 +15722,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, TypeTableEntry *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, field_ptr->value.type->data.pointer.is_const, field_ptr->value.type->data.pointer.is_volatile, + PtrLenSingle, field_ptr_align, 0, 0); IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); if (type_is_invalid(casted_field_ptr->value.type)) @@ -15668,6 +15731,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, container_type, casted_field_ptr->value.type->data.pointer.is_const, casted_field_ptr->value.type->data.pointer.is_volatile, + PtrLenSingle, parent_ptr_align, 0, 0); if (instr_is_comptime(casted_field_ptr)) { @@ -15983,11 +16047,13 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop // lib_name: ?[]const u8 ensure_field_index(fn_def_val->type, "lib_name", 6); fn_def_fields[6].special = ConstValSpecialStatic; - fn_def_fields[6].type = get_maybe_type(ira->codegen, - get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen, - ira->codegen->builtin_types.entry_u8, true))); - if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) - { + TypeTableEntry *u8_ptr = get_pointer_to_type_extra( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), + 0, 0); + fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); + if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_maybe = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); @@ -16009,8 +16075,8 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop size_t fn_arg_count = fn_entry->variable_list.length; ConstExprValue *fn_arg_name_array = create_const_vals(1); fn_arg_name_array->special = ConstValSpecialStatic; - fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, - get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true)), fn_arg_count); + fn_arg_name_array->type = get_array_type(ira->codegen, + get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count); @@ -17088,7 +17154,8 @@ static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructi TypeTableEntry *u8 = ira->codegen->builtin_types.entry_u8; uint32_t dest_align = (dest_uncasted_type->id == TypeTableEntryIdPointer) ? dest_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); - TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, dest_align, 0, 0); + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, + PtrLenUnknown, dest_align, 0, 0); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -17184,8 +17251,10 @@ static TypeTableEntry *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructi src_uncasted_type->data.pointer.alignment : get_abi_alignment(ira->codegen, u8); TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; - TypeTableEntry *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, dest_align, 0, 0); - TypeTableEntry *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, src_align, 0, 0); + TypeTableEntry *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, + PtrLenUnknown, dest_align, 0, 0); + TypeTableEntry *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, + PtrLenUnknown, src_align, 0, 0); IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); if (type_is_invalid(casted_dest_ptr->value.type)) @@ -17333,11 +17402,13 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, ptr_type->data.pointer.is_const || is_comptime_const, ptr_type->data.pointer.is_volatile, + PtrLenUnknown, byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, array_type->data.pointer.alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); if (!end) { @@ -17774,6 +17845,7 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst if (result_ptr->value.type->id == TypeTableEntryIdPointer) { expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, false, result_ptr->value.type->data.pointer.is_volatile, + PtrLenSingle, result_ptr->value.type->data.pointer.alignment, 0, 0); } else { expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); @@ -17929,6 +18001,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, get_abi_alignment(ira->codegen, payload_type), 0, 0); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); @@ -18270,7 +18343,8 @@ static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructio return ir_unreach_error(ira); } - TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type); if (type_is_invalid(casted_msg->value.type)) @@ -18801,7 +18875,8 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type, - instruction->is_const, instruction->is_volatile, align_bytes, + instruction->is_const, instruction->is_volatile, + instruction->ptr_len, align_bytes, instruction->bit_offset_start, instruction->bit_offset_end - instruction->bit_offset_start); return ira->codegen->builtin_types.entry_type; diff --git a/src/parser.cpp b/src/parser.cpp index 6c900c3bfa..3ad2de906b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1225,6 +1225,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, AstNode *child_node = ast_parse_pointer_type(pc, token_index, token); child_node->column += 1; AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token); + parent_node->data.pointer_type.star_token = token; parent_node->data.pointer_type.op_expr = child_node; return parent_node; } diff --git a/std/buffer.zig b/std/buffer.zig index 305746e183..3b2936d223 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -122,7 +122,7 @@ pub const Buffer = struct { } /// For passing to C functions. - pub fn ptr(self: *const Buffer) *u8 { + pub fn ptr(self: *const Buffer) [*]u8 { return self.list.items.ptr; } }; diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 69395e6b27..e3b53d9bea 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,7 +1,7 @@ extern "c" fn __error() *c_int; -pub extern "c" fn _NSGetExecutablePath(buf: *u8, bufsize: *u32) c_int; +pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; -pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: *u8, buf_len: usize, basep: *i64) usize; +pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize; pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; diff --git a/std/c/index.zig b/std/c/index.zig index 114b79cdae..ade37f36c1 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -9,6 +9,8 @@ pub use switch (builtin.os) { }; const empty_import = @import("../empty.zig"); +// TODO https://github.com/ziglang/zig/issues/265 on this whole file + pub extern "c" fn abort() noreturn; pub extern "c" fn exit(code: c_int) noreturn; pub extern "c" fn isatty(fd: c_int) c_int; @@ -16,45 +18,45 @@ pub extern "c" fn close(fd: c_int) c_int; pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; -pub extern "c" fn open(path: *const u8, oflag: c_int, ...) c_int; +pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; -pub extern "c" fn stat(noalias path: *const u8, noalias buf: *Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; -pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; -pub extern "c" fn unlink(path: *const u8) c_int; -pub extern "c" fn getcwd(buf: *u8, size: usize) ?*u8; +pub extern "c" fn read(fd: c_int, buf: [*]c_void, nbyte: usize) isize; +pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; +pub extern "c" fn write(fd: c_int, buf: [*]const c_void, nbyte: usize) isize; +pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void; +pub extern "c" fn munmap(addr: [*]c_void, len: usize) c_int; +pub extern "c" fn unlink(path: [*]const u8) c_int; +pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; pub extern "c" fn fork() c_int; -pub extern "c" fn access(path: *const u8, mode: c_uint) c_int; -pub extern "c" fn pipe(fds: *c_int) c_int; -pub extern "c" fn mkdir(path: *const u8, mode: c_uint) c_int; -pub extern "c" fn symlink(existing: *const u8, new: *const u8) c_int; -pub extern "c" fn rename(old: *const u8, new: *const u8) c_int; -pub extern "c" fn chdir(path: *const u8) c_int; -pub extern "c" fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) c_int; +pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn pipe(fds: *[2]c_int) c_int; +pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; +pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; +pub extern "c" fn chdir(path: [*]const u8) c_int; +pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; pub extern "c" fn dup(fd: c_int) c_int; pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; -pub extern "c" fn readlink(noalias path: *const u8, noalias buf: *u8, bufsize: usize) isize; -pub extern "c" fn realpath(noalias file_name: *const u8, noalias resolved_name: *u8) ?*u8; +pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; +pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int; pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; -pub extern "c" fn rmdir(path: *const u8) c_int; +pub extern "c" fn rmdir(path: [*]const u8) c_int; -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; -pub extern "c" fn malloc(usize) ?*c_void; -pub extern "c" fn realloc(*c_void, usize) ?*c_void; -pub extern "c" fn free(*c_void) void; -pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void; +pub extern "c" fn malloc(usize) ?[*]c_void; +pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void; +pub extern "c" fn free([*]c_void) void; +pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int; pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: [*]c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; diff --git a/std/c/linux.zig b/std/c/linux.zig index 0ab043533e..2699e9bd09 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,6 +1,6 @@ pub use @import("../os/linux/errno.zig"); -pub extern "c" fn getrandom(buf_ptr: *u8, buf_len: usize, flags: c_uint) c_int; +pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; extern "c" fn __errno_location() *c_int; pub const _errno = __errno_location; diff --git a/std/cstr.zig b/std/cstr.zig index d60adf8faa..d9106769c1 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -57,7 +57,7 @@ pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![]u8 { pub const NullTerminated2DArray = struct { allocator: *mem.Allocator, byte_count: usize, - ptr: ?*?*u8, + ptr: ?[*]?[*]u8, /// Takes N lists of strings, concatenates the lists together, and adds a null terminator /// Caller must deinit result @@ -79,12 +79,12 @@ pub const NullTerminated2DArray = struct { errdefer allocator.free(buf); var write_index = index_size; - const index_buf = ([]?*u8)(buf); + const index_buf = ([]?[*]u8)(buf); var i: usize = 0; for (slices) |slice| { for (slice) |inner| { - index_buf[i] = &buf[write_index]; + index_buf[i] = buf.ptr + write_index; i += 1; mem.copy(u8, buf[write_index..], inner); write_index += inner.len; @@ -97,12 +97,12 @@ pub const NullTerminated2DArray = struct { return NullTerminated2DArray{ .allocator = allocator, .byte_count = byte_count, - .ptr = @ptrCast(?*?*u8, buf.ptr), + .ptr = @ptrCast(?[*]?[*]u8, buf.ptr), }; } pub fn deinit(self: *NullTerminated2DArray) void { - const buf = @ptrCast(*u8, self.ptr); + const buf = @ptrCast([*]u8, self.ptr); self.allocator.free(buf[0..self.byte_count]); } }; diff --git a/std/heap.zig b/std/heap.zig index d15a99a757..0b8f4aeb3f 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -18,11 +18,11 @@ var c_allocator_state = Allocator{ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { assert(alignment <= @alignOf(c_longdouble)); - return if (c.malloc(n)) |buf| @ptrCast(*u8, buf)[0..n] else error.OutOfMemory; + return if (c.malloc(n)) |buf| @ptrCast([*]u8, buf)[0..n] else error.OutOfMemory; } fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - const old_ptr = @ptrCast(*c_void, old_mem.ptr); + const old_ptr = @ptrCast([*]c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { return @ptrCast(*u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { @@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![ } fn cFree(self: *Allocator, old_mem: []u8) void { - const old_ptr = @ptrCast(*c_void, old_mem.ptr); + const old_ptr = @ptrCast([*]c_void, old_mem.ptr); c.free(old_ptr); } @@ -74,7 +74,7 @@ pub const DirectAllocator = struct { const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr(*u8, addr)[0..n]; + if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n]; var aligned_addr = addr & ~usize(alignment - 1); aligned_addr += alignment; @@ -93,7 +93,7 @@ pub const DirectAllocator = struct { //It is impossible that there is an unoccupied page at the top of our // mmap. - return @intToPtr(*u8, aligned_addr)[0..n]; + return @intToPtr([*]u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); @@ -109,7 +109,7 @@ pub const DirectAllocator = struct { const adjusted_addr = root_addr + march_forward_bytes; const record_addr = adjusted_addr + n; @intToPtr(*align(1) usize, record_addr).* = root_addr; - return @intToPtr(*u8, adjusted_addr)[0..n]; + return @intToPtr([*]u8, adjusted_addr)[0..n]; }, else => @compileError("Unsupported OS"), } @@ -140,7 +140,7 @@ pub const DirectAllocator = struct { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; - const old_ptr = @intToPtr(os.windows.LPVOID, root_addr); + const old_ptr = @intToPtr([*]c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; @@ -154,7 +154,7 @@ pub const DirectAllocator = struct { assert(new_adjusted_addr % alignment == 0); const new_record_addr = new_adjusted_addr + new_size; @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr; - return @intToPtr(*u8, new_adjusted_addr)[0..new_size]; + return @intToPtr([*]u8, new_adjusted_addr)[0..new_size]; }, else => @compileError("Unsupported OS"), } @@ -170,7 +170,7 @@ pub const DirectAllocator = struct { Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const root_addr = @intToPtr(*align(1) usize, record_addr).*; - const ptr = @intToPtr(os.windows.LPVOID, root_addr); + const ptr = @intToPtr([*]c_void, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, else => @compileError("Unsupported OS"), diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 0e80ae09c1..822ade2eb8 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -639,7 +639,7 @@ pub const ChildProcess = struct { } }; -fn windowsCreateProcess(app_name: *u8, cmd_line: *u8, envp_ptr: ?*u8, cwd_ptr: ?*u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { +fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { const err = windows.GetLastError(); return switch (err) { diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 77e8b6bb6a..b8e18561cc 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -317,7 +317,8 @@ pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { return errnoWrap(c.lseek(fd, offset, whence)); } -pub fn open(path: *const u8, flags: u32, mode: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 on the whole file +pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); } @@ -325,33 +326,33 @@ pub fn raise(sig: i32) usize { return errnoWrap(c.raise(sig)); } -pub fn read(fd: i32, buf: *u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); +pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast([*]c_void, buf), nbyte)); } -pub fn stat(noalias path: *const u8, noalias buf: *stat) usize { +pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { return errnoWrap(c.stat(path, buf)); } -pub fn write(fd: i32, buf: *const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); +pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast([*]const c_void, buf), nbyte)); } -pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap(@ptrCast([*]c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); + return errnoWrap(c.munmap(@intToPtr([*]c_void, address), length)); } -pub fn unlink(path: *const u8) usize { +pub fn unlink(path: [*]const u8) usize { return errnoWrap(c.unlink(path)); } -pub fn getcwd(buf: *u8, size: usize) usize { +pub fn getcwd(buf: [*]u8, size: usize) usize { return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } @@ -364,40 +365,40 @@ pub fn fork() usize { return errnoWrap(c.fork()); } -pub fn access(path: *const u8, mode: u32) usize { +pub fn access(path: [*]const u8, mode: u32) usize { return errnoWrap(c.access(path, mode)); } pub fn pipe(fds: *[2]i32) usize { comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe(@ptrCast(*c_int, fds))); + return errnoWrap(c.pipe(@ptrCast(*[2]c_int, fds))); } -pub fn getdirentries64(fd: i32, buf_ptr: *u8, buf_len: usize, basep: *i64) usize { +pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep))); } -pub fn mkdir(path: *const u8, mode: u32) usize { +pub fn mkdir(path: [*]const u8, mode: u32) usize { return errnoWrap(c.mkdir(path, mode)); } -pub fn symlink(existing: *const u8, new: *const u8) usize { +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { return errnoWrap(c.symlink(existing, new)); } -pub fn rename(old: *const u8, new: *const u8) usize { +pub fn rename(old: [*]const u8, new: [*]const u8) usize { return errnoWrap(c.rename(old, new)); } -pub fn rmdir(path: *const u8) usize { +pub fn rmdir(path: [*]const u8) usize { return errnoWrap(c.rmdir(path)); } -pub fn chdir(path: *const u8) usize { +pub fn chdir(path: [*]const u8) usize { return errnoWrap(c.chdir(path)); } -pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { return errnoWrap(c.execve(path, argv, envp)); } @@ -405,7 +406,7 @@ pub fn dup2(old: i32, new: i32) usize { return errnoWrap(c.dup2(old, new)); } -pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { return errnoWrap(c.readlink(path, buf_ptr, buf_len)); } @@ -417,7 +418,7 @@ pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { return errnoWrap(c.nanosleep(req, rem)); } -pub fn realpath(noalias filename: *const u8, noalias resolved_name: *u8) usize { +pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; } diff --git a/std/os/file.zig b/std/os/file.zig index d943da30ca..378782507b 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -313,7 +313,7 @@ pub const File = struct { if (is_posix) { var index: usize = 0; while (index < buffer.len) { - const amt_read = posix.read(self.handle, &buffer[index], buffer.len - index); + const amt_read = posix.read(self.handle, buffer.ptr + index, buffer.len - index); const read_err = posix.getErrno(amt_read); if (read_err > 0) { switch (read_err) { @@ -334,7 +334,7 @@ pub const File = struct { while (index < buffer.len) { const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, @ptrCast(*c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) { + if (windows.ReadFile(self.handle, @ptrCast([*]c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.OPERATION_ABORTED => continue, diff --git a/std/os/index.zig b/std/os/index.zig index ff638c670b..7e908af9eb 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -134,20 +134,7 @@ pub fn getRandomBytes(buf: []u8) !void { } }, Os.zen => { - const randomness = []u8{ - 42, - 1, - 7, - 12, - 22, - 17, - 99, - 16, - 26, - 87, - 41, - 45, - }; + const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; var i: usize = 0; while (i < buf.len) : (i += 1) { if (i > randomness.len) return error.Unknown; @@ -238,7 +225,7 @@ pub fn posixRead(fd: i32, buf: []u8) !void { var index: usize = 0; while (index < buf.len) { const want_to_read = math.min(buf.len - index, usize(max_buf_len)); - const rc = posix.read(fd, &buf[index], want_to_read); + const rc = posix.read(fd, buf.ptr + index, want_to_read); const err = posix.getErrno(rc); if (err > 0) { return switch (err) { @@ -278,7 +265,7 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { var index: usize = 0; while (index < bytes.len) { const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); - const rc = posix.write(fd, &bytes[index], amt_to_write); + const rc = posix.write(fd, bytes.ptr + index, amt_to_write); const write_err = posix.getErrno(rc); if (write_err > 0) { return switch (write_err) { @@ -328,7 +315,8 @@ pub fn posixOpen(allocator: *Allocator, file_path: []const u8, flags: u32, perm: return posixOpenC(path_with_null.ptr, flags, perm); } -pub fn posixOpenC(file_path: *const u8, flags: u32, perm: usize) !i32 { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { while (true) { const result = posix.open(file_path, flags, perm); const err = posix.getErrno(result); @@ -374,19 +362,19 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { } } -pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?*u8 { +pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?*u8, envp_count + 1); - mem.set(?*u8, envp_buf, null); + const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); + mem.set(?[*]u8, envp_buf, null); errdefer freeNullDelimitedEnvMap(allocator, envp_buf); { var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| : (i += 1) { const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); + @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); env_buf[pair.key.len] = '='; - @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); + @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); env_buf[env_buf.len - 1] = 0; envp_buf[i] = env_buf.ptr; @@ -397,7 +385,7 @@ pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) return envp_buf; } -pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?*u8) void { +pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { for (envp_buf) |env| { const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; allocator.free(env_buf); @@ -411,8 +399,8 @@ pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?*u8) void { /// `argv[0]` is the executable path. /// This function also uses the PATH environment variable to get the full path to the executable. pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { - const argv_buf = try allocator.alloc(?*u8, argv.len + 1); - mem.set(?*u8, argv_buf, null); + const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); + mem.set(?[*]u8, argv_buf, null); defer { for (argv_buf) |arg| { const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; @@ -422,7 +410,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: } for (argv) |arg, i| { const arg_buf = try allocator.alloc(u8, arg.len + 1); - @memcpy(&arg_buf[0], arg.ptr, arg.len); + @memcpy(arg_buf.ptr, arg.ptr, arg.len); arg_buf[arg.len] = 0; argv_buf[i] = arg_buf.ptr; @@ -494,7 +482,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { } pub var linux_aux_raw = []usize{0} ** 38; -pub var posix_environ_raw: []*u8 = undefined; +pub var posix_environ_raw: [][*]u8 = undefined; /// Caller must free result when done. pub fn getEnvMap(allocator: *Allocator) !BufMap { @@ -1311,7 +1299,7 @@ pub const Dir = struct { const next_index = self.index + linux_entry.d_reclen; self.index = next_index; - const name = cstr.toSlice(&linux_entry.d_name); + const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { @@ -1485,12 +1473,12 @@ pub const ArgIteratorPosix = struct { /// This is marked as public but actually it's only meant to be used /// internally by zig's startup code. - pub var raw: []*u8 = undefined; + pub var raw: [][*]u8 = undefined; }; pub const ArgIteratorWindows = struct { index: usize, - cmd_line: *const u8, + cmd_line: [*]const u8, in_quote: bool, quote_count: usize, seen_quote_count: usize, @@ -1501,7 +1489,7 @@ pub const ArgIteratorWindows = struct { return initWithCmdLine(windows.GetCommandLineA()); } - pub fn initWithCmdLine(cmd_line: *const u8) ArgIteratorWindows { + pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { return ArgIteratorWindows{ .index = 0, .cmd_line = cmd_line, @@ -1616,7 +1604,7 @@ pub const ArgIteratorWindows = struct { } } - fn countQuotes(cmd_line: *const u8) usize { + fn countQuotes(cmd_line: [*]const u8) usize { var result: usize = 0; var backslash_count: usize = 0; var index: usize = 0; @@ -1722,39 +1710,12 @@ pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { } test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8{ - "a", - "b", - "c", - "d", - }); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ - "abc", - "d", - "e", - }); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ - "a\\\\\\b", - "de fg", - "h", - }); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ - "a\\\"b", - "c", - "d", - }); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ - "a\\\\b c", - "d", - "e", - }); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ - "a", - "b", - "c", - "\"d", - "f", - }); + testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); + testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ ".\\..\\zig-cache\\build", @@ -1765,7 +1726,7 @@ test "windows arg parsing" { }); } -fn testWindowsCmdLine(input_cmd_line: *const u8, expected_args: []const []const u8) void { +fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { const arg = ??it.next(debug.global_allocator) catch unreachable; @@ -2350,7 +2311,7 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConn pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { var err_code: i32 = undefined; var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(*u8, &err_code), &size); + const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); assert(size == 4); const err = posix.getErrno(rc); switch (err) { @@ -2401,7 +2362,7 @@ pub const Thread = struct { }, builtin.Os.windows => struct { handle: windows.HANDLE, - alloc_start: *c_void, + alloc_start: [*]c_void, heap_handle: windows.HANDLE, }, else => @compileError("Unsupported OS"), @@ -2500,7 +2461,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); - const bytes = @ptrCast(*u8, bytes_ptr)[0..byte_count]; + const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; outer_context.inner = context; outer_context.thread.data.heap_handle = heap_handle; @@ -2572,7 +2533,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr([*]c_void, stack_addr), stack_end - stack_addr) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 3e7b836ac7..0e77371ec2 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -665,15 +665,18 @@ pub fn dup2(old: i32, new: i32) usize { return syscall2(SYS_dup2, usize(old), usize(new)); } -pub fn chdir(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chdir(path: [*]const u8) usize { return syscall1(SYS_chdir, @ptrToInt(path)); } -pub fn chroot(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chroot(path: [*]const u8) usize { return syscall1(SYS_chroot, @ptrToInt(path)); } -pub fn execve(path: *const u8, argv: *const ?*const u8, envp: *const ?*const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); } @@ -685,11 +688,11 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); } -pub fn getcwd(buf: *u8, size: usize) usize { +pub fn getcwd(buf: [*]u8, size: usize) usize { return syscall2(SYS_getcwd, @ptrToInt(buf), size); } -pub fn getdents(fd: i32, dirp: *u8, count: usize) usize { +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count); } @@ -698,27 +701,32 @@ pub fn isatty(fd: i32) bool { return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } -pub fn readlink(noalias path: *const u8, noalias buf_ptr: *u8, buf_len: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); } -pub fn mkdir(path: *const u8, mode: u32) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mkdir(path: [*]const u8, mode: u32) usize { return syscall2(SYS_mkdir, @ptrToInt(path), mode); } -pub fn mount(special: *const u8, dir: *const u8, fstype: *const u8, flags: usize, data: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: usize, data: usize) usize { return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); } -pub fn umount(special: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount(special: [*]const u8) usize { return syscall2(SYS_umount2, @ptrToInt(special), 0); } -pub fn umount2(special: *const u8, flags: u32) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount2(special: [*]const u8, flags: u32) usize { return syscall2(SYS_umount2, @ptrToInt(special), flags); } -pub fn mmap(address: ?*u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); } @@ -726,23 +734,26 @@ pub fn munmap(address: usize, length: usize) usize { return syscall2(SYS_munmap, address, length); } -pub fn read(fd: i32, buf: *u8, count: usize) usize { +pub fn read(fd: i32, buf: [*]u8, count: usize) usize { return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count); } -pub fn rmdir(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rmdir(path: [*]const u8) usize { return syscall1(SYS_rmdir, @ptrToInt(path)); } -pub fn symlink(existing: *const u8, new: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new)); } -pub fn pread(fd: i32, buf: *u8, count: usize, offset: usize) usize { +pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); } -pub fn access(path: *const u8, mode: u32) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn access(path: [*]const u8, mode: u32) usize { return syscall2(SYS_access, @ptrToInt(path), mode); } @@ -754,27 +765,31 @@ pub fn pipe2(fd: *[2]i32, flags: usize) usize { return syscall2(SYS_pipe2, @ptrToInt(fd), flags); } -pub fn write(fd: i32, buf: *const u8, count: usize) usize { +pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count); } -pub fn pwrite(fd: i32, buf: *const u8, count: usize, offset: usize) usize { +pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset); } -pub fn rename(old: *const u8, new: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rename(old: [*]const u8, new: [*]const u8) usize { return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new)); } -pub fn open(path: *const u8, flags: u32, perm: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { return syscall3(SYS_open, @ptrToInt(path), flags, perm); } -pub fn create(path: *const u8, perm: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn create(path: [*]const u8, perm: usize) usize { return syscall2(SYS_creat, @ptrToInt(path), perm); } -pub fn openat(dirfd: i32, path: *const u8, flags: usize, mode: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); } @@ -801,7 +816,7 @@ pub fn exit(status: i32) noreturn { unreachable; } -pub fn getrandom(buf: *u8, count: usize, flags: u32) usize { +pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags)); } @@ -809,7 +824,8 @@ pub fn kill(pid: i32, sig: i32) usize { return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig)); } -pub fn unlink(path: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn unlink(path: [*]const u8) usize { return syscall1(SYS_unlink, @ptrToInt(path)); } @@ -942,8 +958,8 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti .restorer = @ptrCast(extern fn () void, restore_rt), }; var ksa_old: k_sigaction = undefined; - @memcpy(@ptrCast(*u8, *ksa.mask), @ptrCast(*const u8, *act.mask), 8); - const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(*ksa), @ptrToInt(*ksa_old), @sizeOf(@typeOf(ksa.mask))); + @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); + const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); const err = getErrno(result); if (err != 0) { return result; @@ -951,7 +967,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti if (oact) |old| { old.handler = ksa_old.handler; old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast(*u8, *old.mask), @ptrCast(*const u8, *ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); + @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); } return 0; } @@ -1036,7 +1052,7 @@ pub const sockaddr_in6 = extern struct { }; pub const iovec = extern struct { - iov_base: *u8, + iov_base: [*]u8, iov_len: usize, }; @@ -1052,11 +1068,11 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { return syscall3(SYS_socket, domain, socket_type, protocol); } -pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: *const u8, optlen: socklen_t) usize { +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); } -pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: *u8, noalias optlen: *socklen_t) usize { +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } @@ -1072,7 +1088,7 @@ pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); } -pub fn recvfrom(fd: i32, noalias buf: *u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { +pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } @@ -1088,7 +1104,7 @@ pub fn listen(fd: i32, backlog: u32) usize { return syscall2(SYS_listen, usize(fd), backlog); } -pub fn sendto(fd: i32, buf: *const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { +pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen)); } @@ -1108,59 +1124,72 @@ pub fn fstat(fd: i32, stat_buf: *Stat) usize { return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); } -pub fn stat(pathname: *const u8, statbuf: *Stat) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn lstat(pathname: *const u8, statbuf: *Stat) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf)); } -pub fn listxattr(path: *const u8, list: *u8, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn llistxattr(path: *const u8, list: *u8, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); } -pub fn flistxattr(fd: usize, list: *u8, size: usize) usize { +pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); } -pub fn getxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn lgetxattr(path: *const u8, name: *const u8, value: *void, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); } -pub fn fgetxattr(fd: usize, name: *const u8, value: *void, size: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); } -pub fn setxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn lsetxattr(path: *const u8, name: *const u8, value: *const void, size: usize, flags: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn fsetxattr(fd: usize, name: *const u8, value: *const void, size: usize, flags: usize) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); } -pub fn removexattr(path: *const u8, name: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn lremovexattr(path: *const u8, name: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); } -pub fn fremovexattr(fd: usize, name: *const u8) usize { +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fremovexattr(fd: usize, name: [*]const u8) usize { return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); } @@ -1188,7 +1217,7 @@ pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); } -pub fn epoll_wait(epoll_fd: i32, events: *epoll_event, maxevents: u32, timeout: i32) usize { +pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); } diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 06aae1968f..948a3ac96b 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -35,5 +35,6 @@ test "timer" { const events_one: linux.epoll_event = undefined; var events = []linux.epoll_event{events_one} ** 8; - err = linux.epoll_wait(i32(epoll_fd), &events[0], 8, -1); + // TODO implicit cast from *[N]T to [*]T + err = linux.epoll_wait(i32(epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 1317da6388..2ab4d0cbc1 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -12,7 +12,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var ph_addr: usize = vdso_addr + eh.e_phoff; const ph = @intToPtr(*elf.Phdr, ph_addr); - var maybe_dynv: ?*usize = null; + var maybe_dynv: ?[*]usize = null; var base: usize = @maxValue(usize); { var i: usize = 0; @@ -23,7 +23,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const this_ph = @intToPtr(*elf.Phdr, ph_addr); switch (this_ph.p_type) { elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr, - elf.PT_DYNAMIC => maybe_dynv = @intToPtr(*usize, vdso_addr + this_ph.p_offset), + elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, vdso_addr + this_ph.p_offset), else => {}, } } @@ -31,10 +31,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { const dynv = maybe_dynv ?? return 0; if (base == @maxValue(usize)) return 0; - var maybe_strings: ?*u8 = null; - var maybe_syms: ?*elf.Sym = null; - var maybe_hashtab: ?*linux.Elf_Symndx = null; - var maybe_versym: ?*u16 = null; + var maybe_strings: ?[*]u8 = null; + var maybe_syms: ?[*]elf.Sym = null; + var maybe_hashtab: ?[*]linux.Elf_Symndx = null; + var maybe_versym: ?[*]u16 = null; var maybe_verdef: ?*elf.Verdef = null; { @@ -42,10 +42,10 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { while (dynv[i] != 0) : (i += 2) { const p = base + dynv[i + 1]; switch (dynv[i]) { - elf.DT_STRTAB => maybe_strings = @intToPtr(*u8, p), - elf.DT_SYMTAB => maybe_syms = @intToPtr(*elf.Sym, p), - elf.DT_HASH => maybe_hashtab = @intToPtr(*linux.Elf_Symndx, p), - elf.DT_VERSYM => maybe_versym = @intToPtr(*u16, p), + elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), else => {}, } @@ -65,7 +65,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (0 == (u32(1) << u5(syms[i].st_info & 0xf) & OK_TYPES)) continue; if (0 == (u32(1) << u5(syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; - if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { if (!checkver(??maybe_verdef, versym[i], vername, strings)) continue; @@ -76,7 +76,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { return 0; } -fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: *u8) bool { +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { var def = def_arg; const vsym = @bitCast(u32, vsym_arg) & 0x7fff; while (true) { @@ -87,5 +87,5 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: * def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); } const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); - return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name])); + return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); } diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 85f69836d5..c491ae6538 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -10,7 +10,7 @@ pub extern "advapi32" stdcallcc fn CryptAcquireContextA( pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; -pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *BYTE) BOOL; +pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; @@ -61,7 +61,7 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; -pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: LPCH) BOOL; +pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; @@ -69,7 +69,7 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; -pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?LPCH; +pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; @@ -101,17 +101,17 @@ pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL; pub extern "kernel32" stdcallcc fn MoveFileExA( lpExistingFileName: LPCSTR, @@ -127,7 +127,7 @@ pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, - out_lpBuffer: *c_void, + out_lpBuffer: [*]c_void, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: *DWORD, in_out_lpOverlapped: ?*OVERLAPPED, @@ -150,7 +150,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis pub extern "kernel32" stdcallcc fn WriteFile( in_hFile: HANDLE, - in_lpBuffer: *const c_void, + in_lpBuffer: [*]const c_void, in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?*DWORD, in_out_lpOverlapped: ?*OVERLAPPED, @@ -178,16 +178,16 @@ pub const HMODULE = *@OpaqueType(); pub const INT = c_int; pub const LPBYTE = *BYTE; pub const LPCH = *CHAR; -pub const LPCSTR = *const CHAR; -pub const LPCTSTR = *const TCHAR; +pub const LPCSTR = [*]const CHAR; +pub const LPCTSTR = [*]const TCHAR; pub const LPCVOID = *const c_void; pub const LPDWORD = *DWORD; -pub const LPSTR = *CHAR; +pub const LPSTR = [*]CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; pub const LPVOID = *c_void; -pub const LPWSTR = *WCHAR; +pub const LPWSTR = [*]WCHAR; pub const PVOID = *c_void; -pub const PWSTR = *WCHAR; +pub const PWSTR = [*]WCHAR; pub const SIZE_T = usize; pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 7170346108..5a40567310 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast([*]const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, diff --git a/std/segmented_list.zig b/std/segmented_list.zig index be9a2071a0..a2f3607ad8 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -87,7 +87,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const ShelfIndex = std.math.Log2Int(usize); prealloc_segment: [prealloc_item_count]T, - dynamic_segments: []*T, + dynamic_segments: [][*]T, allocator: *Allocator, len: usize, @@ -99,7 +99,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type .allocator = allocator, .len = 0, .prealloc_segment = undefined, - .dynamic_segments = []*T{}, + .dynamic_segments = [][*]T{}, }; } @@ -160,11 +160,11 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const new_cap_shelf_count = shelfCount(new_capacity); const old_shelf_count = ShelfIndex(self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { - self.dynamic_segments = try self.allocator.realloc(*T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = try self.allocator.realloc([*]T, self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; errdefer { self.freeShelves(i, old_shelf_count); - self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, old_shelf_count); + self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, old_shelf_count); } while (i < new_cap_shelf_count) : (i += 1) { self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr; @@ -178,7 +178,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type const len = ShelfIndex(self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); - self.dynamic_segments = []*T{}; + self.dynamic_segments = [][*]T{}; return; } @@ -190,7 +190,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } self.freeShelves(old_shelf_count, new_cap_shelf_count); - self.dynamic_segments = self.allocator.shrink(*T, self.dynamic_segments, new_cap_shelf_count); + self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count); } pub fn uncheckedAt(self: *Self, index: usize) *T { diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 5ed7874ca5..64eae79ce4 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -5,7 +5,7 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); -var argc_ptr: *usize = undefined; +var argc_ptr: [*]usize = undefined; comptime { const strong_linkage = builtin.GlobalLinkage.Strong; @@ -28,12 +28,12 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> *usize) + : [argc] "=r" (-> [*]usize) ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" - : [argc] "=r" (-> *usize) + : [argc] "=r" (-> [*]usize) ); }, else => @compileError("unsupported arch"), @@ -49,15 +49,17 @@ extern fn WinMainCRTStartup() noreturn { std.os.windows.ExitProcess(callMain()); } +// TODO https://github.com/ziglang/zig/issues/265 fn posixCallMainAndExit() noreturn { const argc = argc_ptr.*; - const argv = @ptrCast(**u8, &argc_ptr[1]); - const envp_nullable = @ptrCast(*?*u8, &argv[argc + 1]); + const argv = @ptrCast([*][*]u8, argc_ptr + 1); + + const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); var envp_count: usize = 0; while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast(**u8, envp_nullable)[0..envp_count]; + const envp = @ptrCast([*][*]u8, envp_nullable)[0..envp_count]; if (builtin.os == builtin.Os.linux) { - const auxv = &@ptrCast(*usize, envp.ptr)[envp_count + 1]; + const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); var i: usize = 0; while (auxv[i] != 0) : (i += 2) { if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i + 1]; @@ -68,16 +70,16 @@ fn posixCallMainAndExit() noreturn { std.os.posix.exit(callMainWithArgs(argc, argv, envp)); } -fn callMainWithArgs(argc: usize, argv: **u8, envp: []*u8) u8 { +fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; std.os.posix_environ_raw = envp; return callMain(); } -extern fn main(c_argc: i32, c_argv: **u8, c_envp: *?*u8) i32 { +extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} - const envp = @ptrCast(**u8, c_envp)[0..env_count]; + const envp = @ptrCast([*][*]u8, c_envp)[0..env_count]; return callMainWithArgs(usize(c_argc), c_argv, envp); } diff --git a/std/special/builtin.zig b/std/special/builtin.zig index 9c9cd35103..e537078924 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -14,7 +14,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn } } -export fn memset(dest: ?*u8, c: u8, n: usize) ?*u8 { +export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -24,7 +24,7 @@ export fn memset(dest: ?*u8, c: u8, n: usize) ?*u8 { return dest; } -export fn memcpy(noalias dest: ?*u8, noalias src: ?*const u8, n: usize) ?*u8 { +export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]u8 { @setRuntimeSafety(false); var index: usize = 0; @@ -34,7 +34,7 @@ export fn memcpy(noalias dest: ?*u8, noalias src: ?*const u8, n: usize) ?*u8 { return dest; } -export fn memmove(dest: ?*u8, src: ?*const u8, n: usize) ?*u8 { +export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { @setRuntimeSafety(false); if (@ptrToInt(dest) < @ptrToInt(src)) { diff --git a/test/cases/align.zig b/test/cases/align.zig index 99bdcdf940..b80727258e 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -167,54 +167,41 @@ test "@ptrCast preserves alignment of bigger source" { assert(@typeOf(ptr) == *align(16) u8); } -test "compile-time known array index has best alignment possible" { +test "runtime known array index has best alignment possible" { // take full advantage of over-alignment - var array align(4) = []u8{ - 1, - 2, - 3, - 4, - }; + var array align(4) = []u8{ 1, 2, 3, 4 }; assert(@typeOf(&array[0]) == *align(4) u8); assert(@typeOf(&array[1]) == *u8); assert(@typeOf(&array[2]) == *align(2) u8); assert(@typeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 - var bigger align(2) = []u64{ - 1, - 2, - 3, - 4, - }; + var bigger align(2) = []u64{ 1, 2, 3, 4 }; assert(@typeOf(&bigger[0]) == *align(2) u64); assert(@typeOf(&bigger[1]) == *align(2) u64); assert(@typeOf(&bigger[2]) == *align(2) u64); assert(@typeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 - var smaller align(2) = []u32{ - 1, - 2, - 3, - 4, - }; - testIndex(&smaller[0], 0, *align(2) u32); - testIndex(&smaller[0], 1, *align(2) u32); - testIndex(&smaller[0], 2, *align(2) u32); - testIndex(&smaller[0], 3, *align(2) u32); + var smaller align(2) = []u32{ 1, 2, 3, 4 }; + comptime assert(@typeOf(smaller[0..]) == []align(2) u32); + comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + testIndex(smaller[0..].ptr, 0, *align(2) u32); + testIndex(smaller[0..].ptr, 1, *align(2) u32); + testIndex(smaller[0..].ptr, 2, *align(2) u32); + testIndex(smaller[0..].ptr, 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - testIndex2(&array[0], 0, *u8); - testIndex2(&array[0], 1, *u8); - testIndex2(&array[0], 2, *u8); - testIndex2(&array[0], 3, *u8); + testIndex2(array[0..].ptr, 0, *u8); + testIndex2(array[0..].ptr, 1, *u8); + testIndex2(array[0..].ptr, 2, *u8); + testIndex2(array[0..].ptr, 3, *u8); } -fn testIndex(smaller: *align(2) u32, index: usize, comptime T: type) void { - assert(@typeOf(&smaller[index]) == T); +fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { + comptime assert(@typeOf(&smaller[index]) == T); } -fn testIndex2(ptr: *align(4) u8, index: usize, comptime T: type) void { - assert(@typeOf(&ptr[index]) == T); +fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { + comptime assert(@typeOf(&ptr[index]) == T); } test "alignstack" { diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index e012c729a0..07d02d5df0 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -1,15 +1,16 @@ const debug = @import("std").debug; const assert = debug.assert; -var argv: *const *const u8 = undefined; +var argv: [*]const [*]const u8 = undefined; test "const slice child" { - const strs = ([]*const u8){ + const strs = ([][*]const u8){ c"one", c"two", c"three", }; - argv = &strs[0]; + // TODO this should implicitly cast + argv = @ptrCast([*]const [*]const u8, &strs); bar(strs.len); } @@ -29,7 +30,7 @@ fn bar(argc: usize) void { foo(args); } -fn strlen(ptr: *const u8) usize { +fn strlen(ptr: [*]const u8) usize { var count: usize = 0; while (ptr[count] != 0) : (count += 1) {} return count; diff --git a/test/cases/for.zig b/test/cases/for.zig index c624035708..bdbab312f6 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -35,34 +35,12 @@ fn mangleString(s: []u8) void { } test "basic for loop" { - const expected_result = []u8{ - 9, - 8, - 7, - 6, - 0, - 1, - 2, - 3, - 9, - 8, - 7, - 6, - 0, - 1, - 2, - 3, - }; + 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, - }; + const array = []u8{ 9, 8, 7, 6 }; for (array) |item| { buffer[buf_index] = item; buf_index += 1; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 919b978f9f..5899f20f9c 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -171,8 +171,8 @@ test "memcpy and memset intrinsics" { var foo: [20]u8 = undefined; var bar: [20]u8 = undefined; - @memset(&foo[0], 'A', foo.len); - @memcpy(&bar[0], &foo[0], bar.len); + @memset(foo[0..].ptr, 'A', foo.len); + @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); if (bar[11] != 'A') unreachable; } @@ -194,7 +194,7 @@ test "slicing" { if (slice.len != 5) unreachable; const ptr = &slice[0]; - if (ptr[0] != 1234) unreachable; + if (ptr.* != 1234) unreachable; var slice_rest = array[10..]; if (slice_rest.len != 10) unreachable; @@ -464,8 +464,9 @@ test "array 2D const double ptr" { } fn testArray2DConstDoublePtr(ptr: *const f32) void { - assert(ptr[0] == 1.0); - assert(ptr[1] == 2.0); + const ptr2 = @ptrCast([*]const f32, ptr); + assert(ptr2[0] == 1.0); + assert(ptr2[1] == 2.0); } const Tid = builtin.TypeId; diff --git a/test/cases/pointers.zig b/test/cases/pointers.zig index 87b3d25a74..47afb60a2e 100644 --- a/test/cases/pointers.zig +++ b/test/cases/pointers.zig @@ -12,3 +12,33 @@ fn testDerefPtr() void { y.* += 1; assert(x == 1235); } + +test "pointer arithmetic" { + var ptr = c"abcd"; + + assert(ptr[0] == 'a'); + ptr += 1; + assert(ptr[0] == 'b'); + ptr += 1; + assert(ptr[0] == 'c'); + ptr += 1; + assert(ptr[0] == 'd'); + ptr += 1; + assert(ptr[0] == 0); + ptr -= 1; + assert(ptr[0] == 'd'); + ptr -= 1; + assert(ptr[0] == 'c'); + ptr -= 1; + assert(ptr[0] == 'b'); + ptr -= 1; + assert(ptr[0] == 'a'); +} + +test "double pointer parsing" { + comptime assert(PtrOf(PtrOf(i32)) == **i32); +} + +fn PtrOf(comptime T: type) type { + return *T; +} diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 0712e508de..6f7d44e09b 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -43,7 +43,7 @@ const VoidStructFieldsFoo = struct { test "structs" { var foo: StructFoo = undefined; - @memset(@ptrCast(*u8, &foo), 0, @sizeOf(StructFoo)); + @memset(@ptrCast([*]u8, &foo), 0, @sizeOf(StructFoo)); foo.a += 1; foo.b = foo.a == 1; testFoo(foo); @@ -396,8 +396,8 @@ const Bitfields = packed struct { test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; - @memcpy(&bytes[0], @ptrCast(*u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, &bytes[0]).*; + @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; assert(bitfields.f1 == 0x1111); assert(bitfields.f2 == 0x2222); diff --git a/test/compare_output.zig b/test/compare_output.zig index 00ad4a709b..8d5dc68d45 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -6,7 +6,7 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("hello world with libc", \\const c = @cImport(@cInclude("stdio.h")); - \\export fn main(argc: c_int, argv: **u8) c_int { + \\export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ _ = c.puts(c"Hello, world!"); \\ return 0; \\} @@ -139,7 +139,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: **u8) c_int { + \\export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); @@ -284,9 +284,9 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ - \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int { - \\ const a_int = @ptrCast(*align(1) const i32, a ?? unreachable); - \\ const b_int = @ptrCast(*align(1) const i32, b ?? unreachable); + \\export fn compare_fn(a: ?[*]const c_void, b: ?[*]const c_void) c_int { + \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a)); + \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b)); \\ if (a_int.* < b_int.*) { \\ return -1; \\ } else if (a_int.* > b_int.*) { @@ -297,9 +297,9 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\ \\export fn main() c_int { - \\ var array = []u32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; + \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(*c_void, &array[0]), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?[*]c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { @@ -324,7 +324,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ @cInclude("stdio.h"); \\}); \\ - \\export fn main(argc: c_int, argv: **u8) c_int { + \\export fn main(argc: c_int, argv: [*][*]u8) c_int { \\ if (is_windows) { \\ // we want actual \n, not \r\n \\ _ = c._setmode(1, c._O_BINARY); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ea1357f5bb..7e9ef82e42 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "indexing single-item pointer", + \\export fn entry(ptr: *i32) i32 { + \\ return ptr[1]; + \\} + , + ".tmp_source.zig:2:15: error: indexing not allowed on pointer to single item", + ); + cases.add( "invalid deref on switch target", \\const NextError = error{NextError}; @@ -1002,7 +1011,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected type 'i32', found '*const u8'", + ".tmp_source.zig:3:12: error: expected type 'i32', found '[*]const u8'", ); cases.add( @@ -2442,13 +2451,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\var s_buffer: [10]u8 = undefined; \\pub fn pass(in: []u8) []u8 { \\ var out = &s_buffer; - \\ out[0].* = in[0]; + \\ out.*.* = in[0]; \\ return out.*[0..1]; \\} \\ \\export fn entry() usize { return @sizeOf(@typeOf(pass)); } , - ".tmp_source.zig:4:11: error: attempt to dereference non pointer type '[10]u8'", + ".tmp_source.zig:4:10: error: attempt to dereference non pointer type '[10]u8'", ); cases.add( diff --git a/test/translate_c.zig b/test/translate_c.zig index 9a07bc343d..ac0a98e6cc 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -14,11 +14,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ a: ?*Foo, + \\ a: ?[*]Foo, \\}; \\pub const Foo = struct_Foo; \\pub const struct_Bar = extern struct { - \\ a: ?*Foo, + \\ a: ?[*]Foo, \\}; ); @@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; + \\pub extern fn foo(noalias bar: ?[*]c_void, noalias arg1: ?[*]c_void) void; ); cases.add("simple struct", @@ -110,7 +110,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\const struct_Foo = extern struct { \\ x: c_int, - \\ y: ?*u8, + \\ y: ?[*]u8, \\}; , \\pub const Foo = struct_Foo; @@ -141,7 +141,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const BarB = enum_Bar.B; , - \\pub extern fn func(a: ?*struct_Foo, b: ?*(?*enum_Bar)) void; + \\pub extern fn func(a: ?[*]struct_Foo, b: ?[*](?[*]enum_Bar)) void; , \\pub const Foo = struct_Foo; , @@ -151,7 +151,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("constant size array", \\void func(int array[20]); , - \\pub extern fn func(array: ?*c_int) void; + \\pub extern fn func(array: ?[*]c_int) void; ); cases.add("self referential struct with function pointer", @@ -160,7 +160,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn(?*struct_Foo) void, + \\ derp: ?extern fn(?[*]struct_Foo) void, \\}; , \\pub const Foo = struct_Foo; @@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; + \\pub extern fn some_func(foo: ?[*]struct_Foo, x: c_int) ?[*]struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -219,11 +219,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Bar = extern struct { - \\ next: ?*struct_Foo, + \\ next: ?[*]struct_Foo, \\}; , \\pub const struct_Foo = extern struct { - \\ next: ?*struct_Bar, + \\ next: ?[*]struct_Bar, \\}; ); @@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?*Foo) Foo; + \\pub extern fn fun(a: ?[*]Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -607,7 +607,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const struct_Foo = extern struct { \\ field: c_int, \\}; - \\pub export fn read_field(foo: ?*struct_Foo) c_int { + \\pub export fn read_field(foo: ?[*]struct_Foo) c_int { \\ return (??foo).field; \\} ); @@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?*c_ushort) ?*c_void { - \\ return @ptrCast(?*c_void, x); + \\pub export fn foo(x: ?[*]c_ushort) ?[*]c_void { + \\ return @ptrCast(?[*]c_void, x); \\} ); @@ -674,7 +674,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 0; \\} , - \\pub export fn foo() ?*c_int { + \\pub export fn foo() ?[*]c_int { \\ return null; \\} ); @@ -983,7 +983,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ *x = 1; \\} , - \\pub export fn foo(x: ?*c_int) void { + \\pub export fn foo(x: ?[*]c_int) void { \\ (??x).* = 1; \\} ); @@ -1011,7 +1011,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub fn foo() c_int { \\ var x: c_int = 1234; - \\ var ptr: ?*c_int = &x; + \\ var ptr: ?[*]c_int = &x; \\ return (??ptr).*; \\} ); @@ -1021,7 +1021,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return "bar"; \\} , - \\pub fn foo() ?*const u8 { + \\pub fn foo() ?[*]const u8 { \\ return c"bar"; \\} ); @@ -1150,8 +1150,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (float *)a; \\} , - \\fn ptrcast(a: ?*c_int) ?*f32 { - \\ return @ptrCast(?*f32, a); + \\fn ptrcast(a: ?[*]c_int) ?[*]f32 { + \\ return @ptrCast(?[*]f32, a); \\} ); @@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1194,7 +1194,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("const ptr initializer", \\static const char *v0 = "0.0.0"; , - \\pub var v0: ?*const u8 = c"0.0.0"; + \\pub var v0: ?[*]const u8 = c"0.0.0"; ); cases.add("static incomplete array inside function", @@ -1203,14 +1203,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ const v2: *const u8 = c"2.2.2"; + \\ const v2: [*]const u8 = c"2.2.2"; \\} ); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(*NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(*NRF_GPIO_Type, NRF_GPIO_BASE) else (*NRF_GPIO_Type)(NRF_GPIO_BASE); + \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*]NRF_GPIO_Type)(NRF_GPIO_BASE); ); cases.add("if on none bool", @@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: ?[*]c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; -- cgit v1.2.3 From 32e0dfd4f0dab351a024e7680280343db5d7c43e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 14:09:31 -0400 Subject: never call malloc with size 0 instead we return nullptr. this makes the behavior consistent across all platforms. closes #1044 closes #1045 --- src/analyze.cpp | 4 ++-- src/util.hpp | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 2b9d776e78..3165227033 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1860,7 +1860,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { } assert(!struct_type->data.structure.zero_bits_loop_flag); - assert(struct_type->data.structure.fields); + assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0); assert(decl_node->type == NodeTypeContainerDecl); size_t field_count = struct_type->data.structure.src_field_count; @@ -2677,8 +2677,8 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { return; } tag_type = enum_type; + abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count covered_enum_fields = allocate(enum_type->data.enumeration.src_field_count); - abi_alignment_so_far = get_abi_alignment(g, enum_type); } else { tag_type = nullptr; abi_alignment_so_far = 0; diff --git a/src/util.hpp b/src/util.hpp index 25141d8435..52baab7ace 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -65,6 +65,11 @@ static inline int clzll(unsigned long long mask) { template ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (count == 0) + return nullptr; +#endif T *ptr = reinterpret_cast(malloc(count * sizeof(T))); if (!ptr) zig_panic("allocation failed"); @@ -73,6 +78,11 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) { template ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (count == 0) + return nullptr; +#endif T *ptr = reinterpret_cast(calloc(count, sizeof(T))); if (!ptr) zig_panic("allocation failed"); @@ -93,9 +103,7 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) { template static inline T *reallocate(T *old, size_t old_count, size_t new_count) { - T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T))); - if (!ptr) - zig_panic("allocation failed"); + T *ptr = reallocate_nonzero(old, old_count, new_count); if (new_count > old_count) { memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T)); } @@ -104,6 +112,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) { template static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (new_count == 0 && old == nullptr) + return nullptr; +#endif T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T))); if (!ptr) zig_panic("allocation failed"); -- cgit v1.2.3 From e53b683bd3958a7b1c517e2391edce42b9d4e48b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Jun 2018 22:11:14 -0400 Subject: Pointer Reform: proper slicing and indexing (#1053) * enable slicing for single-item ptr to arrays * disable slicing for other single-item pointers * enable indexing for single-item ptr to arrays * disable indexing for other single-item pointers see #770 closes #386 --- doc/langref.html.in | 13 ++-- example/mix_o_files/base64.zig | 2 +- src/all_types.hpp | 5 ++ src/analyze.cpp | 14 ++-- src/analyze.hpp | 5 +- src/codegen.cpp | 27 +++++-- src/ir.cpp | 157 +++++++++++++++++++++++++++++++++++------ std/fmt/errol/index.zig | 2 +- std/fmt/index.zig | 8 +-- std/heap.zig | 2 +- std/io.zig | 4 +- std/macho.zig | 2 +- std/mem.zig | 34 +++++---- std/net.zig | 2 +- std/os/index.zig | 4 +- test/cases/align.zig | 4 +- test/cases/array.zig | 29 ++++++++ test/cases/eval.zig | 6 +- test/cases/misc.zig | 4 +- test/cases/slice.zig | 2 +- test/compile_errors.zig | 21 ++++-- 21 files changed, 268 insertions(+), 79 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 32481ade50..28fdf4d8b9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1565,7 +1565,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); assert(@typeOf(&foo) == *align(4) u8); - const slice = (&foo)[0..1]; + const slice = (*[1]u8)(&foo)[0..]; assert(@typeOf(slice) == []align(4) u8); } @@ -1671,7 +1671,7 @@ test "using slices for strings" { test "slice pointer" { var array: [10]u8 = undefined; - const ptr = &array[0]; + const ptr = &array; // You can use slicing syntax to convert a pointer into a slice: const slice = ptr[0..5]; @@ -6004,9 +6004,12 @@ const c = @cImport({ {#code_begin|syntax#} const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: *u8, dest_len: usize, - source_ptr: *const u8, source_len: usize) usize -{ +export fn decode_base_64( + dest_ptr: [*]u8, + dest_len: usize, + source_ptr: [*]const u8, + source_len: usize, +) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard_decoder_unsafe; diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig index 35b090825b..7ded9824a0 100644 --- a/example/mix_o_files/base64.zig +++ b/example/mix_o_files/base64.zig @@ -1,6 +1,6 @@ const base64 = @import("std").base64; -export fn decode_base_64(dest_ptr: *u8, dest_len: usize, source_ptr: *const u8, source_len: usize) usize { +export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize { const src = source_ptr[0..source_len]; const dest = dest_ptr[0..dest_len]; const base64_decoder = base64.standard_decoder_unsafe; diff --git a/src/all_types.hpp b/src/all_types.hpp index f1cf96238f..d237eb00bb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -83,6 +83,7 @@ enum ConstParentId { ConstParentIdStruct, ConstParentIdArray, ConstParentIdUnion, + ConstParentIdScalar, }; struct ConstParent { @@ -100,6 +101,9 @@ struct ConstParent { struct { ConstExprValue *union_val; } p_union; + struct { + ConstExprValue *scalar_val; + } p_scalar; } data; }; @@ -578,6 +582,7 @@ enum CastOp { CastOpBytesToSlice, CastOpNumLitToConcrete, CastOpErrSet, + CastOpBitCast, }; struct AstNodeFnCallExpr { diff --git a/src/analyze.cpp b/src/analyze.cpp index 3165227033..31c0726459 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5158,7 +5158,8 @@ void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *arr const_val->type = get_slice_type(g, ptr_type); const_val->data.x_struct.fields = create_const_vals(2); - init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const); + init_const_ptr_array(g, &const_val->data.x_struct.fields[slice_ptr_index], array_val, start, is_const, + PtrLenUnknown); init_const_usize(g, &const_val->data.x_struct.fields[slice_len_index], len); } @@ -5169,21 +5170,24 @@ ConstExprValue *create_const_slice(CodeGen *g, ConstExprValue *array_val, size_t } void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, - size_t elem_index, bool is_const) + size_t elem_index, bool is_const, PtrLen ptr_len) { assert(array_val->type->id == TypeTableEntryIdArray); TypeTableEntry *child_type = array_val->type->data.array.child_type; const_val->special = ConstValSpecialStatic; - const_val->type = get_pointer_to_type(g, child_type, is_const); + const_val->type = get_pointer_to_type_extra(g, child_type, is_const, false, + ptr_len, get_abi_alignment(g, child_type), 0, 0); const_val->data.x_ptr.special = ConstPtrSpecialBaseArray; const_val->data.x_ptr.data.base_array.array_val = array_val; const_val->data.x_ptr.data.base_array.elem_index = elem_index; } -ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const) { +ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const, + PtrLen ptr_len) +{ ConstExprValue *const_val = create_const_vals(1); - init_const_ptr_array(g, const_val, array_val, elem_index, is_const); + init_const_ptr_array(g, const_val, array_val, elem_index, is_const, ptr_len); return const_val; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 905bfa86dd..25bda198d6 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -152,8 +152,9 @@ ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, TypeTableEntry *poi size_t addr, bool is_const); void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, - size_t elem_index, bool is_const); -ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const); + size_t elem_index, bool is_const, PtrLen ptr_len); +ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, + bool is_const, PtrLen ptr_len); void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val, size_t start, size_t len, bool is_const); diff --git a/src/codegen.cpp b/src/codegen.cpp index 64e29a4da4..49c93feaa5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2574,6 +2574,8 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, add_error_range_check(g, wanted_type, g->err_tag_type, expr_val); } return expr_val; + case CastOpBitCast: + return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); } zig_unreachable(); } @@ -2884,7 +2886,13 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI bool safety_check_on = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on; - if (array_type->id == TypeTableEntryIdArray) { + if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == TypeTableEntryIdPointer) { + assert(array_type->data.pointer.child_type->id == TypeTableEntryIdArray); + array_type = array_type->data.pointer.child_type; + } if (safety_check_on) { LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); @@ -3794,7 +3802,12 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); - if (array_type->id == TypeTableEntryIdArray) { + if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == TypeTableEntryIdPointer) { + array_type = array_type->data.pointer.child_type; + } LLVMValueRef start_val = ir_llvm_value(g, instruction->start); LLVMValueRef end_val; if (instruction->end) { @@ -3835,6 +3848,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst return tmp_struct_ptr; } else if (array_type->id == TypeTableEntryIdPointer) { + assert(array_type->data.pointer.ptr_len == PtrLenUnknown); LLVMValueRef start_val = ir_llvm_value(g, instruction->start); LLVMValueRef end_val = ir_llvm_value(g, instruction->end); @@ -4812,7 +4826,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) { static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); -static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *array_const_val); +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -4828,6 +4842,10 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent parent->data.p_array.elem_index); case ConstParentIdUnion: return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val); + case ConstParentIdScalar: + render_const_val(g, parent->data.p_scalar.scalar_val, ""); + render_const_val_global(g, parent->data.p_scalar.scalar_val, ""); + return parent->data.p_scalar.scalar_val->global_refs->llvm_global; } zig_unreachable(); } @@ -4853,7 +4871,8 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar }; return LLVMConstInBoundsGEP(base_ptr, indices, 2); } else { - zig_unreachable(); + assert(parent->id == ConstParentIdScalar); + return base_ptr; } } diff --git a/src/ir.cpp b/src/ir.cpp index a230c60456..5cea04ea55 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -107,6 +107,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var); static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); +static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -6849,7 +6850,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *free_fn = ir_build_load_ptr(irb, scope, node, free_fn_ptr); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_mem_ptr_maybe); + IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, + get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, get_abi_alignment(irb->codegen, irb->codegen->builtin_types.entry_u8), + 0, 0)); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); @@ -8729,6 +8734,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, case CastOpNoCast: zig_unreachable(); case CastOpErrSet: + case CastOpBitCast: zig_panic("TODO"); case CastOpNoop: { @@ -9750,6 +9756,49 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return result; } +static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + TypeTableEntry *wanted_type) +{ + assert(wanted_type->id == TypeTableEntryIdPointer); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, target->value.type->data.pointer.alignment); + TypeTableEntry *array_type = wanted_type->data.pointer.child_type; + assert(array_type->id == TypeTableEntryIdArray); + assert(array_type->data.array.len == 1); + + if (instr_is_comptime(target)) { + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + assert(val->type->id == TypeTableEntryIdPointer); + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, val); + if (pointee->special != ConstValSpecialRuntime) { + ConstExprValue *array_val = create_const_vals(1); + array_val->special = ConstValSpecialStatic; + array_val->type = array_type; + array_val->data.x_array.special = ConstArraySpecialNone; + array_val->data.x_array.s_none.elements = pointee; + array_val->data.x_array.s_none.parent.id = ConstParentIdScalar; + array_val->data.x_array.s_none.parent.data.p_scalar.scalar_val = pointee; + + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value.type = wanted_type; + const_instruction->base.value.special = ConstValSpecialStatic; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialRef; + const_instruction->base.value.data.x_ptr.data.ref.pointee = array_val; + const_instruction->base.value.data.x_ptr.mut = val->data.x_ptr.mut; + return &const_instruction->base; + } + } + + // pointer to array and pointer to single item are represented the same way at runtime + IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, + wanted_type, target, CastOpBitCast); + result->value.type = wanted_type; + return result; +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -10156,6 +10205,30 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // explicit cast from *T to *[1]T + if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) + { + TypeTableEntry *array_type = wanted_type->data.pointer.child_type; + if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 && + types_match_const_cast_only(ira, array_type->data.array.child_type, + actual_type->data.pointer.child_type, source_node).id == ConstCastResultIdOk) + { + if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + add_error_note(ira->codegen, msg, value->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), + actual_type->data.pointer.alignment)); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), + wanted_type->data.pointer.alignment)); + return ira->codegen->invalid_instruction; + } + return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); + } + } + + // explicit cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefLit) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); @@ -13162,11 +13235,13 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(array_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *orig_array_ptr_val = &array_ptr->value; + IrInstruction *elem_index = elem_ptr_instruction->elem_index->other; if (type_is_invalid(elem_index->value.type)) return ira->codegen->builtin_types.entry_invalid; - TypeTableEntry *ptr_type = array_ptr->value.type; + TypeTableEntry *ptr_type = orig_array_ptr_val->type; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *array_type = ptr_type->data.pointer.child_type; @@ -13177,7 +13252,18 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(array_type)) { return array_type; - } else if (array_type->id == TypeTableEntryIdArray) { + } else if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && + array_type->data.pointer.ptr_len == PtrLenSingle && + array_type->data.pointer.child_type->id == TypeTableEntryIdArray)) + { + if (array_type->id == TypeTableEntryIdPointer) { + array_type = array_type->data.pointer.child_type; + ptr_type = ptr_type->data.pointer.child_type; + if (orig_array_ptr_val->special != ConstValSpecialRuntime) { + orig_array_ptr_val = const_ptr_pointee(ira->codegen, orig_array_ptr_val); + } + } if (array_type->data.array.len == 0) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index 0 outside array of size 0")); @@ -13205,7 +13291,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } else if (array_type->id == TypeTableEntryIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, - buf_sprintf("indexing not allowed on pointer to single item")); + buf_sprintf("index of single-item pointer")); return ira->codegen->builtin_types.entry_invalid; } return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); @@ -13294,9 +13380,9 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } ConstExprValue *array_ptr_val; - if (array_ptr->value.special != ConstValSpecialRuntime && - (array_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == TypeTableEntryIdArray) && - (array_ptr_val = const_ptr_pointee(ira->codegen, &array_ptr->value)) && + if (orig_array_ptr_val->special != ConstValSpecialRuntime && + (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == TypeTableEntryIdArray) && + (array_ptr_val = const_ptr_pointee(ira->codegen, orig_array_ptr_val)) && array_ptr_val->special != ConstValSpecialRuntime && (array_type->id != TypeTableEntryIdPointer || array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) @@ -13401,7 +13487,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc } else if (array_type->id == TypeTableEntryIdArray) { ConstExprValue *out_val = ir_build_const_from(ira, &elem_ptr_instruction->base); 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.mut = orig_array_ptr_val->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; return return_type; @@ -17406,14 +17492,29 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { - TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, - array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, - PtrLenUnknown, - array_type->data.pointer.alignment, 0, 0); - return_type = get_slice_type(ira->codegen, slice_ptr_type); - if (!end) { - ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); - return ira->codegen->builtin_types.entry_invalid; + if (array_type->data.pointer.ptr_len == PtrLenSingle) { + TypeTableEntry *main_type = array_type->data.pointer.child_type; + if (main_type->id == TypeTableEntryIdArray) { + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, + main_type->data.pointer.child_type, + array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, + array_type->data.pointer.alignment, 0, 0); + return_type = get_slice_type(ira->codegen, slice_ptr_type); + } else { + ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); + return ira->codegen->builtin_types.entry_invalid; + } + } else { + TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, + array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, + array_type->data.pointer.alignment, 0, 0); + return_type = get_slice_type(ira->codegen, slice_ptr_type); + 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)) { TypeTableEntry *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry; @@ -17433,12 +17534,24 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; - if (array_type->id == TypeTableEntryIdArray) { - array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); - abs_offset = 0; - rel_end = array_type->data.array.len; - parent_ptr = nullptr; + if (array_type->id == TypeTableEntryIdArray || + (array_type->id == TypeTableEntryIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == TypeTableEntryIdPointer) { + TypeTableEntry *child_array_type = array_type->data.pointer.child_type; + assert(child_array_type->id == TypeTableEntryIdArray); + parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); + array_val = const_ptr_pointee(ira->codegen, parent_ptr); + rel_end = child_array_type->data.array.len; + abs_offset = 0; + } else { + array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); + rel_end = array_type->data.array.len; + parent_ptr = nullptr; + abs_offset = 0; + } } else if (array_type->id == TypeTableEntryIdPointer) { + assert(array_type->data.pointer.ptr_len == PtrLenUnknown); parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); if (parent_ptr->special == ConstValSpecialUndef) { array_val = nullptr; @@ -17537,7 +17650,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio if (array_val) { size_t index = abs_offset + start_scalar; bool is_const = slice_is_const(return_type); - init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const); + init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown); if (array_type->id == TypeTableEntryIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; } else if (is_slice(array_type)) { diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index 933958ac18..a906b714ab 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -59,7 +59,7 @@ pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: Ro float_decimal.exp += 1; // Re-size the buffer to use the reserved leading byte. - const one_before = @intToPtr(*u8, @ptrToInt(&float_decimal.digits[0]) - 1); + const one_before = @intToPtr([*]u8, @ptrToInt(&float_decimal.digits[0]) - 1); float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; float_decimal.digits[0] = '1'; return; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 21991e9ba3..047a154bb8 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -278,7 +278,7 @@ pub fn formatAsciiChar( comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { - return output(context, (&c)[0..1]); + return output(context, (*[1]u8)(&c)[0..]); } pub fn formatBuf( @@ -603,7 +603,7 @@ fn formatIntSigned( const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { const minus_sign: u8 = '-'; - try output(context, (&minus_sign)[0..1]); + try output(context, (*[1]u8)(&minus_sign)[0..]); const new_value = uint(-(value + 1)) + 1; const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); @@ -611,7 +611,7 @@ fn formatIntSigned( return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output); } else { const plus_sign: u8 = '+'; - try output(context, (&plus_sign)[0..1]); + try output(context, (*[1]u8)(&plus_sign)[0..]); const new_value = uint(value); const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); @@ -648,7 +648,7 @@ fn formatIntUnsigned( const zero_byte: u8 = '0'; var leftover_padding = padding - index; while (true) { - try output(context, (&zero_byte)[0..1]); + try output(context, (*[1]u8)(&zero_byte)[0..]); leftover_padding -= 1; if (leftover_padding == 0) break; } diff --git a/std/heap.zig b/std/heap.zig index 0b8f4aeb3f..4444a2307a 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -24,7 +24,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const old_ptr = @ptrCast([*]c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { - return @ptrCast(*u8, buf)[0..new_size]; + return @ptrCast([*]u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { diff --git a/std/io.zig b/std/io.zig index e20a284e4e..a603d0cf5e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -219,12 +219,12 @@ pub fn OutStream(comptime WriteError: type) type { } pub fn writeByte(self: *Self, byte: u8) !void { - const slice = (&byte)[0..1]; + const slice = (*[1]u8)(&byte)[0..]; return self.writeFn(self, slice); } pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) !void { - const slice = (&byte)[0..1]; + const slice = (*[1]u8)(&byte)[0..]; var i: usize = 0; while (i < n) : (i += 1) { try self.writeFn(self, slice); diff --git a/std/macho.zig b/std/macho.zig index e71ac76b1a..d6eef9a325 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -164,7 +164,7 @@ fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void { return in.stream.readNoEof(([]u8)(result)); } fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void { - return readNoEof(in, T, result[0..1]); + return readNoEof(in, T, (*[1]T)(result)[0..]); } fn isSymbol(sym: *const Nlist64) bool { diff --git a/std/mem.zig b/std/mem.zig index aec24e8491..423460e73b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -31,14 +31,16 @@ pub const Allocator = struct { /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: *Allocator, old_mem: []u8) void, - fn create(self: *Allocator, comptime T: type) !*T { + /// Call destroy with the result + pub fn create(self: *Allocator, comptime T: type) !*T { if (@sizeOf(T) == 0) return *{}; const slice = try self.alloc(T, 1); return &slice[0]; } - // TODO once #733 is solved, this will replace create - fn construct(self: *Allocator, init: var) t: { + /// Call destroy with the result + /// TODO once #733 is solved, this will replace create + pub fn construct(self: *Allocator, init: var) t: { // TODO this is a workaround for type getting parsed as Error!&const T const T = @typeOf(init).Child; break :t Error!*T; @@ -51,17 +53,19 @@ pub const Allocator = struct { return ptr; } - fn destroy(self: *Allocator, ptr: var) void { - self.free(ptr[0..1]); + /// `ptr` should be the return value of `construct` or `create` + pub fn destroy(self: *Allocator, ptr: var) void { + const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); + self.freeFn(self, non_const_ptr[0..@sizeOf(@typeOf(ptr).Child)]); } - fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { + pub fn alloc(self: *Allocator, comptime T: type, n: usize) ![]T { return self.alignedAlloc(T, @alignOf(T), n); } - fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { + pub fn alignedAlloc(self: *Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { if (n == 0) { - return (*align(alignment) T)(undefined)[0..0]; + return ([*]align(alignment) T)(undefined)[0..0]; } const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); @@ -73,17 +77,17 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { + pub fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { + pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); } if (n == 0) { self.free(old_mem); - return (*align(alignment) T)(undefined)[0..0]; + return ([*]align(alignment) T)(undefined)[0..0]; } const old_byte_slice = ([]u8)(old_mem); @@ -102,11 +106,11 @@ pub const Allocator = struct { /// Reallocate, but `n` must be less than or equal to `old_mem.len`. /// Unlike `realloc`, this function cannot fail. /// Shrinking to 0 is the same as calling `free`. - fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { + pub fn shrink(self: *Allocator, comptime T: type, old_mem: []T, n: usize) []T { return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } - fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { + pub fn alignedShrink(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) []align(alignment) T { if (n == 0) { self.free(old_mem); return old_mem[0..0]; @@ -123,10 +127,10 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn free(self: *Allocator, memory: var) void { + pub fn free(self: *Allocator, memory: var) void { const bytes = ([]const u8)(memory); if (bytes.len == 0) return; - const non_const_ptr = @intToPtr(*u8, @ptrToInt(bytes.ptr)); + const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); } }; diff --git a/std/net.zig b/std/net.zig index bfe4b1c2a0..f21611ff91 100644 --- a/std/net.zig +++ b/std/net.zig @@ -68,7 +68,7 @@ pub const Address = struct { pub fn parseIp4(buf: []const u8) !u32 { var result: u32 = undefined; - const out_ptr = ([]u8)((&result)[0..1]); + const out_ptr = ([]u8)((*[1]u32)(&result)[0..]); var x: u8 = 0; var index: u8 = 0; diff --git a/std/os/index.zig b/std/os/index.zig index 7e908af9eb..6023929b04 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1240,7 +1240,7 @@ pub const Dir = struct { const next_index = self.index + darwin_entry.d_reclen; self.index = next_index; - const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen]; + const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; // skip . and .. entries if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { @@ -1704,7 +1704,7 @@ pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { for (args_alloc) |arg| { total_bytes += @sizeOf([]u8) + arg.len; } - const unaligned_allocated_buf = @ptrCast(*const u8, args_alloc.ptr)[0..total_bytes]; + const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); return allocator.free(aligned_allocated_buf); } diff --git a/test/cases/align.zig b/test/cases/align.zig index b80727258e..682c185e86 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -6,7 +6,7 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@typeOf(&foo).alignment == 4); assert(@typeOf(&foo) == *align(4) u8); - const slice = (&foo)[0..1]; + const slice = (*[1]u8)(&foo)[0..]; assert(@typeOf(slice) == []align(4) u8); } @@ -60,7 +60,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnalignedSlice((&a)[0..1], (&b)[0..1]) == 7); + assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); } fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; diff --git a/test/cases/array.zig b/test/cases/array.zig index 9a405216d8..ef919b27bd 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -115,3 +115,32 @@ test "array len property" { var x: [5]i32 = undefined; assert(@typeOf(x).len == 5); } + +test "single-item pointer to array indexing and slicing" { + testSingleItemPtrArrayIndexSlice(); + comptime testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() void { + var array = "aaaa"; + doSomeMangling(&array); + assert(mem.eql(u8, "azya", array)); +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + testImplicitCastSingleItemPtr(); + comptime testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() void { + var byte: u8 = 100; + const slice = (*[1]u8)(&byte)[0..]; + slice[0] += 1; + assert(byte == 101); +} + diff --git a/test/cases/eval.zig b/test/cases/eval.zig index b6d6a4f37b..461408afea 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -418,9 +418,9 @@ test "string literal used as comptime slice is memoized" { } test "comptime slice of undefined pointer of length 0" { - const slice1 = (*i32)(undefined)[0..0]; + const slice1 = ([*]i32)(undefined)[0..0]; assert(slice1.len == 0); - const slice2 = (*i32)(undefined)[100..100]; + const slice2 = ([*]i32)(undefined)[100..100]; assert(slice2.len == 0); } @@ -508,7 +508,7 @@ test "comptime slice of slice preserves comptime var" { test "comptime slice of pointer preserves comptime var" { comptime { var buff: [10]u8 = undefined; - var a = &buff[0]; + var a = buff[0..].ptr; a[0..1][0] = 1; assert(buff[0..][0..][0] == 1); } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 5899f20f9c..e007ec4c46 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -274,7 +274,7 @@ test "generic malloc free" { } var some_mem: [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { - return @ptrCast(*T, &some_mem[0])[0..n]; + return @ptrCast([*]T, &some_mem[0])[0..n]; } fn memFree(comptime T: type, memory: []T) void {} @@ -588,7 +588,7 @@ var global_ptr = &gdt[0]; // can't really run this test but we can make sure it has no compile error // and generates code -const vram = @intToPtr(*volatile u8, 0x20000000)[0..0x8000]; +const vram = @intToPtr([*]volatile u8, 0x20000000)[0..0x8000]; export fn writeToVRam() void { vram[0] = 'X'; } diff --git a/test/cases/slice.zig b/test/cases/slice.zig index 24e5239e2d..b4b43bdd19 100644 --- a/test/cases/slice.zig +++ b/test/cases/slice.zig @@ -1,7 +1,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -const x = @intToPtr(*i32, 0x1000)[0..0x500]; +const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { assert(@ptrToInt(x.ptr) == 0x1000); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7e9ef82e42..17136e150f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,13 +1,22 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "slicing single-item pointer", + \\export fn entry(ptr: *i32) void { + \\ const slice = ptr[0..2]; + \\} + , + ".tmp_source.zig:2:22: error: slice of single-item pointer", + ); + cases.add( "indexing single-item pointer", \\export fn entry(ptr: *i32) i32 { \\ return ptr[1]; \\} , - ".tmp_source.zig:2:15: error: indexing not allowed on pointer to single item", + ".tmp_source.zig:2:15: error: index of single-item pointer", ); cases.add( @@ -144,10 +153,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "comptime slice of undefined pointer non-zero len", \\export fn entry() void { - \\ const slice = (*i32)(undefined)[0..1]; + \\ const slice = ([*]i32)(undefined)[0..1]; \\} , - ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer", + ".tmp_source.zig:2:38: error: non-zero length slice of undefined pointer", ); cases.add( @@ -3129,14 +3138,16 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var foo = Foo { .a = 1, .b = 10 }; \\ foo.b += 1; - \\ bar((&foo.b)[0..1]); + \\ bar((*[1]u32)(&foo.b)[0..]); \\} \\ \\fn bar(x: []u32) void { \\ x[0] += 1; \\} , - ".tmp_source.zig:9:17: error: expected type '[]u32', found '[]align(1) u32'", + ".tmp_source.zig:9:18: error: cast increases pointer alignment", + ".tmp_source.zig:9:23: note: '*align(1) u32' has alignment 1", + ".tmp_source.zig:9:18: note: '*[1]u32' has alignment 4", ); cases.add( -- cgit v1.2.3 From 02cb220faf0d527b656a3a87ec96e6738770c8e6 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Tue, 5 Jun 2018 11:14:43 +0200 Subject: Renamed "(int/float literal)" to "comptime_int/float" --- doc/langref.html.in | 18 ++-- src/all_types.hpp | 4 +- src/analyze.cpp | 94 ++++++++++---------- src/codegen.cpp | 38 ++++---- src/ir.cpp | 230 ++++++++++++++++++++++++------------------------ std/math/ln.zig | 4 +- std/math/log.zig | 6 +- std/math/log10.zig | 4 +- std/math/log2.zig | 4 +- std/math/sqrt.zig | 4 +- test/cases/math.zig | 24 +++-- test/cases/misc.zig | 4 +- test/compile_errors.zig | 12 +-- 13 files changed, 230 insertions(+), 216 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 28fdf4d8b9..0689baa6f9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4893,8 +4893,8 @@ pub const TypeId = enum { Pointer, Array, Struct, - FloatLiteral, - IntLiteral, + ComptimeFloat, + ComptimeInt, UndefinedLiteral, NullLiteral, Nullable, @@ -4927,8 +4927,8 @@ pub const TypeInfo = union(TypeId) { Pointer: Pointer, Array: Array, Struct: Struct, - FloatLiteral: void, - IntLiteral: void, + ComptimeFloat: void, + ComptimeInt: void, UndefinedLiteral: void, NullLiteral: void, Nullable: Nullable, @@ -5685,8 +5685,8 @@ pub const TypeId = enum { Pointer, Array, Struct, - FloatLiteral, - IntLiteral, + ComptimeFloat, + ComptimeInt, UndefinedLiteral, NullLiteral, Nullable, @@ -5713,10 +5713,10 @@ pub const TypeInfo = union(TypeId) { Pointer: Pointer, Array: Array, Struct: Struct, - FloatLiteral: void, - IntLiteral: void, + ComptimeFloat: void, + ComptimeInt: void, UndefinedLiteral: void, - NullLiteral: void, + Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, ErrorSet: ErrorSet, diff --git a/src/all_types.hpp b/src/all_types.hpp index d237eb00bb..bf635eae7c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1159,8 +1159,8 @@ enum TypeTableEntryId { TypeTableEntryIdPointer, TypeTableEntryIdArray, TypeTableEntryIdStruct, - TypeTableEntryIdNumLitFloat, - TypeTableEntryIdNumLitInt, + TypeTableEntryIdComptimeFloat, + TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, TypeTableEntryIdNullLit, TypeTableEntryIdMaybe, diff --git a/src/analyze.cpp b/src/analyze.cpp index 31c0726459..21841d45b6 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -232,8 +232,8 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -268,8 +268,8 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -1333,8 +1333,8 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: @@ -1374,8 +1374,8 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: @@ -1518,8 +1518,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -1607,8 +1607,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c buf_sprintf("return type '%s' not allowed", buf_ptr(&fn_type_id.return_type->name))); return g->builtin_types.entry_invalid; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -3337,8 +3337,6 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: @@ -3347,6 +3345,8 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt add_node_error(g, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdNamespace: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: @@ -3480,8 +3480,8 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); implicit_type = g->builtin_types.entry_invalid; } else if ((!is_const || linkage == VarLinkageExternal) && - (implicit_type->id == TypeTableEntryIdNumLitFloat || - implicit_type->id == TypeTableEntryIdNumLitInt)) + (implicit_type->id == TypeTableEntryIdComptimeFloat || + implicit_type->id == TypeTableEntryIdComptimeInt)) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; @@ -3730,8 +3730,8 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -3779,8 +3779,8 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -4283,8 +4283,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -4568,7 +4568,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdVoid: return (uint32_t)4149439618; case TypeTableEntryIdInt: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: { uint32_t result = 1331471175; for (size_t i = 0; i < const_val->data.x_bigint.digit_count; i += 1) { @@ -4609,7 +4609,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { default: zig_unreachable(); } - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: { float128_t f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); uint32_t ints[4]; @@ -4754,8 +4754,8 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -4819,8 +4819,8 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -4930,8 +4930,8 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdInvalid: case TypeTableEntryIdOpaque: zig_unreachable(); - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMetaType: @@ -5070,7 +5070,7 @@ ConstExprValue *create_const_signed(TypeTableEntry *type, int64_t x) { void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double value) { const_val->special = ConstValSpecialStatic; const_val->type = type; - if (type->id == TypeTableEntryIdNumLitFloat) { + if (type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_64(&const_val->data.x_bigfloat, value); } else if (type->id == TypeTableEntryIdFloat) { switch (type->data.floating.bit_count) { @@ -5350,10 +5350,10 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { default: zig_unreachable(); } - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: return bigfloat_cmp(&a->data.x_bigfloat, &b->data.x_bigfloat) == CmpEQ; case TypeTableEntryIdInt: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: case TypeTableEntryIdFn: @@ -5514,7 +5514,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case TypeTableEntryIdVoid: buf_appendf(buf, "{}"); return; - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: bigfloat_append_buf(buf, &const_val->data.x_bigfloat); return; case TypeTableEntryIdFloat: @@ -5542,7 +5542,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { default: zig_unreachable(); } - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: bigint_append_buf(buf, &const_val->data.x_bigint, 10); return; @@ -5761,8 +5761,8 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -5807,8 +5807,8 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdFloat: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -5929,8 +5929,8 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdPointer, TypeTableEntryIdArray, TypeTableEntryIdStruct, - TypeTableEntryIdNumLitFloat, - TypeTableEntryIdNumLitInt, + TypeTableEntryIdComptimeFloat, + TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, TypeTableEntryIdNullLit, TypeTableEntryIdMaybe, @@ -5980,9 +5980,9 @@ size_t type_id_index(TypeTableEntry *entry) { if (entry->data.structure.is_slice) return 25; return 8; - case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdComptimeFloat: return 9; - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeInt: return 10; case TypeTableEntryIdUndefLit: return 11; @@ -6038,10 +6038,10 @@ const char *type_id_name(TypeTableEntryId id) { return "Array"; case TypeTableEntryIdStruct: return "Struct"; - case TypeTableEntryIdNumLitFloat: - return "FloatLiteral"; - case TypeTableEntryIdNumLitInt: - return "IntLiteral"; + case TypeTableEntryIdComptimeFloat: + return "ComptimeFloat"; + case TypeTableEntryIdComptimeInt: + return "ComptimeInt"; case TypeTableEntryIdUndefLit: return "UndefinedLiteral"; case TypeTableEntryIdNullLit: diff --git a/src/codegen.cpp b/src/codegen.cpp index 49c93feaa5..dc915e766d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4916,8 +4916,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: @@ -5362,8 +5362,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -5604,7 +5604,7 @@ static void do_code_gen(CodeGen *g) { TldVar *tld_var = g->global_vars.at(i); VariableTableEntry *var = tld_var->var; - if (var->value->type->id == TypeTableEntryIdNumLitFloat) { + if (var->value->type->id == TypeTableEntryIdComptimeFloat) { // Generate debug info for it but that's it. ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); @@ -5618,7 +5618,7 @@ static void do_code_gen(CodeGen *g) { continue; } - if (var->value->type->id == TypeTableEntryIdNumLitInt) { + if (var->value->type->id == TypeTableEntryIdComptimeInt) { // Generate debug info for it but that's it. ConstExprValue *const_val = var->value; assert(const_val->special != ConstValSpecialRuntime); @@ -6012,16 +6012,18 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_block = entry; } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat); - buf_init_from_str(&entry->name, "(float literal)"); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdComptimeFloat); + buf_init_from_str(&entry->name, "comptime_float"); entry->zero_bits = true; g->builtin_types.entry_num_lit_float = entry; + g->primitive_type_table.put(&entry->name, entry); } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt); - buf_init_from_str(&entry->name, "(integer literal)"); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdComptimeInt); + buf_init_from_str(&entry->name, "comptime_int"); entry->zero_bits = true; g->builtin_types.entry_num_lit_int = entry; + g->primitive_type_table.put(&entry->name, entry); } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit); @@ -6495,8 +6497,8 @@ static void define_builtin_compile_vars(CodeGen *g) { " Slice: Slice,\n" " Array: Array,\n" " Struct: Struct,\n" - " FloatLiteral: void,\n" - " IntLiteral: void,\n" + " ComptimeFloat: void,\n" + " ComptimeInt: void,\n" " UndefinedLiteral: void,\n" " NullLiteral: void,\n" " Nullable: Nullable,\n" @@ -7070,8 +7072,8 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -7255,8 +7257,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdBoundFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdArgTuple: @@ -7407,8 +7409,8 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdArray: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: diff --git a/src/ir.cpp b/src/ir.cpp index 5cea04ea55..2819ef5b0e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6945,14 +6945,14 @@ static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *so } static bool const_val_fits_in_num_lit(ConstExprValue *const_val, TypeTableEntry *num_lit_type) { - return ((num_lit_type->id == TypeTableEntryIdNumLitFloat && - (const_val->type->id == TypeTableEntryIdFloat || const_val->type->id == TypeTableEntryIdNumLitFloat)) || - (num_lit_type->id == TypeTableEntryIdNumLitInt && - (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdNumLitInt))); + return ((num_lit_type->id == TypeTableEntryIdComptimeFloat && + (const_val->type->id == TypeTableEntryIdFloat || const_val->type->id == TypeTableEntryIdComptimeFloat)) || + (num_lit_type->id == TypeTableEntryIdComptimeInt && + (const_val->type->id == TypeTableEntryIdInt || const_val->type->id == TypeTableEntryIdComptimeInt))); } static bool float_has_fraction(ConstExprValue *const_val) { - if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + if (const_val->type->id == TypeTableEntryIdComptimeFloat) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { @@ -6975,7 +6975,7 @@ static bool float_has_fraction(ConstExprValue *const_val) { } static void float_append_buf(Buf *buf, ConstExprValue *const_val) { - if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + if (const_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { @@ -7010,7 +7010,7 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) { } static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { - if (const_val->type->id == TypeTableEntryIdNumLitFloat) { + if (const_val->type->id == TypeTableEntryIdComptimeFloat) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { @@ -7046,7 +7046,7 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { } static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7068,7 +7068,7 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { } static void float_init_f32(ConstExprValue *dest_val, float x) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7094,7 +7094,7 @@ static void float_init_f32(ConstExprValue *dest_val, float x) { } static void float_init_f64(ConstExprValue *dest_val, double x) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7120,7 +7120,7 @@ static void float_init_f64(ConstExprValue *dest_val, double x) { } static void float_init_f128(ConstExprValue *dest_val, float128_t x) { - if (dest_val->type->id == TypeTableEntryIdNumLitFloat) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { @@ -7150,7 +7150,7 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) { } static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) { - if (src_val->type->id == TypeTableEntryIdNumLitFloat) { + if (src_val->type->id == TypeTableEntryIdComptimeFloat) { float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == TypeTableEntryIdFloat) { switch (src_val->type->data.floating.bit_count) { @@ -7173,7 +7173,7 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7210,7 +7210,7 @@ static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { } static Cmp float_cmp_zero(ConstExprValue *op) { - if (op->type->id == TypeTableEntryIdNumLitFloat) { + if (op->type->id == TypeTableEntryIdComptimeFloat) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { @@ -7251,7 +7251,7 @@ static Cmp float_cmp_zero(ConstExprValue *op) { static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7275,7 +7275,7 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7299,7 +7299,7 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7323,7 +7323,7 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7347,7 +7347,7 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7382,7 +7382,7 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7407,7 +7407,7 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7431,7 +7431,7 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; - if (op1->type->id == TypeTableEntryIdNumLitFloat) { + if (op1->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { @@ -7456,7 +7456,7 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { out_val->type = op->type; - if (op->type->id == TypeTableEntryIdNumLitFloat) { + if (op->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { @@ -7530,9 +7530,9 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc assert(const_val->special != ConstValSpecialRuntime); bool const_val_is_int = (const_val->type->id == TypeTableEntryIdInt || - const_val->type->id == TypeTableEntryIdNumLitInt); + const_val->type->id == TypeTableEntryIdComptimeInt); bool const_val_is_float = (const_val->type->id == TypeTableEntryIdFloat || - const_val->type->id == TypeTableEntryIdNumLitFloat); + const_val->type->id == TypeTableEntryIdComptimeFloat); if (other_type->id == TypeTableEntryIdFloat) { return true; } else if (other_type->id == TypeTableEntryIdInt && const_val_is_int) { @@ -7576,7 +7576,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc return true; } } - if (explicit_cast && (other_type->id == TypeTableEntryIdInt || other_type->id == TypeTableEntryIdNumLitInt) && + if (explicit_cast && (other_type->id == TypeTableEntryIdInt || other_type->id == TypeTableEntryIdComptimeInt) && const_val_is_float) { if (float_has_fraction(const_val)) { @@ -7589,7 +7589,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc buf_ptr(&other_type->name))); return false; } else { - if (other_type->id == TypeTableEntryIdNumLitInt) { + if (other_type->id == TypeTableEntryIdComptimeInt) { return true; } else { BigInt bigint; @@ -8078,8 +8078,8 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit number literal to typed number // implicit number literal to &const integer - if (actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt) + if (actual_type->id == TypeTableEntryIdComptimeFloat || + actual_type->id == TypeTableEntryIdComptimeInt) { if (expected_type->id == TypeTableEntryIdPointer && expected_type->data.pointer.is_const) @@ -8099,9 +8099,9 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit typed number to integer or float literal. // works when the number is known if (value->value.special == ConstValSpecialStatic) { - if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdNumLitInt) { + if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdComptimeInt) { return ImplicitCastMatchResultYes; - } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdNumLitFloat) { + } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdComptimeFloat) { return ImplicitCastMatchResultYes; } } @@ -8555,8 +8555,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdNumLitInt || - prev_type->id == TypeTableEntryIdNumLitFloat) + if (prev_type->id == TypeTableEntryIdComptimeInt || + prev_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { prev_inst = cur_inst; @@ -8566,8 +8566,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (cur_type->id == TypeTableEntryIdNumLitInt || - cur_type->id == TypeTableEntryIdNumLitFloat) + if (cur_type->id == TypeTableEntryIdComptimeInt || + cur_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { continue; @@ -8671,8 +8671,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) { return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type); } else { - if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || - prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) + if (prev_inst->value.type->id == TypeTableEntryIdComptimeInt || + prev_inst->value.type->id == TypeTableEntryIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of number literal")); @@ -8686,8 +8686,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { - if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || - prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) + if (prev_inst->value.type->id == TypeTableEntryIdComptimeInt || + prev_inst->value.type->id == TypeTableEntryIdComptimeFloat) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make maybe out of number literal")); @@ -8743,7 +8743,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, break; } case CastOpNumLitToConcrete: - if (other_val->type->id == TypeTableEntryIdNumLitFloat) { + if (other_val->type->id == TypeTableEntryIdComptimeFloat) { assert(new_type->id == TypeTableEntryIdFloat); switch (new_type->data.floating.bit_count) { case 32: @@ -8758,7 +8758,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, default: zig_unreachable(); } - } else if (other_val->type->id == TypeTableEntryIdNumLitInt) { + } else if (other_val->type->id == TypeTableEntryIdComptimeInt) { bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); } else { zig_unreachable(); @@ -9601,9 +9601,9 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - if (wanted_type->id == TypeTableEntryIdNumLitFloat) { + if (wanted_type->id == TypeTableEntryIdComptimeFloat) { float_init_float(&result->value, val); - } else if (wanted_type->id == TypeTableEntryIdNumLitInt) { + } else if (wanted_type->id == TypeTableEntryIdComptimeInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); } else { zig_unreachable(); @@ -9978,8 +9978,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); - } else if (actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) + } else if (actual_type->id == TypeTableEntryIdComptimeInt || + actual_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); @@ -10013,8 +10013,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); - } else if (actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) + } else if (actual_type->id == TypeTableEntryIdComptimeInt || + actual_type->id == TypeTableEntryIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); @@ -10062,8 +10062,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || actual_type->id == TypeTableEntryIdNullLit || - actual_type->id == TypeTableEntryIdNumLitInt || - actual_type->id == TypeTableEntryIdNumLitFloat) + actual_type->id == TypeTableEntryIdComptimeInt || + actual_type->id == TypeTableEntryIdComptimeFloat) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -10079,8 +10079,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from number literal to another type // explicit cast from number literal to &const integer - if (actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt) + if (actual_type->id == TypeTableEntryIdComptimeFloat || + actual_type->id == TypeTableEntryIdComptimeInt) { ensure_complete_type(ira->codegen, wanted_type); if (type_is_invalid(wanted_type)) @@ -10109,9 +10109,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return cast2; } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { CastOp op; - if ((actual_type->id == TypeTableEntryIdNumLitFloat && + if ((actual_type->id == TypeTableEntryIdComptimeFloat && wanted_type->id == TypeTableEntryIdFloat) || - (actual_type->id == TypeTableEntryIdNumLitInt && + (actual_type->id == TypeTableEntryIdComptimeInt && wanted_type->id == TypeTableEntryIdInt)) { op = CastOpNumLitToConcrete; @@ -10131,8 +10131,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from typed number to integer or float literal. // works when the number is known at compile time if (instr_is_comptime(value) && - ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdNumLitInt) || - (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdNumLitFloat))) + ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) || + (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat))) { return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } @@ -10759,8 +10759,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdInvalid: zig_unreachable(); // handled above - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: break; @@ -10818,10 +10818,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) { bool answer; - if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) { + if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); answer = resolve_cmp_op_id(op_id, cmp_result); - } else if (resolved_type->id == TypeTableEntryIdNumLitInt || resolved_type->id == TypeTableEntryIdInt) { + } else if (resolved_type->id == TypeTableEntryIdComptimeInt || resolved_type->id == TypeTableEntryIdInt) { Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); answer = resolve_cmp_op_id(op_id, cmp_result); } else { @@ -10885,12 +10885,12 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, bool is_int; bool is_float; Cmp op2_zcmp; - if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdNumLitInt) { + if (type_entry->id == TypeTableEntryIdInt || type_entry->id == TypeTableEntryIdComptimeInt) { is_int = true; is_float = false; op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); } else if (type_entry->id == TypeTableEntryIdFloat || - type_entry->id == TypeTableEntryIdNumLitFloat) + type_entry->id == TypeTableEntryIdComptimeFloat) { is_int = false; is_float = true; @@ -11064,7 +11064,7 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * if (type_is_invalid(op1->value.type)) return ira->codegen->builtin_types.entry_invalid; - if (op1->value.type->id != TypeTableEntryIdInt && op1->value.type->id != TypeTableEntryIdNumLitInt) { + if (op1->value.type->id != TypeTableEntryIdInt && op1->value.type->id != TypeTableEntryIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("bit shifting operation expected integer type, found '%s'", buf_ptr(&op1->value.type->name))); @@ -11077,7 +11077,7 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * IrInstruction *casted_op2; IrBinOp op_id = bin_op_instruction->op_id; - if (op1->value.type->id == TypeTableEntryIdNumLitInt) { + if (op1->value.type->id == TypeTableEntryIdComptimeInt) { casted_op2 = op2; if (op_id == IrBinOpBitShiftLeftLossy) { @@ -11122,7 +11122,7 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false); return op1->value.type; - } else if (op1->value.type->id == TypeTableEntryIdNumLitInt) { + } else if (op1->value.type->id == TypeTableEntryIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); return ira->codegen->builtin_types.entry_invalid; @@ -11158,15 +11158,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (type_is_invalid(resolved_type)) return resolved_type; - bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt; - bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdNumLitFloat; + bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdComptimeInt; + bool is_float = resolved_type->id == TypeTableEntryIdFloat || resolved_type->id == TypeTableEntryIdComptimeFloat; bool is_signed_div = ( (resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) || resolved_type->id == TypeTableEntryIdFloat || - (resolved_type->id == TypeTableEntryIdNumLitFloat && + (resolved_type->id == TypeTableEntryIdComptimeFloat && ((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) != (bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) || - (resolved_type->id == TypeTableEntryIdNumLitInt && + (resolved_type->id == TypeTableEntryIdComptimeInt && ((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) != (bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT))) ); @@ -11267,7 +11267,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_invalid; } - if (resolved_type->id == TypeTableEntryIdNumLitInt) { + if (resolved_type->id == TypeTableEntryIdComptimeInt) { if (op_id == IrBinOpAddWrap) { op_id = IrBinOpAdd; } else if (op_id == IrBinOpSubWrap) { @@ -11641,8 +11641,8 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdFn: case TypeTableEntryIdPromise: return VarClassRequiredAny; - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdBlock: case TypeTableEntryIdNullLit: @@ -11910,8 +11910,8 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -11934,8 +11934,8 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -12149,7 +12149,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } bool comptime_arg = param_decl_node->data.param_decl.is_inline || - casted_arg->value.type->id == TypeTableEntryIdNumLitInt || casted_arg->value.type->id == TypeTableEntryIdNumLitFloat; + casted_arg->value.type->id == TypeTableEntryIdComptimeInt || casted_arg->value.type->id == TypeTableEntryIdComptimeFloat; ConstExprValue *arg_val; @@ -12174,8 +12174,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod var->shadowable = !comptime_arg; *next_proto_i += 1; - } else if (casted_arg->value.type->id == TypeTableEntryIdNumLitInt || - casted_arg->value.type->id == TypeTableEntryIdNumLitFloat) + } else if (casted_arg->value.type->id == TypeTableEntryIdComptimeInt || + casted_arg->value.type->id == TypeTableEntryIdComptimeFloat) { ir_add_error(ira, casted_arg, buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557")); @@ -12898,8 +12898,8 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -12935,10 +12935,10 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap); - bool is_float = (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat); + bool is_float = (expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdComptimeFloat); if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) || - expr_type->id == TypeTableEntryIdNumLitInt || (is_float && !is_wrap_op)) + expr_type->id == TypeTableEntryIdComptimeInt || (is_float && !is_wrap_op)) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); @@ -12954,7 +12954,7 @@ static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un } else { bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint); } - if (is_wrap_op || is_float || expr_type->id == TypeTableEntryIdNumLitInt) { + if (is_wrap_op || is_float || expr_type->id == TypeTableEntryIdComptimeInt) { return expr_type; } @@ -13150,8 +13150,8 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (type_is_invalid(resolved_type)) return resolved_type; - if (resolved_type->id == TypeTableEntryIdNumLitFloat || - resolved_type->id == TypeTableEntryIdNumLitInt || + if (resolved_type->id == TypeTableEntryIdComptimeFloat || + resolved_type->id == TypeTableEntryIdComptimeInt || resolved_type->id == TypeTableEntryIdNullLit || resolved_type->id == TypeTableEntryIdUndefLit) { @@ -14213,8 +14213,8 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); // handled above - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -14495,8 +14495,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -14603,8 +14603,8 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -14659,8 +14659,8 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdBlock: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: @@ -15020,8 +15020,8 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdPointer: case TypeTableEntryIdPromise: case TypeTableEntryIdFn: @@ -15618,8 +15618,8 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdPromise: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: @@ -16280,8 +16280,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -17143,7 +17143,7 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; if (dest_type->id != TypeTableEntryIdInt && - dest_type->id != TypeTableEntryIdNumLitInt) + dest_type->id != TypeTableEntryIdComptimeInt) { ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -17155,7 +17155,7 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; if (src_type->id != TypeTableEntryIdInt && - src_type->id != TypeTableEntryIdNumLitInt) + src_type->id != TypeTableEntryIdComptimeInt) { ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -17876,8 +17876,8 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdNamespace: @@ -18377,8 +18377,8 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; - assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdNumLitInt); - assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdNumLitInt); + assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); + assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { @@ -18610,8 +18610,8 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdPromise: @@ -18677,8 +18677,8 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdPromise: @@ -18758,8 +18758,8 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: ir_add_error(ira, dest_type_value, @@ -18784,8 +18784,8 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: ir_add_error(ira, dest_type_value, @@ -19560,7 +19560,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction if (type_is_invalid(op->value.type)) return ira->codegen->builtin_types.entry_invalid; - bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat; + bool ok_type = float_type->id == TypeTableEntryIdComptimeFloat || float_type->id == TypeTableEntryIdFloat; if (!ok_type) { ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -19577,7 +19577,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - if (float_type->id == TypeTableEntryIdNumLitFloat) { + if (float_type->id == TypeTableEntryIdComptimeFloat) { bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); } else if (float_type->id == TypeTableEntryIdFloat) { switch (float_type->data.floating.bit_count) { diff --git a/std/math/ln.zig b/std/math/ln.zig index 263e5955cb..3fd75977b9 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -14,7 +14,7 @@ const TypeId = builtin.TypeId; pub fn ln(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(ln_64(x)); }, TypeId.Float => { @@ -24,7 +24,7 @@ pub fn ln(x: var) @typeOf(x) { else => @compileError("ln not implemented for " ++ @typeName(T)), }; }, - TypeId.IntLiteral => { + TypeId.ComptimeInt => { return @typeOf(1)(math.floor(ln_64(f64(x)))); }, TypeId.Int => { diff --git a/std/math/log.zig b/std/math/log.zig index 1cba1138db..2c876081d8 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -9,15 +9,15 @@ pub fn log(comptime T: type, base: T, x: T) T { return math.log2(x); } else if (base == 10) { return math.log10(x); - } else if ((@typeId(T) == TypeId.Float or @typeId(T) == TypeId.FloatLiteral) and base == math.e) { + } else if ((@typeId(T) == TypeId.Float or @typeId(T) == TypeId.ComptimeFloat) and base == math.e) { return math.ln(x); } switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(math.ln(f64(x)) / math.ln(f64(base))); }, - TypeId.IntLiteral => { + TypeId.ComptimeInt => { return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(f64(base)))); }, builtin.TypeId.Int => { diff --git a/std/math/log10.zig b/std/math/log10.zig index d9fa1dcb02..c444add9ac 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -14,7 +14,7 @@ const TypeId = builtin.TypeId; pub fn log10(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(log10_64(x)); }, TypeId.Float => { @@ -24,7 +24,7 @@ pub fn log10(x: var) @typeOf(x) { else => @compileError("log10 not implemented for " ++ @typeName(T)), }; }, - TypeId.IntLiteral => { + TypeId.ComptimeInt => { return @typeOf(1)(math.floor(log10_64(f64(x)))); }, TypeId.Int => { diff --git a/std/math/log2.zig b/std/math/log2.zig index 22cc8082b3..2530519941 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -14,7 +14,7 @@ const TypeId = builtin.TypeId; pub fn log2(x: var) @typeOf(x) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => { + TypeId.ComptimeFloat => { return @typeOf(1.0)(log2_64(x)); }, TypeId.Float => { @@ -24,7 +24,7 @@ pub fn log2(x: var) @typeOf(x) { else => @compileError("log2 not implemented for " ++ @typeName(T)), }; }, - TypeId.IntLiteral => comptime { + TypeId.ComptimeInt => comptime { var result = 0; var x_shifted = x; while (b: { diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 982bd28b72..7a3ddb3b96 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -14,9 +14,9 @@ const TypeId = builtin.TypeId; pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) { const T = @typeOf(x); switch (@typeId(T)) { - TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128 + TypeId.ComptimeFloat => return T(@sqrt(f64, x)), // TODO upgrade to f128 TypeId.Float => return @sqrt(T, x), - TypeId.IntLiteral => comptime { + TypeId.ComptimeInt => comptime { if (x > @maxValue(u128)) { @compileError("sqrt not implemented for comptime_int greater than 128 bits"); } diff --git a/test/cases/math.zig b/test/cases/math.zig index 0c18293dd5..0bf99cff0e 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -329,14 +329,14 @@ fn testShrExact(x: u8) void { assert(shifted == 0b00101101); } -test "big number addition" { +test "comptime_int addition" { comptime { assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } -test "big number multiplication" { +test "comptime_int multiplication" { comptime { assert( 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, @@ -347,13 +347,13 @@ test "big number multiplication" { } } -test "big number shifting" { +test "comptime_int shifting" { comptime { assert((u128(1) << 127) == 0x80000000000000000000000000000000); } } -test "big number multi-limb shift and mask" { +test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; @@ -370,7 +370,7 @@ test "big number multi-limb shift and mask" { } } -test "big number multi-limb partial shift right" { +test "comptime_int multi-limb partial shift right" { comptime { var a = 0x1ffffffffeeeeeeee; a >>= 16; @@ -391,7 +391,7 @@ fn test_xor() void { assert(0xFF ^ 0xFF == 0x00); } -test "big number xor" { +test "comptime_int xor" { comptime { assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); @@ -449,3 +449,15 @@ test "@sqrt" { fn testSqrt(comptime T: type, x: T) void { assert(@sqrt(T, x * x) == x); } + +test "comptime_int param and return" { + const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); + assert(a == 137114567242441932203689521744947848950); + + const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); + assert(b == 985095453608931032642182098849559179469148836107390954364380); +} + +fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { + return a + b; +} diff --git a/test/cases/misc.zig b/test/cases/misc.zig index e007ec4c46..1821e29a20 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -501,8 +501,8 @@ test "@typeId" { assert(@typeId(*f32) == Tid.Pointer); assert(@typeId([2]u8) == Tid.Array); assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.IntLiteral); - assert(@typeId(@typeOf(1.0)) == Tid.FloatLiteral); + assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); assert(@typeId(@typeOf(null)) == Tid.NullLiteral); assert(@typeId(?i32) == Tid.Nullable); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 17136e150f..e264d57b5e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1539,7 +1539,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const (integer literal)'", + ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", ); cases.add( @@ -1555,7 +1555,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const x = 2 == 2.0; \\export fn entry() usize { return @sizeOf(@typeOf(x)); } , - ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type '(float literal)'", + ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type 'comptime_float'", ); cases.add( @@ -2189,7 +2189,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(block_aligned_stuff)); } , - ".tmp_source.zig:3:60: error: unable to perform binary not operation on type '(integer literal)'", + ".tmp_source.zig:3:60: error: unable to perform binary not operation on type 'comptime_int'", ); cases.addCase(x: { @@ -3269,10 +3269,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ fn bar(self: *const Foo) void {} \\}; , - ".tmp_source.zig:4:4: error: variable of type '*(integer literal)' must be const or comptime", + ".tmp_source.zig:4:4: error: variable of type '*comptime_int' must be const or comptime", ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", - ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime", - ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime", + ".tmp_source.zig:8:4: error: variable of type 'comptime_int' must be const or comptime", + ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", -- cgit v1.2.3 From 236c680f6bae490fddab4935892bd75240176d0b Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Tue, 5 Jun 2018 11:30:01 +0200 Subject: Removed NullLiteral to Null --- doc/langref.html.in | 6 ++--- src/all_types.hpp | 2 +- src/analyze.cpp | 46 ++++++++++++++++++------------------- src/codegen.cpp | 14 ++++++------ src/ir.cpp | 66 ++++++++++++++++++++++++++--------------------------- test/cases/misc.zig | 2 +- 6 files changed, 68 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 0689baa6f9..70f11c0e2b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4896,7 +4896,7 @@ pub const TypeId = enum { ComptimeFloat, ComptimeInt, UndefinedLiteral, - NullLiteral, + Null, Nullable, ErrorUnion, Error, @@ -4930,7 +4930,7 @@ pub const TypeInfo = union(TypeId) { ComptimeFloat: void, ComptimeInt: void, UndefinedLiteral: void, - NullLiteral: void, + Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, ErrorSet: ErrorSet, @@ -5688,7 +5688,7 @@ pub const TypeId = enum { ComptimeFloat, ComptimeInt, UndefinedLiteral, - NullLiteral, + Null, Nullable, ErrorUnion, ErrorSet, diff --git a/src/all_types.hpp b/src/all_types.hpp index bf635eae7c..6b30a1155d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1162,7 +1162,7 @@ enum TypeTableEntryId { TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, - TypeTableEntryIdNullLit, + TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, diff --git a/src/analyze.cpp b/src/analyze.cpp index 21841d45b6..a605cb3a7f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -235,7 +235,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -271,7 +271,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -1336,7 +1336,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -1377,7 +1377,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -1512,7 +1512,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, param_node->data.param_decl.type, @@ -1600,7 +1600,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c zig_unreachable(); case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: add_node_error(g, fn_proto->return_type, @@ -3338,7 +3338,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -3485,7 +3485,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; - } else if (implicit_type->id == TypeTableEntryIdNullLit) { + } else if (implicit_type->id == TypeTableEntryIdNull) { add_node_error(g, source_node, buf_sprintf("unable to infer variable type")); implicit_type = g->builtin_types.entry_invalid; } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) { @@ -3733,7 +3733,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -3782,7 +3782,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -4286,7 +4286,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -4674,7 +4674,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return 223048345; case TypeTableEntryIdUndefLit: return 162837799; - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: return 844854567; case TypeTableEntryIdArray: // TODO better hashing algorithm @@ -4757,7 +4757,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: case TypeTableEntryIdFn: @@ -4822,7 +4822,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: case TypeTableEntryIdFn: @@ -4933,7 +4933,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -5412,7 +5412,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return true; case TypeTableEntryIdUndefLit: zig_panic("TODO"); - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: zig_panic("TODO"); case TypeTableEntryIdMaybe: if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) { @@ -5646,7 +5646,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "}"); return; } - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: { buf_appendf(buf, "null"); return; @@ -5764,7 +5764,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -5810,7 +5810,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: @@ -5932,7 +5932,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefLit, - TypeTableEntryIdNullLit, + TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, @@ -5986,7 +5986,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 10; case TypeTableEntryIdUndefLit: return 11; - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: return 12; case TypeTableEntryIdMaybe: return 13; @@ -6044,8 +6044,8 @@ const char *type_id_name(TypeTableEntryId id) { return "ComptimeInt"; case TypeTableEntryIdUndefLit: return "UndefinedLiteral"; - case TypeTableEntryIdNullLit: - return "NullLiteral"; + case TypeTableEntryIdNull: + return "Null"; case TypeTableEntryIdMaybe: return "Nullable"; case TypeTableEntryIdErrorUnion: diff --git a/src/codegen.cpp b/src/codegen.cpp index dc915e766d..3177a2491f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4919,7 +4919,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -5365,7 +5365,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -6032,7 +6032,7 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_undef = entry; } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNullLit); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNull); buf_init_from_str(&entry->name, "(null)"); entry->zero_bits = true; g->builtin_types.entry_null = entry; @@ -6500,7 +6500,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " ComptimeFloat: void,\n" " ComptimeInt: void,\n" " UndefinedLiteral: void,\n" - " NullLiteral: void,\n" + " Null: void,\n" " Nullable: Nullable,\n" " ErrorUnion: ErrorUnion,\n" " ErrorSet: ErrorSet,\n" @@ -7075,7 +7075,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -7260,7 +7260,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: zig_unreachable(); @@ -7413,7 +7413,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdArray: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: diff --git a/src/ir.cpp b/src/ir.cpp index 2819ef5b0e..2fbc72309a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7960,7 +7960,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit conversion from null literal to maybe type if (expected_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdNullLit) + actual_type->id == TypeTableEntryIdNull) { return ImplicitCastMatchResultYes; } @@ -8190,7 +8190,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit); + bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNull); bool convert_to_const_slice = false; for (size_t i = 1; i < instruction_count; i += 1) { IrInstruction *cur_inst = instructions[i]; @@ -8469,12 +8469,12 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (prev_type->id == TypeTableEntryIdNullLit) { + if (prev_type->id == TypeTableEntryIdNull) { prev_inst = cur_inst; continue; } - if (cur_type->id == TypeTableEntryIdNullLit) { + if (cur_type->id == TypeTableEntryIdNull) { any_are_null = true; continue; } @@ -8677,7 +8677,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of number literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) { + } else if (prev_inst->value.type->id == TypeTableEntryIdNull) { ir_add_error_node(ira, source_node, buf_sprintf("unable to make error union out of null literal")); return ira->codegen->builtin_types.entry_invalid; @@ -8685,7 +8685,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); } } - } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { + } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNull) { if (prev_inst->value.type->id == TypeTableEntryIdComptimeInt || prev_inst->value.type->id == TypeTableEntryIdComptimeFloat) { @@ -10004,7 +10004,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from null literal to maybe type if (wanted_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdNullLit) + actual_type->id == TypeTableEntryIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); } @@ -10061,7 +10061,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || - actual_type->id == TypeTableEntryIdNullLit || + actual_type->id == TypeTableEntryIdNull || actual_type->id == TypeTableEntryIdComptimeInt || actual_type->id == TypeTableEntryIdComptimeFloat) { @@ -10627,19 +10627,19 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); if (is_equality_cmp && - ((op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdMaybe) || - (op2->value.type->id == TypeTableEntryIdNullLit && op1->value.type->id == TypeTableEntryIdMaybe) || - (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit))) + ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdMaybe) || + (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdMaybe) || + (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull))) { - if (op1->value.type->id == TypeTableEntryIdNullLit && op2->value.type->id == TypeTableEntryIdNullLit) { + if (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull) { ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq); return ira->codegen->builtin_types.entry_bool; } IrInstruction *maybe_op; - if (op1->value.type->id == TypeTableEntryIdNullLit) { + if (op1->value.type->id == TypeTableEntryIdNull) { maybe_op = op2; - } else if (op2->value.type->id == TypeTableEntryIdNullLit) { + } else if (op2->value.type->id == TypeTableEntryIdNull) { maybe_op = op1; } else { zig_unreachable(); @@ -10796,7 +10796,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: @@ -11645,7 +11645,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdBlock: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdOpaque: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: @@ -11913,7 +11913,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -11937,7 +11937,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -12901,7 +12901,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -13152,7 +13152,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdComptimeInt || - resolved_type->id == TypeTableEntryIdNullLit || + resolved_type->id == TypeTableEntryIdNull || resolved_type->id == TypeTableEntryIdUndefLit) { ir_add_error_node(ira, phi_instruction->base.source_node, @@ -14216,7 +14216,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -14480,7 +14480,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -14588,7 +14588,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -14657,7 +14657,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: @@ -14713,7 +14713,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn ir_build_test_nonnull_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; - } else if (type_entry->id == TypeTableEntryIdNullLit) { + } else if (type_entry->id == TypeTableEntryIdNull) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = false; return ira->codegen->builtin_types.entry_bool; @@ -15100,7 +15100,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -15621,7 +15621,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -16283,7 +16283,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -17879,7 +17879,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -18613,7 +18613,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdVoid: @@ -18680,7 +18680,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdVoid: @@ -18761,7 +18761,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); return ira->codegen->builtin_types.entry_invalid; @@ -18787,7 +18787,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: + case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->builtin_types.entry_invalid; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 1821e29a20..ed14243b39 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -504,7 +504,7 @@ test "@typeId" { assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); - assert(@typeId(@typeOf(null)) == Tid.NullLiteral); + assert(@typeId(@typeOf(null)) == Tid.Null); assert(@typeId(?i32) == Tid.Nullable); assert(@typeId(error!i32) == Tid.ErrorUnion); assert(@typeId(error) == Tid.ErrorSet); -- cgit v1.2.3 From a8146ade2a57bea12ea2d16bd273f03578e5d559 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Tue, 5 Jun 2018 11:54:11 +0200 Subject: Renamed UndefinedLiteral to Undefined --- doc/langref.html.in | 8 ++++---- src/all_types.hpp | 2 +- src/analyze.cpp | 44 ++++++++++++++++++++++---------------------- src/codegen.cpp | 14 +++++++------- src/ir.cpp | 44 ++++++++++++++++++++++---------------------- test/cases/misc.zig | 2 +- 6 files changed, 57 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 70f11c0e2b..4359cadb58 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4895,7 +4895,7 @@ pub const TypeId = enum { Struct, ComptimeFloat, ComptimeInt, - UndefinedLiteral, + Undefined, Null, Nullable, ErrorUnion, @@ -4929,7 +4929,7 @@ pub const TypeInfo = union(TypeId) { Struct: Struct, ComptimeFloat: void, ComptimeInt: void, - UndefinedLiteral: void, + Undefined: void, Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, @@ -5687,7 +5687,7 @@ pub const TypeId = enum { Struct, ComptimeFloat, ComptimeInt, - UndefinedLiteral, + Undefined, Null, Nullable, ErrorUnion, @@ -5715,7 +5715,7 @@ pub const TypeInfo = union(TypeId) { Struct: Struct, ComptimeFloat: void, ComptimeInt: void, - UndefinedLiteral: void, + Undefined: void, Null: void, Nullable: Nullable, ErrorUnion: ErrorUnion, diff --git a/src/all_types.hpp b/src/all_types.hpp index 6b30a1155d..3b2ea02b71 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1161,7 +1161,7 @@ enum TypeTableEntryId { TypeTableEntryIdStruct, TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, - TypeTableEntryIdUndefLit, + TypeTableEntryIdUndefined, TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, diff --git a/src/analyze.cpp b/src/analyze.cpp index a605cb3a7f..8008bea68d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -234,7 +234,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -270,7 +270,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -1335,7 +1335,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -1376,7 +1376,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -1511,7 +1511,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -1599,7 +1599,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdInvalid: zig_unreachable(); - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: @@ -3337,7 +3337,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -3732,7 +3732,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -3781,7 +3781,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -4285,7 +4285,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -4672,7 +4672,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdPromise: // TODO better hashing algorithm return 223048345; - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: return 162837799; case TypeTableEntryIdNull: return 844854567; @@ -4756,7 +4756,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case TypeTableEntryIdFloat: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: @@ -4821,7 +4821,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdFloat: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBoundFn: @@ -4932,7 +4932,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { zig_unreachable(); case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: @@ -5410,7 +5410,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return false; } return true; - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: zig_panic("TODO"); case TypeTableEntryIdNull: zig_panic("TODO"); @@ -5651,7 +5651,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "null"); return; } - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: { buf_appendf(buf, "undefined"); return; @@ -5763,7 +5763,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorSet: @@ -5809,7 +5809,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdPromise: @@ -5931,7 +5931,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdStruct, TypeTableEntryIdComptimeFloat, TypeTableEntryIdComptimeInt, - TypeTableEntryIdUndefLit, + TypeTableEntryIdUndefined, TypeTableEntryIdNull, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, @@ -5984,7 +5984,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 9; case TypeTableEntryIdComptimeInt: return 10; - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: return 11; case TypeTableEntryIdNull: return 12; @@ -6042,8 +6042,8 @@ const char *type_id_name(TypeTableEntryId id) { return "ComptimeFloat"; case TypeTableEntryIdComptimeInt: return "ComptimeInt"; - case TypeTableEntryIdUndefLit: - return "UndefinedLiteral"; + case TypeTableEntryIdUndefined: + return "Undefined"; case TypeTableEntryIdNull: return "Null"; case TypeTableEntryIdMaybe: diff --git a/src/codegen.cpp b/src/codegen.cpp index 3177a2491f..a977c34daf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4918,7 +4918,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: @@ -5364,7 +5364,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -6026,7 +6026,7 @@ static void define_builtin_types(CodeGen *g) { g->primitive_type_table.put(&entry->name, entry); } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefined); buf_init_from_str(&entry->name, "(undefined)"); entry->zero_bits = true; g->builtin_types.entry_undef = entry; @@ -6499,7 +6499,7 @@ static void define_builtin_compile_vars(CodeGen *g) { " Struct: Struct,\n" " ComptimeFloat: void,\n" " ComptimeInt: void,\n" - " UndefinedLiteral: void,\n" + " Undefined: void,\n" " Null: void,\n" " Nullable: Nullable,\n" " ErrorUnion: ErrorUnion,\n" @@ -7074,7 +7074,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdMetaType: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -7259,7 +7259,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdBlock: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: @@ -7412,7 +7412,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: case TypeTableEntryIdArray: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: diff --git a/src/ir.cpp b/src/ir.cpp index 2fbc72309a..9578795fcc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8142,7 +8142,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit undefined literal to anything - if (actual_type->id == TypeTableEntryIdUndefLit) { + if (actual_type->id == TypeTableEntryIdUndefined) { return ImplicitCastMatchResultYes; } @@ -8546,11 +8546,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (cur_type->id == TypeTableEntryIdUndefLit) { + if (cur_type->id == TypeTableEntryIdUndefined) { continue; } - if (prev_type->id == TypeTableEntryIdUndefLit) { + if (prev_type->id == TypeTableEntryIdUndefined) { prev_inst = cur_inst; continue; } @@ -10230,7 +10230,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from undefined to anything - if (actual_type->id == TypeTableEntryIdUndefLit) { + if (actual_type->id == TypeTableEntryIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } @@ -10795,7 +10795,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -11643,7 +11643,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { return VarClassRequiredAny; case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdBlock: case TypeTableEntryIdNull: case TypeTableEntryIdOpaque: @@ -11912,7 +11912,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -11936,7 +11936,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdArray: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -12900,7 +12900,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -13153,7 +13153,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdComptimeInt || resolved_type->id == TypeTableEntryIdNull || - resolved_type->id == TypeTableEntryIdUndefLit) + resolved_type->id == TypeTableEntryIdUndefined) { ir_add_error_node(ira, phi_instruction->base.source_node, buf_sprintf("unable to infer expression type")); @@ -14215,7 +14215,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi zig_unreachable(); // handled above case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -14479,7 +14479,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -14587,7 +14587,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdArgTuple: @@ -14656,7 +14656,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdInvalid: // handled above zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdBlock: case TypeTableEntryIdComptimeFloat: @@ -15099,7 +15099,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdBlock: @@ -15620,7 +15620,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: @@ -16282,7 +16282,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -17878,7 +17878,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -18612,7 +18612,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); @@ -18679,7 +18679,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: case TypeTableEntryIdPromise: zig_unreachable(); @@ -18760,7 +18760,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); @@ -18786,7 +18786,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc case TypeTableEntryIdUnreachable: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefLit: + case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: ir_add_error(ira, dest_type_value, buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index ed14243b39..9450cf5e6e 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -503,7 +503,7 @@ test "@typeId" { assert(@typeId(AStruct) == Tid.Struct); assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); + assert(@typeId(@typeOf(undefined)) == Tid.Undefined); assert(@typeId(@typeOf(null)) == Tid.Null); assert(@typeId(?i32) == Tid.Nullable); assert(@typeId(error!i32) == Tid.ErrorUnion); -- cgit v1.2.3 From 7a0948253636080e5abe59b938761ee7348a7025 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 10:48:53 -0400 Subject: fix crash when evaluating return type has compile error closes #1058 --- src/analyze.cpp | 2 ++ test/compile_errors.zig | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 8008bea68d..b0f0196020 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1018,6 +1018,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } if (fn_type_id->return_type != nullptr) { ensure_complete_type(g, fn_type_id->return_type); + if (type_is_invalid(fn_type_id->return_type)) + return g->builtin_types.entry_invalid; } else { zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e264d57b5e..4bd6e9bc24 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,22 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "error when evaluating return type", + \\const Foo = struct { + \\ map: i32(i32), + \\ + \\ fn init() Foo { + \\ return undefined; + \\ } + \\}; + \\export fn entry() void { + \\ var rule_set = try Foo.init(); + \\} + , + ".tmp_source.zig:2:13: error: invalid cast from type 'type' to 'i32'", + ); + cases.add( "slicing single-item pointer", \\export fn entry(ptr: *i32) void { -- cgit v1.2.3 From 652f4bdf6242462182005f4c7149f13beaaa3259 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 18:03:21 -0400 Subject: disallow unknown-length pointer to opaque This also means that translate-c has to detect when a pointer to opaque is happening, and use `*` instead of `[*]`. See #1059 --- src/analyze.cpp | 1 + src/ir.cpp | 10 +++++----- src/tokenizer.hpp | 2 ++ src/translate_c.cpp | 37 +++++++++++++++++++++++++++++++++---- std/c/index.zig | 20 ++++++++++---------- std/heap.zig | 8 ++++---- std/os/darwin.zig | 8 ++++---- std/os/file.zig | 2 +- std/os/index.zig | 4 ++-- std/os/windows/index.zig | 14 +++++++------- std/os/windows/util.zig | 2 +- test/compare_output.zig | 4 ++-- test/compile_errors.zig | 7 +++++++ test/translate_c.zig | 20 ++++++++++---------- 14 files changed, 89 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index b0f0196020..0adb992798 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -384,6 +384,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) { assert(!type_is_invalid(child_type)); + assert(ptr_len == PtrLenSingle || child_type->id != TypeTableEntryIdOpaque); TypeId type_id = {}; TypeTableEntry **parent_pointer = nullptr; diff --git a/src/ir.cpp b/src/ir.cpp index 9578795fcc..5c44e7c0ff 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4620,11 +4620,8 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction * static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePointerType); - // The null check here is for C imports which don't set a token on the AST node. We could potentially - // update that code to create a fake token and then remove this check. - PtrLen ptr_len = (node->data.pointer_type.star_token != nullptr && - (node->data.pointer_type.star_token->id == TokenIdStar || - node->data.pointer_type.star_token->id == TokenIdStarStar)) ? PtrLenSingle : PtrLenUnknown; + PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar || + node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown; bool is_const = node->data.pointer_type.is_const; bool is_volatile = node->data.pointer_type.is_volatile; AstNode *expr_node = node->data.pointer_type.op_expr; @@ -18973,6 +18970,9 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc if (child_type->id == TypeTableEntryIdUnreachable) { ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); return ira->codegen->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) { + ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque")); + return ira->codegen->builtin_types.entry_invalid; } uint32_t align_bytes; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index d659c0a772..d0089909cd 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -170,6 +170,8 @@ struct Token { TokenCharLit char_lit; } data; }; +// work around conflicting name Token which is also found in libclang +typedef Token ZigToken; struct Tokenization { ZigList *tokens; diff --git a/src/translate_c.cpp b/src/translate_c.cpp index db541d34f3..d78bd1fa70 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -276,8 +276,11 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod node); } -static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { +static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) { AstNode *node = trans_create_node(c, NodeTypePointerType); + node->data.pointer_type.star_token = allocate(1); + node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket; + node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_volatile = is_volatile; node->data.pointer_type.op_expr = child_node; @@ -731,6 +734,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) { } } +static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) { + switch (ty->getTypeClass()) { + case Type::Builtin: { + const BuiltinType *builtin_ty = static_cast(ty); + return builtin_ty->getKind() == BuiltinType::Void; + } + case Type::Record: { + const RecordType *record_ty = static_cast(ty); + return record_ty->getDecl()->getDefinition() == nullptr; + } + case Type::Elaborated: { + const ElaboratedType *elaborated_ty = static_cast(ty); + return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc); + } + case Type::Typedef: { + const TypedefType *typedef_ty = static_cast(ty); + const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc); + } + default: + return false; + } +} + static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { switch (ty->getTypeClass()) { case Type::Builtin: @@ -855,8 +882,10 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); } + PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown; + AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_node); + child_qt.isVolatileQualified(), child_node, ptr_len); return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); } case Type::Typedef: @@ -1041,7 +1070,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return nullptr; } AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_type_node); + child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown); return pointer_node; } case Type::BlockPointer: @@ -4448,7 +4477,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } else if (first_tok->id == CTokIdAsterisk) { *tok_i += 1; - node = trans_create_node_ptr_type(c, false, false, node); + node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown); } else { return node; } diff --git a/std/c/index.zig b/std/c/index.zig index ade37f36c1..7de8634d07 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -20,11 +20,11 @@ pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: [*]c_void, nbyte: usize) isize; +pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: [*]const c_void, nbyte: usize) isize; -pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void; -pub extern "c" fn munmap(addr: [*]c_void, len: usize) c_int; +pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; +pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; +pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; pub extern "c" fn unlink(path: [*]const u8) c_int; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; @@ -48,15 +48,15 @@ pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; pub extern "c" fn rmdir(path: [*]const u8) c_int; -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void; -pub extern "c" fn malloc(usize) ?[*]c_void; -pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void; -pub extern "c" fn free([*]c_void) void; -pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; +pub extern "c" fn malloc(usize) ?*c_void; +pub extern "c" fn realloc(*c_void, usize) ?*c_void; +pub extern "c" fn free(*c_void) void; +pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: [*]c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; diff --git a/std/heap.zig b/std/heap.zig index 4444a2307a..5d430bc761 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -22,7 +22,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 { } fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { - const old_ptr = @ptrCast([*]c_void, old_mem.ptr); + const old_ptr = @ptrCast(*c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { return @ptrCast([*]u8, buf)[0..new_size]; } else if (new_size <= old_mem.len) { @@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![ } fn cFree(self: *Allocator, old_mem: []u8) void { - const old_ptr = @ptrCast([*]c_void, old_mem.ptr); + const old_ptr = @ptrCast(*c_void, old_mem.ptr); c.free(old_ptr); } @@ -140,7 +140,7 @@ pub const DirectAllocator = struct { const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_record_addr = old_adjusted_addr + old_mem.len; const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; - const old_ptr = @intToPtr([*]c_void, root_addr); + const old_ptr = @intToPtr(*c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; @@ -170,7 +170,7 @@ pub const DirectAllocator = struct { Os.windows => { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const root_addr = @intToPtr(*align(1) usize, record_addr).*; - const ptr = @intToPtr([*]c_void, root_addr); + const ptr = @intToPtr(*c_void, root_addr); _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); }, else => @compileError("Unsupported OS"), diff --git a/std/os/darwin.zig b/std/os/darwin.zig index b8e18561cc..a835959103 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -327,7 +327,7 @@ pub fn raise(sig: i32) usize { } pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast([*]c_void, buf), nbyte)); + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); } pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { @@ -335,17 +335,17 @@ pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { } pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast([*]const c_void, buf), nbyte)); + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); } pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast([*]c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); + const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr([*]c_void, address), length)); + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); } pub fn unlink(path: [*]const u8) usize { diff --git a/std/os/file.zig b/std/os/file.zig index 378782507b..d5af55b5e4 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -334,7 +334,7 @@ pub const File = struct { while (index < buffer.len) { const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, @ptrCast([*]c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { + if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.OPERATION_ABORTED => continue, diff --git a/std/os/index.zig b/std/os/index.zig index 6023929b04..fe5ecc38ba 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2362,7 +2362,7 @@ pub const Thread = struct { }, builtin.Os.windows => struct { handle: windows.HANDLE, - alloc_start: [*]c_void, + alloc_start: *c_void, heap_handle: windows.HANDLE, }, else => @compileError("Unsupported OS"), @@ -2533,7 +2533,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread // align to page stack_end -= stack_end % os.page_size; - assert(c.pthread_attr_setstack(&attr, @intToPtr([*]c_void, stack_addr), stack_end - stack_addr) == 0); + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); switch (err) { diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index c491ae6538..53e12500e7 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -101,17 +101,17 @@ pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void; +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; pub extern "kernel32" stdcallcc fn MoveFileExA( lpExistingFileName: LPCSTR, @@ -127,7 +127,7 @@ pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; pub extern "kernel32" stdcallcc fn ReadFile( in_hFile: HANDLE, - out_lpBuffer: [*]c_void, + out_lpBuffer: *c_void, in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: *DWORD, in_out_lpOverlapped: ?*OVERLAPPED, @@ -150,7 +150,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis pub extern "kernel32" stdcallcc fn WriteFile( in_hFile: HANDLE, - in_lpBuffer: [*]const c_void, + in_lpBuffer: *const c_void, in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?*DWORD, in_out_lpOverlapped: ?*OVERLAPPED, diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 5a40567310..7170346108 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast([*]const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, diff --git a/test/compare_output.zig b/test/compare_output.zig index 8d5dc68d45..eec077ef85 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -284,7 +284,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addC("expose function pointer to C land", \\const c = @cImport(@cInclude("stdlib.h")); \\ - \\export fn compare_fn(a: ?[*]const c_void, b: ?[*]const c_void) c_int { + \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int { \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a)); \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b)); \\ if (a_int.* < b_int.*) { @@ -299,7 +299,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\export fn main() c_int { \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(?[*]c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 4bd6e9bc24..9cecb859fa 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,13 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "unknown length pointer to opaque", + \\export const T = [*]@OpaqueType(); + , + ".tmp_source.zig:1:18: error: unknown-length pointer to opaque", + ); + cases.add( "error when evaluating return type", \\const Foo = struct { diff --git a/test/translate_c.zig b/test/translate_c.zig index ac0a98e6cc..3489f9da21 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?[*]c_void, noalias arg1: ?[*]c_void) void; + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; ); cases.add("simple struct", @@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?[*]struct_Foo, x: c_int) ?[*]struct_Foo; + \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?[*]Foo) Foo; + \\pub extern fn fun(a: ?*Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?[*]c_ushort) ?[*]c_void { - \\ return @ptrCast(?[*]c_void, x); + \\pub export fn foo(x: ?[*]c_ushort) ?*c_void { + \\ return @ptrCast(?*c_void, x); \\} ); @@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?[*]c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; -- cgit v1.2.3 From 0ccc18686921dce8e7f2feb95eed83b894ca8df4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 20:24:11 -0400 Subject: disable field access for unknown length pointers See #770 --- src/analyze.cpp | 4 ++-- test/compile_errors.zig | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 0adb992798..15f08aa3fe 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3753,13 +3753,13 @@ static bool is_container(TypeTableEntry *type_entry) { } bool is_container_ref(TypeTableEntry *type_entry) { - return (type_entry->id == TypeTableEntryIdPointer) ? + return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? is_container(type_entry->data.pointer.child_type) : is_container(type_entry); } TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { assert(is_container_ref(type_entry)); - return (type_entry->id == TypeTableEntryIdPointer) ? + return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? type_entry->data.pointer.child_type : type_entry; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9cecb859fa..ab539dd94a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "field access of unknown length pointer", + \\const Foo = extern struct { + \\ a: i32, + \\}; + \\ + \\export fn entry(foo: [*]Foo) void { + \\ foo.a += 1; + \\} + , + ".tmp_source.zig:6:8: error: type '[*]Foo' does not support field access", + ); + cases.add( "unknown length pointer to opaque", \\export const T = [*]@OpaqueType(); -- cgit v1.2.3 From bd13e757e7e36994d2a1fd4595c617d14e22b7c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 5 Jun 2018 22:23:23 -0400 Subject: disable deref syntax for unknown length pointers See #770 --- src/ir.cpp | 12 ++++++++++++ std/special/bootstrap.zig | 2 +- test/compile_errors.zig | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 5c44e7c0ff..a6686aae76 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11132,7 +11132,13 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; + if (type_is_invalid(op1->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *op2 = bin_op_instruction->op2->other; + if (type_is_invalid(op2->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrBinOp op_id = bin_op_instruction->op_id; // look for pointer math @@ -12851,6 +12857,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp if (type_is_invalid(ptr_type)) { return ira->codegen->builtin_types.entry_invalid; } else if (ptr_type->id == TypeTableEntryIdPointer) { + if (ptr_type->data.pointer.ptr_len == PtrLenUnknown) { + ir_add_error_node(ira, un_op_instruction->base.source_node, + buf_sprintf("index syntax required for unknown-length pointer type '%s'", + buf_ptr(&ptr_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } child_type = ptr_type->data.pointer.child_type; } else { ir_add_error_node(ira, un_op_instruction->base.source_node, diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 64eae79ce4..8aefe4751f 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -51,7 +51,7 @@ extern fn WinMainCRTStartup() noreturn { // TODO https://github.com/ziglang/zig/issues/265 fn posixCallMainAndExit() noreturn { - const argc = argc_ptr.*; + const argc = argc_ptr[0]; const argv = @ptrCast([*][*]u8, argc_ptr + 1); const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ab539dd94a..412b2d5fc9 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "dereference unknown length pointer", + \\export fn entry(x: [*]i32) i32 { + \\ return x.*; + \\} + , + ".tmp_source.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'", + ); + cases.add( "field access of unknown length pointer", \\const Foo = extern struct { -- cgit v1.2.3 From d3693dca73dfc726aed32908691437abe614e5cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 6 Jun 2018 00:39:39 -0400 Subject: Pointer Reform: update @typeInfo * add assertion for trying to do @typeInfo on global error set * remove TypeInfo.Slice * add TypeInfo.Pointer.Size with possible values - One - Many - Slice See #770 --- src/analyze.cpp | 2 +- src/codegen.cpp | 11 ++++--- src/ir.cpp | 80 ++++++++++++++++++++++++++++++------------------ std/fmt/index.zig | 31 +++++++++++-------- test/cases/type_info.zig | 33 +++++++++++++++----- 5 files changed, 102 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 15f08aa3fe..93373f6ec2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5981,7 +5981,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 7; case TypeTableEntryIdStruct: if (entry->data.structure.is_slice) - return 25; + return 6; return 8; case TypeTableEntryIdComptimeFloat: return 9; diff --git a/src/codegen.cpp b/src/codegen.cpp index a977c34daf..7f95f335d1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6481,7 +6481,6 @@ static void define_builtin_compile_vars(CodeGen *g) { const TypeTableEntryId id = type_id_at_index(i); buf_appendf(contents, " %s,\n", type_id_name(id)); } - buf_appendf(contents, " Slice,\n"); buf_appendf(contents, "};\n\n"); } { @@ -6494,7 +6493,6 @@ static void define_builtin_compile_vars(CodeGen *g) { " Int: Int,\n" " Float: Float,\n" " Pointer: Pointer,\n" - " Slice: Slice,\n" " Array: Array,\n" " Struct: Struct,\n" " ComptimeFloat: void,\n" @@ -6524,13 +6522,18 @@ static void define_builtin_compile_vars(CodeGen *g) { " };\n" "\n" " pub const Pointer = struct {\n" + " size: Size,\n" " is_const: bool,\n" " is_volatile: bool,\n" " alignment: u32,\n" " child: type,\n" - " };\n" "\n" - " pub const Slice = Pointer;\n" + " pub const Size = enum {\n" + " One,\n" + " Many,\n" + " Slice,\n" + " };\n" + " };\n" "\n" " pub const Array = struct {\n" " len: usize,\n" diff --git a/src/ir.cpp b/src/ir.cpp index a6686aae76..3486e8c047 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16222,8 +16222,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop return true; } -static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) -{ +static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { assert(type_entry != nullptr); assert(!type_is_invalid(type_entry)); @@ -16248,38 +16247,67 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t enum_field_val->data.x_struct.fields = inner_fields; }; - const auto create_ptr_like_type_info = [ira](const char *name, TypeTableEntry *ptr_type_entry) { + const auto create_ptr_like_type_info = [ira](TypeTableEntry *ptr_type_entry) { + TypeTableEntry *attrs_type; + uint32_t size_enum_index; + if (is_slice(ptr_type_entry)) { + attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry; + size_enum_index = 2; + } else if (ptr_type_entry->id == TypeTableEntryIdPointer) { + attrs_type = ptr_type_entry; + size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1; + } else { + zig_unreachable(); + } + + TypeTableEntry *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer"); + ensure_complete_type(ira->codegen, type_info_pointer_type); + assert(!type_is_invalid(type_info_pointer_type)); + ConstExprValue *result = create_const_vals(1); result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, name); + result->type = type_info_pointer_type; - ConstExprValue *fields = create_const_vals(4); + ConstExprValue *fields = create_const_vals(5); result->data.x_struct.fields = fields; - // is_const: bool - ensure_field_index(result->type, "is_const", 0); + // size: Size + ensure_field_index(result->type, "size", 0); + TypeTableEntry *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); + ensure_complete_type(ira->codegen, type_info_pointer_size_type); + assert(!type_is_invalid(type_info_pointer_size_type)); fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_bool; - fields[0].data.x_bool = ptr_type_entry->data.pointer.is_const; - // is_volatile: bool - ensure_field_index(result->type, "is_volatile", 1); + fields[0].type = type_info_pointer_size_type; + bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index); + + // is_const: bool + ensure_field_index(result->type, "is_const", 1); fields[1].special = ConstValSpecialStatic; fields[1].type = ira->codegen->builtin_types.entry_bool; - fields[1].data.x_bool = ptr_type_entry->data.pointer.is_volatile; - // alignment: u32 - ensure_field_index(result->type, "alignment", 2); + fields[1].data.x_bool = attrs_type->data.pointer.is_const; + // is_volatile: bool + ensure_field_index(result->type, "is_volatile", 2); fields[2].special = ConstValSpecialStatic; - fields[2].type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields[2].data.x_bigint, ptr_type_entry->data.pointer.alignment); - // child: type - ensure_field_index(result->type, "child", 3); + fields[2].type = ira->codegen->builtin_types.entry_bool; + fields[2].data.x_bool = attrs_type->data.pointer.is_volatile; + // alignment: u32 + ensure_field_index(result->type, "alignment", 3); fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_type; - fields[3].data.x_type = ptr_type_entry->data.pointer.child_type; + fields[3].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment); + // child: type + ensure_field_index(result->type, "child", 4); + fields[4].special = ConstValSpecialStatic; + fields[4].type = ira->codegen->builtin_types.entry_type; + fields[4].data.x_type = attrs_type->data.pointer.child_type; return result; }; + if (type_entry == ira->codegen->builtin_types.entry_global_error_set) { + zig_panic("TODO implement @typeInfo for global error set"); + } + ConstExprValue *result = nullptr; switch (type_entry->id) { @@ -16348,7 +16376,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t } case TypeTableEntryIdPointer: { - result = create_ptr_like_type_info("Pointer", type_entry); + result = create_ptr_like_type_info(type_entry); break; } case TypeTableEntryIdArray: @@ -16621,15 +16649,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t case TypeTableEntryIdStruct: { if (type_entry->data.structure.is_slice) { - Buf ptr_field_name = BUF_INIT; - buf_init_from_str(&ptr_field_name, "ptr"); - TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry; - ensure_complete_type(ira->codegen, ptr_type); - if (type_is_invalid(ptr_type)) - return nullptr; - buf_deinit(&ptr_field_name); - - result = create_ptr_like_type_info("Slice", ptr_type); + result = create_ptr_like_type_info(type_entry); break; } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 047a154bb8..bbf48df0cf 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -97,7 +97,11 @@ pub fn formatType( output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { const T = @typeOf(value); - switch (@typeId(T)) { + if (T == error) { + try output(context, "error."); + return output(context, @errorName(value)); + } + switch (@typeInfo(T)) { builtin.TypeId.Int, builtin.TypeId.Float => { return formatValue(value, fmt, context, Errors, output); }, @@ -125,12 +129,13 @@ pub fn formatType( try output(context, "error."); return output(context, @errorName(value)); }, - builtin.TypeId.Pointer => { - switch (@typeId(T.Child)) { - builtin.TypeId.Array => { - if (T.Child.Child == u8) { + builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { + builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { + builtin.TypeId.Array => |info| { + if (info.child == u8) { return formatText(value, fmt, context, Errors, output); } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); }, builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { const has_cust_fmt = comptime cf: { @@ -154,14 +159,16 @@ pub fn formatType( return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); }, else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), - } - }, - else => if (@canImplicitCast([]const u8, value)) { - const casted_value = ([]const u8)(value); - return output(context, casted_value); - } else { - @compileError("Unable to format type '" ++ @typeName(T) ++ "'"); + }, + builtin.TypeInfo.Pointer.Size.Many => { + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, + builtin.TypeInfo.Pointer.Size.Slice => { + const casted_value = ([]const u8)(value); + return output(context, casted_value); + }, }, + else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 921ff785a7..b452c8e9f6 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -39,12 +39,28 @@ test "type info: pointer type info" { fn testPointer() void { const u32_ptr_info = @typeInfo(*u32); assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); assert(u32_ptr_info.Pointer.is_const == false); assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == 4); + assert(u32_ptr_info.Pointer.alignment == @alignOf(u32)); assert(u32_ptr_info.Pointer.child == u32); } +test "type info: unknown length pointer type info" { + testUnknownLenPtr(); + comptime testUnknownLenPtr(); +} + +fn testUnknownLenPtr() void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + assert(TypeId(u32_ptr_info) == TypeId.Pointer); + assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + assert(u32_ptr_info.Pointer.is_const == true); + assert(u32_ptr_info.Pointer.is_volatile == true); + assert(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + assert(u32_ptr_info.Pointer.child == f64); +} + test "type info: slice type info" { testSlice(); comptime testSlice(); @@ -52,11 +68,12 @@ test "type info: slice type info" { fn testSlice() void { const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Slice); - assert(u32_slice_info.Slice.is_const == false); - assert(u32_slice_info.Slice.is_volatile == false); - assert(u32_slice_info.Slice.alignment == 4); - assert(u32_slice_info.Slice.child == u32); + assert(TypeId(u32_slice_info) == TypeId.Pointer); + assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); + assert(u32_slice_info.Pointer.is_const == false); + assert(u32_slice_info.Pointer.is_volatile == false); + assert(u32_slice_info.Pointer.alignment == 4); + assert(u32_slice_info.Pointer.child == u32); } test "type info: array type info" { @@ -149,11 +166,11 @@ fn testUnion() void { assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(typeinfo_info.Union.tag_type == TypeId); - assert(typeinfo_info.Union.fields.len == 26); + assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 21); + assert(typeinfo_info.Union.defs.len == 20); const TestNoTagUnion = union { Foo: void, -- cgit v1.2.3 From 31aefa6a2179dfae752020195fb193c6333bae7e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 17:26:41 -0400 Subject: fix structs that contain types which require comptime Now, if a struct has any fields which require comptime, such as `type`, then the struct is marked as requiring comptime as well. Same goes for unions. This means that a function will implicitly be called at comptime if the return type is a struct which contains a field of type `type`. closes #586 --- src/all_types.hpp | 8 ++++ src/analyze.cpp | 23 +++++++++- src/ir.cpp | 112 ++++++++++++++---------------------------------- test/cases/eval.zig | 13 ++++++ test/compile_errors.zig | 2 +- 5 files changed, 77 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 3b2ea02b71..b193fe8ae8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1037,6 +1037,10 @@ struct TypeTableEntryStruct { // whether we've finished resolving it bool complete; + // whether any of the fields require comptime + // the value is not valid until zero_bits_known == true + bool requires_comptime; + bool zero_bits_loop_flag; bool zero_bits_known; uint32_t abi_alignment; // also figured out with zero_bits pass @@ -1105,6 +1109,10 @@ struct TypeTableEntryUnion { // whether we've finished resolving it bool complete; + // whether any of the fields require comptime + // the value is not valid until zero_bits_known == true + bool requires_comptime; + bool zero_bits_loop_flag; bool zero_bits_known; uint32_t abi_alignment; // also figured out with zero_bits pass diff --git a/src/analyze.cpp b/src/analyze.cpp index 93373f6ec2..e05fb23237 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2533,6 +2533,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { continue; } + if (type_requires_comptime(field_type)) { + struct_type->data.structure.requires_comptime = true; + } + if (!type_has_bits(field_type)) continue; @@ -2724,6 +2728,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { } union_field->type_entry = field_type; + if (type_requires_comptime(field_type)) { + union_type->data.unionation.requires_comptime = true; + } + + if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, buf_sprintf("non-enum union field assignment")); @@ -4944,17 +4953,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdArgTuple: return true; case TypeTableEntryIdArray: + return type_requires_comptime(type_entry->data.array.child_type); case TypeTableEntryIdStruct: + assert(type_has_zero_bits_known(type_entry)); + return type_entry->data.structure.requires_comptime; case TypeTableEntryIdUnion: + assert(type_has_zero_bits_known(type_entry)); + return type_entry->data.unionation.requires_comptime; case TypeTableEntryIdMaybe: + return type_requires_comptime(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: + return type_requires_comptime(type_entry->data.error_union.payload_type); + case TypeTableEntryIdPointer: + if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { + return false; + } else { + return type_requires_comptime(type_entry->data.pointer.child_type); + } case TypeTableEntryIdEnum: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: - case TypeTableEntryIdPointer: case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: case TypeTableEntryIdPromise: diff --git a/src/ir.cpp b/src/ir.cpp index 3486e8c047..304127b099 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11624,61 +11624,6 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi zig_unreachable(); } -enum VarClassRequired { - VarClassRequiredAny, - VarClassRequiredConst, - VarClassRequiredIllegal, -}; - -static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { - switch (type_entry->id) { - case TypeTableEntryIdInvalid: - zig_unreachable(); - case TypeTableEntryIdUnreachable: - return VarClassRequiredIllegal; - case TypeTableEntryIdBool: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdVoid: - case TypeTableEntryIdErrorSet: - case TypeTableEntryIdFn: - case TypeTableEntryIdPromise: - return VarClassRequiredAny; - case TypeTableEntryIdComptimeFloat: - case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefined: - case TypeTableEntryIdBlock: - case TypeTableEntryIdNull: - case TypeTableEntryIdOpaque: - case TypeTableEntryIdMetaType: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBoundFn: - case TypeTableEntryIdArgTuple: - return VarClassRequiredConst; - - case TypeTableEntryIdPointer: - if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) { - return VarClassRequiredAny; - } else { - return get_var_class_required(type_entry->data.pointer.child_type); - } - case TypeTableEntryIdArray: - return get_var_class_required(type_entry->data.array.child_type); - case TypeTableEntryIdMaybe: - return get_var_class_required(type_entry->data.maybe.child_type); - case TypeTableEntryIdErrorUnion: - return get_var_class_required(type_entry->data.error_union.payload_type); - - case TypeTableEntryIdStruct: - case TypeTableEntryIdEnum: - case TypeTableEntryIdUnion: - // TODO check the fields of these things and make sure that they don't recursively - // contain any of the other variable classes - return VarClassRequiredAny; - } - zig_unreachable(); -} - static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { VariableTableEntry *var = decl_var_instruction->var; @@ -11713,36 +11658,41 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; } else { - switch (get_var_class_required(result_type)) { - case VarClassRequiredIllegal: + type_ensure_zero_bits_known(ira->codegen, result_type); + if (type_is_invalid(result_type)) { + result_type = ira->codegen->builtin_types.entry_invalid; + } + } + + if (!type_is_invalid(result_type)) { + if (result_type->id == TypeTableEntryIdUnreachable || + result_type->id == TypeTableEntryIdOpaque) + { + ir_add_error_node(ira, source_node, + buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); + result_type = ira->codegen->builtin_types.entry_invalid; + } else if (type_requires_comptime(result_type)) { + var_class_requires_const = true; + if (!var->src_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, - buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); + buf_sprintf("variable of type '%s' must be const or comptime", + buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; - break; - case VarClassRequiredConst: + } + } else { + if (casted_init_value->value.special == ConstValSpecialStatic && + casted_init_value->value.type->id == TypeTableEntryIdFn && + casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) + { var_class_requires_const = true; if (!var->src_is_const && !is_comptime_var) { - ir_add_error_node(ira, source_node, - buf_sprintf("variable of type '%s' must be const or comptime", - buf_ptr(&result_type->name))); + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("functions marked inline must be stored in const or comptime var")); + AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; + add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); result_type = ira->codegen->builtin_types.entry_invalid; } - break; - case VarClassRequiredAny: - if (casted_init_value->value.special == ConstValSpecialStatic && - casted_init_value->value.type->id == TypeTableEntryIdFn && - casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) - { - var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("functions marked inline must be stored in const or comptime var")); - AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node; - add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); - result_type = ira->codegen->builtin_types.entry_invalid; - } - } - break; + } } } @@ -12623,6 +12573,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal inst_fn_type_id.return_type = specified_return_type; } + type_ensure_zero_bits_known(ira->codegen, specified_return_type); + if (type_is_invalid(specified_return_type)) + return ira->codegen->builtin_types.entry_invalid; + if (type_requires_comptime(specified_return_type)) { // Throw out our work and call the function as if it were comptime. return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 461408afea..9612466a86 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -610,3 +610,16 @@ test "slice of type" { } } } + +const Wrapper = struct { + T: type, +}; + +fn wrap(comptime T: type) Wrapper { + return Wrapper{ .T = T }; +} + +test "function which returns struct with type field causes implicit comptime" { + const ty = wrap(i32).T; + assert(ty == i32); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c995cd679e..102c4e428d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3329,7 +3329,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", - ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", + ".tmp_source.zig:12:4: error: variable of type 'Opaque' not allowed", ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", -- cgit v1.2.3 From 688ff2830d82ea36a9f022ecb7cf4c2bf2e4c586 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 19:10:45 -0400 Subject: langref: automatic update of builtin.zig now the docs can't get out of date for this See #367 --- doc/docgen.zig | 18 +++ doc/langref.html.in | 423 +--------------------------------------------------- src/codegen.cpp | 21 ++- src/codegen.hpp | 2 + src/main.cpp | 15 ++ 5 files changed, 51 insertions(+), 428 deletions(-) (limited to 'src') diff --git a/doc/docgen.zig b/doc/docgen.zig index fed4bb8eba..ed0e1be273 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -300,6 +300,7 @@ const Link = struct { const Node = union(enum) { Content: []const u8, Nav, + Builtin, HeaderOpen: HeaderOpen, SeeAlso: []const SeeAlsoItem, Code: Code, @@ -356,6 +357,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { _ = try eatToken(tokenizer, Token.Id.BracketClose); try nodes.append(Node.Nav); + } else if (mem.eql(u8, tag_name, "builtin")) { + _ = try eatToken(tokenizer, Token.Id.BracketClose); + try nodes.append(Node.Builtin); } else if (mem.eql(u8, tag_name, "header_open")) { _ = try eatToken(tokenizer, Token.Id.Separator); const content_token = try eatToken(tokenizer, Token.Id.TagContent); @@ -690,6 +694,9 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; + + const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe)); + for (toc.nodes) |node| { switch (node) { Node.Content => |data| { @@ -704,6 +711,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var Node.Nav => { try out.write(toc.toc); }, + Node.Builtin => { + try out.print("
    {}
    ", builtin_code); + }, Node.HeaderOpen => |info| { try out.print("{}\n", info.n, info.url, info.name, info.n); }, @@ -1060,3 +1070,11 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex } return result; } + +fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 { + const result = try exec(allocator, []const []const u8{ + zig_exe, + "builtin", + }); + return result.stdout; +} diff --git a/doc/langref.html.in b/doc/langref.html.in index 4359cadb58..adb5470d98 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5474,425 +5474,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';

    Example of what is imported with @import("builtin"):

    - {#code_begin|syntax#} -pub const StackTrace = struct { - index: usize, - instruction_addresses: []usize, -}; - -pub const Os = enum { - freestanding, - ananas, - cloudabi, - dragonfly, - freebsd, - fuchsia, - ios, - kfreebsd, - linux, - lv2, - macosx, - netbsd, - openbsd, - solaris, - windows, - haiku, - minix, - rtems, - nacl, - cnk, - aix, - cuda, - nvcl, - amdhsa, - ps4, - elfiamcu, - tvos, - watchos, - mesa3d, - contiki, - amdpal, - zen, -}; - -pub const Arch = enum { - armv8_3a, - armv8_2a, - armv8_1a, - armv8, - armv8r, - armv8m_baseline, - armv8m_mainline, - armv7, - armv7em, - armv7m, - armv7s, - armv7k, - armv7ve, - armv6, - armv6m, - armv6k, - armv6t2, - armv5, - armv5te, - armv4t, - armebv8_3a, - armebv8_2a, - armebv8_1a, - armebv8, - armebv8r, - armebv8m_baseline, - armebv8m_mainline, - armebv7, - armebv7em, - armebv7m, - armebv7s, - armebv7k, - armebv7ve, - armebv6, - armebv6m, - armebv6k, - armebv6t2, - armebv5, - armebv5te, - armebv4t, - aarch64, - aarch64_be, - arc, - avr, - bpfel, - bpfeb, - hexagon, - mips, - mipsel, - mips64, - mips64el, - msp430, - nios2, - powerpc, - powerpc64, - powerpc64le, - r600, - amdgcn, - riscv32, - riscv64, - sparc, - sparcv9, - sparcel, - s390x, - tce, - tcele, - thumb, - thumbeb, - i386, - x86_64, - xcore, - nvptx, - nvptx64, - le32, - le64, - amdil, - amdil64, - hsail, - hsail64, - spir, - spir64, - kalimbav3, - kalimbav4, - kalimbav5, - shave, - lanai, - wasm32, - wasm64, - renderscript32, - renderscript64, -}; - -pub const Environ = enum { - unknown, - gnu, - gnuabin32, - gnuabi64, - gnueabi, - gnueabihf, - gnux32, - code16, - eabi, - eabihf, - android, - musl, - musleabi, - musleabihf, - msvc, - itanium, - cygnus, - amdopencl, - coreclr, - opencl, - simulator, -}; - -pub const ObjectFormat = enum { - unknown, - coff, - elf, - macho, - wasm, -}; - -pub const GlobalLinkage = enum { - Internal, - Strong, - Weak, - LinkOnce, -}; - -pub const AtomicOrder = enum { - Unordered, - Monotonic, - Acquire, - Release, - AcqRel, - SeqCst, -}; - -pub const AtomicRmwOp = enum { - Xchg, - Add, - Sub, - And, - Nand, - Or, - Xor, - Max, - Min, -}; - -pub const Mode = enum { - Debug, - ReleaseSafe, - ReleaseFast, - ReleaseSmall, -}; - -pub const TypeId = enum { - Type, - Void, - Bool, - NoReturn, - Int, - Float, - Pointer, - Array, - Struct, - ComptimeFloat, - ComptimeInt, - Undefined, - Null, - Nullable, - ErrorUnion, - ErrorSet, - Enum, - Union, - Fn, - Namespace, - Block, - BoundFn, - ArgTuple, - Opaque, - Promise, -}; - -pub const TypeInfo = union(TypeId) { - Type: void, - Void: void, - Bool: void, - NoReturn: void, - Int: Int, - Float: Float, - Pointer: Pointer, - Array: Array, - Struct: Struct, - ComptimeFloat: void, - ComptimeInt: void, - Undefined: void, - Null: void, - Nullable: Nullable, - ErrorUnion: ErrorUnion, - ErrorSet: ErrorSet, - Enum: Enum, - Union: Union, - Fn: Fn, - Namespace: void, - Block: void, - BoundFn: Fn, - ArgTuple: void, - Opaque: void, - Promise: Promise, - - - pub const Int = struct { - is_signed: bool, - bits: u8, - }; - - pub const Float = struct { - bits: u8, - }; - - pub const Pointer = struct { - is_const: bool, - is_volatile: bool, - alignment: u32, - child: type, - }; - - pub const Array = struct { - len: usize, - child: type, - }; - - pub const ContainerLayout = enum { - Auto, - Extern, - Packed, - }; - - pub const StructField = struct { - name: []const u8, - offset: ?usize, - field_type: type, - }; - - pub const Struct = struct { - layout: ContainerLayout, - fields: []StructField, - defs: []Definition, - }; - - pub const Nullable = struct { - child: type, - }; - - pub const ErrorUnion = struct { - error_set: type, - payload: type, - }; - - pub const Error = struct { - name: []const u8, - value: usize, - }; - - pub const ErrorSet = struct { - errors: []Error, - }; - - pub const EnumField = struct { - name: []const u8, - value: usize, - }; - - pub const Enum = struct { - layout: ContainerLayout, - tag_type: type, - fields: []EnumField, - defs: []Definition, - }; - - pub const UnionField = struct { - name: []const u8, - enum_field: ?EnumField, - field_type: type, - }; - - pub const Union = struct { - layout: ContainerLayout, - tag_type: type, - fields: []UnionField, - defs: []Definition, - }; - - pub const CallingConvention = enum { - Unspecified, - C, - Cold, - Naked, - Stdcall, - Async, - }; - - pub const FnArg = struct { - is_generic: bool, - is_noalias: bool, - arg_type: type, - }; - - pub const Fn = struct { - calling_convention: CallingConvention, - is_generic: bool, - is_var_args: bool, - return_type: type, - async_allocator_type: type, - args: []FnArg, - }; - - pub const Promise = struct { - child: type, - }; - - pub const Definition = struct { - name: []const u8, - is_pub: bool, - data: Data, - - pub const Data = union(enum) { - Type: type, - Var: type, - Fn: FnDef, - - pub const FnDef = struct { - fn_type: type, - inline_type: Inline, - calling_convention: CallingConvention, - is_var_args: bool, - is_extern: bool, - is_export: bool, - lib_name: ?[]const u8, - return_type: type, - arg_names: [][] const u8, - - pub const Inline = enum { - Auto, - Always, - Never, - }; - }; - }; - }; -}; - -pub const FloatMode = enum { - Optimized, - Strict, -}; - -pub const Endian = enum { - Big, - Little, -}; - -pub const endian = Endian.Little; -pub const is_test = true; -pub const os = Os.linux; -pub const arch = Arch.x86_64; -pub const environ = Environ.gnu; -pub const object_format = ObjectFormat.elf; -pub const mode = Mode.Debug; -pub const link_libc = false; -pub const have_error_return_tracing = true; -pub const __zig_test_fn_slice = {}; // overwritten later - {#code_end#} + {#builtin#} {#see_also|Build Mode#} {#header_close#} {#header_open|Root Source File#} @@ -6053,8 +5635,7 @@ pub fn build(b: *Builder) void { b.default_step.dependOn(&exe.step); } {#code_end#} - {#header_close#} - {#header_open|Terminal#} +

    terminal

    $ zig build
     $ ./test
     all your base are belong to us
    diff --git a/src/codegen.cpp b/src/codegen.cpp index 7f95f335d1..fb59ca7569 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6335,13 +6335,7 @@ static const char *build_mode_to_str(BuildMode build_mode) { zig_unreachable(); } -static void define_builtin_compile_vars(CodeGen *g) { - if (g->std_package == nullptr) - return; - - const char *builtin_zig_basename = "builtin.zig"; - Buf *builtin_zig_path = buf_alloc(); - os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); +Buf *codegen_generate_builtin_source(CodeGen *g) { Buf *contents = buf_alloc(); // Modifications to this struct must be coordinated with code that does anything with @@ -6707,6 +6701,19 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); + + return contents; +} + +static void define_builtin_compile_vars(CodeGen *g) { + if (g->std_package == nullptr) + return; + + const char *builtin_zig_basename = "builtin.zig"; + Buf *builtin_zig_path = buf_alloc(); + os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); + + Buf *contents = codegen_generate_builtin_source(g); ensure_cache_dir(g); os_write_file(builtin_zig_path, contents); diff --git a/src/codegen.hpp b/src/codegen.hpp index a7a4b748c4..b5f3374ec4 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -59,5 +59,7 @@ void codegen_add_object(CodeGen *g, Buf *object_path); void codegen_translate_c(CodeGen *g, Buf *path); +Buf *codegen_generate_builtin_source(CodeGen *g); + #endif diff --git a/src/main.cpp b/src/main.cpp index 9c36f9b091..c63a143bff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ static int usage(const char *arg0) { " build-exe [source] create executable from source or object files\n" " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" + " builtin show the source code of that @import(\"builtin\")\n" " run [source] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" " targets list available compilation targets\n" @@ -214,6 +215,7 @@ static Buf *resolve_zig_lib_dir(void) { enum Cmd { CmdInvalid, CmdBuild, + CmdBuiltin, CmdRun, CmdTest, CmdVersion, @@ -664,6 +666,8 @@ int main(int argc, char **argv) { out_type = OutTypeExe; } else if (strcmp(arg, "targets") == 0) { cmd = CmdTargets; + } else if (strcmp(arg, "builtin") == 0) { + cmd = CmdBuiltin; } else { fprintf(stderr, "Unrecognized command: %s\n", arg); return usage(arg0); @@ -681,6 +685,7 @@ int main(int argc, char **argv) { return usage(arg0); } break; + case CmdBuiltin: case CmdVersion: case CmdZen: case CmdTargets: @@ -727,6 +732,16 @@ int main(int argc, char **argv) { } switch (cmd) { + case CmdBuiltin: { + Buf *zig_lib_dir_buf = resolve_zig_lib_dir(); + CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, zig_lib_dir_buf); + Buf *builtin_source = codegen_generate_builtin_source(g); + if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { + fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } case CmdRun: case CmdBuild: case CmdTranslateC: -- cgit v1.2.3 From b65203f5736199bdc8d98d27728be5e92a17d565 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 19:50:25 -0400 Subject: remove @canImplicitCast builtin nobody will miss it --- doc/langref.html.in | 11 +--------- src/all_types.hpp | 9 -------- src/codegen.cpp | 2 -- src/ir.cpp | 60 ----------------------------------------------------- src/ir_print.cpp | 11 ---------- test/cases/misc.zig | 8 ------- 6 files changed, 1 insertion(+), 100 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index adb5470d98..6a1f1c3102 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3845,9 +3845,6 @@ pub fn printValue(self: *OutStream, value: var) !void { return self.printInt(T, value); } else if (@isFloat(T)) { return self.printFloat(T, value); - } else if (@canImplicitCast([]const u8, value)) { - const casted_value = ([]const u8)(value); - return self.write(casted_value); } else { @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); } @@ -4102,12 +4099,6 @@ comptime {

    {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} {#header_close#} - {#header_open|@canImplicitCast#} -
    @canImplicitCast(comptime T: type, value) bool
    -

    - Returns whether a value can be implicitly casted to a given type. -

    - {#header_close#} {#header_open|@clz#}
    @clz(x: T) U

    @@ -6136,7 +6127,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index b193fe8ae8..9d41b86fa0 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1354,7 +1354,6 @@ enum BuiltinFnId { BuiltinFnIdSetRuntimeSafety, BuiltinFnIdSetFloatMode, BuiltinFnIdTypeName, - BuiltinFnIdCanImplicitCast, BuiltinFnIdPanic, BuiltinFnIdPtrCast, BuiltinFnIdBitCast, @@ -2065,7 +2064,6 @@ enum IrInstructionId { IrInstructionIdCheckSwitchProngs, IrInstructionIdCheckStatementIsVoid, IrInstructionIdTypeName, - IrInstructionIdCanImplicitCast, IrInstructionIdDeclRef, IrInstructionIdPanic, IrInstructionIdTagName, @@ -2858,13 +2856,6 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; -struct IrInstructionCanImplicitCast { - IrInstruction base; - - IrInstruction *type_value; - IrInstruction *target_value; -}; - struct IrInstructionDeclRef { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index fb59ca7569..d156a8a178 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4625,7 +4625,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdTypeName: - case IrInstructionIdCanImplicitCast: case IrInstructionIdDeclRef: case IrInstructionIdSwitchVar: case IrInstructionIdOffsetOf: @@ -6277,7 +6276,6 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); - create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); diff --git a/src/ir.cpp b/src/ir.cpp index 304127b099..3c9adab796 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -585,10 +585,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) { return IrInstructionIdTypeName; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) { - return IrInstructionIdCanImplicitCast; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) { return IrInstructionIdDeclRef; } @@ -2348,20 +2344,6 @@ static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *type_value, IrInstruction *target_value) -{ - IrInstructionCanImplicitCast *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->type_value = type_value; - instruction->target_value = target_value; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(target_value, irb->current_basic_block); - - return &instruction->base; -} - static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) { @@ -4132,21 +4114,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, type_name, lval); } - case BuiltinFnIdCanImplicitCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_instruction) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_instruction) - return arg1_value; - - IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, can_implicit_cast, lval); - } case BuiltinFnIdPanic: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -18405,30 +18372,6 @@ static TypeTableEntry *ir_analyze_instruction_check_statement_is_void(IrAnalyze return ira->codegen->builtin_types.entry_void; } -static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira, - IrInstructionCanImplicitCast *instruction) -{ - IrInstruction *type_value = instruction->type_value->other; - TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); - if (type_is_invalid(type_entry)) - return ira->codegen->builtin_types.entry_invalid; - - IrInstruction *target_value = instruction->target_value->other; - if (type_is_invalid(target_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - - ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type, - target_value); - - if (result == ImplicitCastMatchResultReportedError) { - zig_panic("TODO refactor implicit cast tester to return bool without reporting errors"); - } - - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = (result == ImplicitCastMatchResultYes); - return ira->codegen->builtin_types.entry_bool; -} - static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) { IrInstruction *msg = instruction->msg->other; if (type_is_invalid(msg->value.type)) @@ -19762,8 +19705,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); case IrInstructionIdCheckStatementIsVoid: return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction); - case IrInstructionIdCanImplicitCast: - return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction); case IrInstructionIdDeclRef: return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: @@ -20043,7 +19984,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdIntToEnum: case IrInstructionIdIntToErr: case IrInstructionIdErrToInt: - case IrInstructionIdCanImplicitCast: case IrInstructionIdDeclRef: case IrInstructionIdErrName: case IrInstructionIdTypeName: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 3c177a8bbf..776ef64566 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -913,14 +913,6 @@ static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) { ir_print_other_instruction(irp, instruction->target); } -static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) { - fprintf(irp->f, "@canImplicitCast("); - ir_print_other_instruction(irp, instruction->type_value); - fprintf(irp->f, ","); - ir_print_other_instruction(irp, instruction->target_value); - fprintf(irp->f, ")"); -} - static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { fprintf(irp->f, "&"); if (instruction->align_value != nullptr) { @@ -1524,9 +1516,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTagName: ir_print_tag_name(irp, (IrInstructionTagName *)instruction); break; - case IrInstructionIdCanImplicitCast: - ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); - break; case IrInstructionIdPtrType: ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction); break; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 9450cf5e6e..369d8e5cf3 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -523,14 +523,6 @@ test "@typeId" { } } -test "@canImplicitCast" { - comptime { - assert(@canImplicitCast(i64, i32(3))); - assert(!@canImplicitCast(i32, f32(1.234))); - assert(@canImplicitCast([]const u8, "aoeu")); - } -} - test "@typeName" { const Struct = struct {}; const Union = union { -- cgit v1.2.3 From f0b6dac1f2d37ea9eff0116bec34e9b2be9f3ce7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 7 Jun 2018 22:19:00 -0400 Subject: add implicit casts from `*[N]T` * to `[]T` * to `[*]T` See #770 --- src/all_types.hpp | 1 + src/codegen.cpp | 26 ++++++++++- src/ir.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test/cases/cast.zig | 16 +++++++ 4 files changed, 163 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 9d41b86fa0..c671682363 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -583,6 +583,7 @@ enum CastOp { CastOpNumLitToConcrete, CastOpErrSet, CastOpBitCast, + CastOpPtrOfArrayToSlice, }; struct AstNodeFnCallExpr { diff --git a/src/codegen.cpp b/src/codegen.cpp index d156a8a178..fab2ad659e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2530,7 +2530,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, assert(wanted_type->data.structure.is_slice); assert(actual_type->id == TypeTableEntryIdArray); - TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; @@ -2576,6 +2576,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, return expr_val; case CastOpBitCast: return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpPtrOfArrayToSlice: { + assert(cast_instruction->tmp_ptr); + assert(actual_type->id == TypeTableEntryIdPointer); + TypeTableEntry *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == TypeTableEntryIdArray); + + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + slice_ptr_index, ""); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->type_ref), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false), + }; + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, + slice_len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + array_type->data.array.len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return cast_instruction->tmp_ptr; + } } zig_unreachable(); } @@ -3815,7 +3838,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst } else { end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); } - if (want_runtime_safety) { add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); if (instruction->end) { diff --git a/src/ir.cpp b/src/ir.cpp index 3c9adab796..cc4ffb44a9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -108,6 +108,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align); +static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -8024,6 +8025,33 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } } + // implicit *[N]T to [*]T + if (expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenUnknown && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && + types_match_const_cast_only(ira, expected_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ImplicitCastMatchResultYes; + } + + // implicit *[N]T to []T + if (is_slice(expected_type) && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) + { + TypeTableEntry *slice_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == TypeTableEntryIdPointer); + if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ImplicitCastMatchResultYes; + } + } + // implicit [N]T to ?[]const T if (expected_type->id == TypeTableEntryIdMaybe && is_slice(expected_type->data.maybe.child_type) && @@ -8699,6 +8727,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, zig_unreachable(); case CastOpErrSet: case CastOpBitCast: + case CastOpPtrOfArrayToSlice: zig_panic("TODO"); case CastOpNoop: { @@ -8786,6 +8815,63 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst } } +static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, TypeTableEntry *wanted_type) +{ + assert(value->value.type->id == TypeTableEntryIdPointer); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment); + + if (instr_is_comptime(value)) { + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); + if (pointee->special != ConstValSpecialRuntime) { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.type = wanted_type; + result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; + result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; + result->value.data.x_ptr.data.base_array.array_val = pointee; + result->value.data.x_ptr.data.base_array.elem_index = 0; + result->value.data.x_ptr.data.base_array.is_cstr = false; + return result; + } + } + + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpBitCast); + result->value.type = wanted_type; + return result; +} + +static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *value, TypeTableEntry *wanted_type) +{ + wanted_type = adjust_slice_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment); + + if (instr_is_comptime(value)) { + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); + if (pointee->special != ConstValSpecialRuntime) { + assert(value->value.type->id == TypeTableEntryIdPointer); + TypeTableEntry *array_type = value->value.type->data.pointer.child_type; + assert(is_slice(wanted_type)); + bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; + + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); + result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = + value->value.data.x_ptr.mut; + result->value.type = wanted_type; + return result; + } + } + + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, + wanted_type, value, CastOpPtrOfArrayToSlice); + result->value.type = wanted_type; + ir_add_alloca(ira, result, wanted_type); + return result; +} + static bool is_container(TypeTableEntry *type) { return type->id == TypeTableEntryIdStruct || type->id == TypeTableEntryIdEnum || @@ -9937,6 +10023,35 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // explicit *[N]T to [*]T + if (wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenUnknown && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && + actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + } + + // explicit *[N]T to []T + if (is_slice(wanted_type) && + actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) + { + TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + assert(slice_ptr_type->id == TypeTableEntryIdPointer); + if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type); + } + } + + // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; @@ -13150,6 +13265,13 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count); } +static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align) { + assert(is_slice(slice_type)); + TypeTableEntry *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry, + new_align); + return get_slice_type(g, ptr_type); +} + static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) { assert(ptr_type->id == TypeTableEntryIdPointer); return get_pointer_to_type_extra(g, diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 7358a4ffd8..c3ef24cd78 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -384,3 +384,19 @@ test "const slice widen cast" { assert(@bitCast(u32, bytes) == 0x12121212); } + +test "single-item pointer of array to slice and to unknown length pointer" { + testCastPtrOfArrayToSliceAndPtr(); + comptime testCastPtrOfArrayToSliceAndPtr(); +} + +fn testCastPtrOfArrayToSliceAndPtr() void { + var array = "ao" ++ "eu"; // TODO https://github.com/ziglang/zig/issues/1076 + const x: [*]u8 = &array; + x[0] += 1; + assert(mem.eql(u8, array[0..], "boeu")); + const y: []u8 = &array; + y[0] += 1; + assert(mem.eql(u8, array[0..], "coeu")); +} + -- cgit v1.2.3 From bf3d1c1aab336c4a650bb67dcaca132d4a0f6164 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 8 Jun 2018 09:21:31 +0200 Subject: Allow access of array.len through a pointer --- src/analyze.cpp | 14 ++++++++++++-- src/analyze.hpp | 2 ++ src/ir.cpp | 8 ++++++-- test/cases/array.zig | 10 +++++++++- 4 files changed, 29 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index e05fb23237..84f1473ea1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3761,14 +3761,24 @@ static bool is_container(TypeTableEntry *type_entry) { zig_unreachable(); } +bool is_ref(TypeTableEntry *type_entry) { + return type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle; +} + +bool is_array_ref(TypeTableEntry *type_entry) { + TypeTableEntry *array = is_ref(type_entry) ? + type_entry->data.pointer.child_type : type_entry; + return array->id == TypeTableEntryIdArray; +} + bool is_container_ref(TypeTableEntry *type_entry) { - return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? + return is_ref(type_entry) ? is_container(type_entry->data.pointer.child_type) : is_container(type_entry); } TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { assert(is_container_ref(type_entry)); - return (type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle) ? + return is_ref(type_entry) ? type_entry->data.pointer.child_type : type_entry; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 25bda198d6..88e06b2390 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -70,6 +70,8 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name); TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); +bool is_ref(TypeTableEntry *type_entry); +bool is_array_ref(TypeTableEntry *type_entry); bool is_container_ref(TypeTableEntry *type_entry); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_import(CodeGen *g, ImportTableEntry *import); diff --git a/src/ir.cpp b/src/ir.cpp index cc4ffb44a9..4766bff5e7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13846,10 +13846,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru ir_link_new_instruction(result, &field_ptr_instruction->base); return result->value.type; } - } else if (container_type->id == TypeTableEntryIdArray) { + } else if (is_array_ref(container_type)) { if (buf_eql_str(field_name, "len")) { ConstExprValue *len_val = create_const_vals(1); - init_const_usize(ira->codegen, len_val, container_type->data.array.len); + if (container_type->id == TypeTableEntryIdPointer) { + init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); + } else { + init_const_usize(ira->codegen, len_val, container_type->data.array.len); + } TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize; bool ptr_is_const = true; diff --git a/test/cases/array.zig b/test/cases/array.zig index ef919b27bd..b481261b4f 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -116,6 +116,15 @@ test "array len property" { assert(@typeOf(x).len == 5); } +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + assert(arr.len == 4); + comptime assert(arr.len == 4); + assert(ptr.len == 4); + comptime assert(ptr.len == 4); +} + test "single-item pointer to array indexing and slicing" { testSingleItemPtrArrayIndexSlice(); comptime testSingleItemPtrArrayIndexSlice(); @@ -143,4 +152,3 @@ fn testImplicitCastSingleItemPtr() void { slice[0] += 1; assert(byte == 101); } - -- cgit v1.2.3 From 39fa313ad881d242c4fbb6789bab26fed72449a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 8 Jun 2018 14:57:16 -0400 Subject: disable some implicit casts for unknown length pointers closes #770 --- src/ir.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 4766bff5e7..e62ec71875 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7994,6 +7994,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit &const [N]T to []const T if (is_slice(expected_type) && actual_type->id == TypeTableEntryIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.is_const && actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) { @@ -8012,6 +8013,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit [N]T to &const []const T if (expected_type->id == TypeTableEntryIdPointer && expected_type->data.pointer.is_const && + expected_type->data.pointer.ptr_len == PtrLenSingle && is_slice(expected_type->data.pointer.child_type) && actual_type->id == TypeTableEntryIdArray) { @@ -8074,6 +8076,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, actual_type->id == TypeTableEntryIdComptimeInt) { if (expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenSingle && expected_type->data.pointer.is_const) { if (ir_num_lit_fits_in_other_type(ira, value, expected_type->data.pointer.child_type, false)) { @@ -8121,7 +8124,10 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit enum to &const union which has the enum as the tag type - if (actual_type->id == TypeTableEntryIdEnum && expected_type->id == TypeTableEntryIdPointer) { + if (actual_type->id == TypeTableEntryIdEnum && + expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenSingle) + { TypeTableEntry *union_type = expected_type->data.pointer.child_type; if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) @@ -8141,7 +8147,11 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { + if (expected_type->id == TypeTableEntryIdPointer && + expected_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, expected_type, const_ptr_actual, + source_node).id == ConstCastResultIdOk) + { return ImplicitCastMatchResultYes; } } -- cgit v1.2.3 From 6edd81109d16178f1dc688dacee4b38964b617c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 00:15:23 -0400 Subject: nullable pointers follow const-casting rules any *T -> ?*T cast is allowed implicitly, even when it occurs deep inside the type, and the cast is a no-op at runtime. in order to add this I had to make the comptime value representation of nullable pointers the same as the comptime value representation of normal pointers, so that we don't have to do any recursive transformation of values when doing this kind of cast. --- src/all_types.hpp | 5 +- src/analyze.cpp | 280 ++++++++++++++++++++++++++++------------------------ src/codegen.cpp | 158 +++++++++++++++-------------- src/ir.cpp | 121 +++++++++++++++-------- test/cases/cast.zig | 10 +- 5 files changed, 322 insertions(+), 252 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index c671682363..14a44ea768 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -144,6 +144,9 @@ enum ConstPtrSpecial { // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. // In this case index is the numeric address value. + // We also use this for null pointer. We need the data layout for ConstCastOnly == true + // types to be the same, so all nullables of pointer types use x_ptr + // instead of x_nullable ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. @@ -251,7 +254,7 @@ struct ConstExprValue { bool x_bool; ConstBoundFnValue x_bound_fn; TypeTableEntry *x_type; - ConstExprValue *x_maybe; + ConstExprValue *x_nullable; ConstErrValue x_err_union; ErrorTableEntry *x_err_set; BigInt x_enum_tag; diff --git a/src/analyze.cpp b/src/analyze.cpp index 84f1473ea1..16b2cb0590 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4578,6 +4578,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } +static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { + uint32_t hash_val = 0; + switch (const_val->data.x_ptr.mut) { + case ConstPtrMutRuntimeVar: + hash_val += (uint32_t)3500721036; + break; + case ConstPtrMutComptimeConst: + hash_val += (uint32_t)4214318515; + break; + case ConstPtrMutComptimeVar: + hash_val += (uint32_t)1103195694; + break; + } + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + hash_val += (uint32_t)2478261866; + hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); + return hash_val; + case ConstPtrSpecialBaseArray: + hash_val += (uint32_t)1764906839; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); + hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492; + return hash_val; + case ConstPtrSpecialBaseStruct: + hash_val += (uint32_t)3518317043; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); + return hash_val; + case ConstPtrSpecialHardCodedAddr: + hash_val += (uint32_t)4048518294; + hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); + return hash_val; + case ConstPtrSpecialDiscard: + hash_val += 2010123162; + return hash_val; + case ConstPtrSpecialFunction: + hash_val += (uint32_t)2590901619; + hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); + return hash_val; + } + zig_unreachable(); +} + static uint32_t hash_const_val(ConstExprValue *const_val) { assert(const_val->special == ConstValSpecialStatic); switch (const_val->type->id) { @@ -4646,51 +4692,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); case TypeTableEntryIdPointer: - { - uint32_t hash_val = 0; - switch (const_val->data.x_ptr.mut) { - case ConstPtrMutRuntimeVar: - hash_val += (uint32_t)3500721036; - break; - case ConstPtrMutComptimeConst: - hash_val += (uint32_t)4214318515; - break; - case ConstPtrMutComptimeVar: - hash_val += (uint32_t)1103195694; - break; - } - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - hash_val += (uint32_t)2478261866; - hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee); - return hash_val; - case ConstPtrSpecialBaseArray: - hash_val += (uint32_t)1764906839; - hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); - hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); - hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492; - return hash_val; - case ConstPtrSpecialBaseStruct: - hash_val += (uint32_t)3518317043; - hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); - hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); - return hash_val; - case ConstPtrSpecialHardCodedAddr: - hash_val += (uint32_t)4048518294; - hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); - return hash_val; - case ConstPtrSpecialDiscard: - hash_val += 2010123162; - return hash_val; - case ConstPtrSpecialFunction: - hash_val += (uint32_t)2590901619; - hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); - return hash_val; - } - zig_unreachable(); - } + return hash_const_val_ptr(const_val); case TypeTableEntryIdPromise: // TODO better hashing algorithm return 223048345; @@ -4708,10 +4710,14 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 2709806591; case TypeTableEntryIdMaybe: - if (const_val->data.x_maybe) { - return hash_const_val(const_val->data.x_maybe) * 1992916303; + if (get_codegen_ptr_type(const_val->type) != nullptr) { + return hash_const_val(const_val) * 1992916303; } else { - return 4016830364; + if (const_val->data.x_nullable) { + return hash_const_val(const_val->data.x_nullable) * 1992916303; + } else { + return 4016830364; + } } case TypeTableEntryIdErrorUnion: // TODO better hashing algorithm @@ -4812,9 +4818,11 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { return false; case TypeTableEntryIdMaybe: - if (value->data.x_maybe == nullptr) + if (get_codegen_ptr_type(value->type) != nullptr) + return value->data.x_ptr.mut == ConstPtrMutComptimeVar; + if (value->data.x_nullable == nullptr) return false; - return can_mutate_comptime_var_state(value->data.x_maybe); + return can_mutate_comptime_var_state(value->data.x_nullable); case TypeTableEntryIdErrorUnion: if (value->data.x_err_union.err != nullptr) @@ -5340,6 +5348,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) { return var->is_comptime->value.data.x_bool; } +bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { + if (a->data.x_ptr.special != b->data.x_ptr.special) + return false; + if (a->data.x_ptr.mut != b->data.x_ptr.mut) + return false; + switch (a->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee) + return false; + return true; + case ConstPtrSpecialBaseArray: + if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val && + a->data.x_ptr.data.base_array.array_val->global_refs != + b->data.x_ptr.data.base_array.array_val->global_refs) + { + return false; + } + if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index) + return false; + if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr) + return false; + return true; + case ConstPtrSpecialBaseStruct: + if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val && + a->data.x_ptr.data.base_struct.struct_val->global_refs != + b->data.x_ptr.data.base_struct.struct_val->global_refs) + { + return false; + } + if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) + return false; + return true; + case ConstPtrSpecialHardCodedAddr: + if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) + return false; + return true; + case ConstPtrSpecialDiscard: + return true; + case ConstPtrSpecialFunction: + return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + } + zig_unreachable(); +} + bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { assert(a->type->id == b->type->id); assert(a->special == ConstValSpecialStatic); @@ -5391,49 +5445,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; case TypeTableEntryIdPointer: case TypeTableEntryIdFn: - if (a->data.x_ptr.special != b->data.x_ptr.special) - return false; - if (a->data.x_ptr.mut != b->data.x_ptr.mut) - return false; - switch (a->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee) - return false; - return true; - case ConstPtrSpecialBaseArray: - if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val && - a->data.x_ptr.data.base_array.array_val->global_refs != - b->data.x_ptr.data.base_array.array_val->global_refs) - { - return false; - } - if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index) - return false; - if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr) - return false; - return true; - case ConstPtrSpecialBaseStruct: - if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val && - a->data.x_ptr.data.base_struct.struct_val->global_refs != - b->data.x_ptr.data.base_struct.struct_val->global_refs) - { - return false; - } - if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) - return false; - return true; - case ConstPtrSpecialHardCodedAddr: - if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) - return false; - return true; - case ConstPtrSpecialDiscard: - return true; - case ConstPtrSpecialFunction: - return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; - } - zig_unreachable(); + return const_values_equal_ptr(a, b); case TypeTableEntryIdArray: zig_panic("TODO"); case TypeTableEntryIdStruct: @@ -5449,10 +5461,12 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdNull: zig_panic("TODO"); case TypeTableEntryIdMaybe: - if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) { - return (a->data.x_maybe == nullptr && b->data.x_maybe == nullptr); + if (get_codegen_ptr_type(a->type) != nullptr) + return const_values_equal_ptr(a, b); + if (a->data.x_nullable == nullptr || b->data.x_nullable == nullptr) { + return (a->data.x_nullable == nullptr && b->data.x_nullable == nullptr); } else { - return const_values_equal(a->data.x_maybe, b->data.x_maybe); + return const_values_equal(a->data.x_nullable, b->data.x_nullable); } case TypeTableEntryIdErrorUnion: zig_panic("TODO"); @@ -5525,6 +5539,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue * } } +void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) { + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + case ConstPtrSpecialBaseStruct: + buf_appendf(buf, "*"); + render_const_value(g, buf, const_ptr_pointee(g, const_val)); + return; + case ConstPtrSpecialBaseArray: + if (const_val->data.x_ptr.data.base_array.is_cstr) { + buf_appendf(buf, "*(c str lit)"); + return; + } else { + buf_appendf(buf, "*"); + render_const_value(g, buf, const_ptr_pointee(g, const_val)); + return; + } + case ConstPtrSpecialHardCodedAddr: + buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), + const_val->data.x_ptr.data.hard_coded_addr.addr); + return; + case ConstPtrSpecialDiscard: + buf_append_str(buf, "*_"); + return; + case ConstPtrSpecialFunction: + { + FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; + buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); + return; + } + } + zig_unreachable(); +} + void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -5601,38 +5650,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; } case TypeTableEntryIdPointer: - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - case ConstPtrSpecialBaseStruct: - buf_appendf(buf, "&"); - render_const_value(g, buf, const_ptr_pointee(g, const_val)); - return; - case ConstPtrSpecialBaseArray: - if (const_val->data.x_ptr.data.base_array.is_cstr) { - buf_appendf(buf, "&(c str lit)"); - return; - } else { - buf_appendf(buf, "&"); - render_const_value(g, buf, const_ptr_pointee(g, const_val)); - return; - } - case ConstPtrSpecialHardCodedAddr: - buf_appendf(buf, "(&%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), - const_val->data.x_ptr.data.hard_coded_addr.addr); - return; - case ConstPtrSpecialDiscard: - buf_append_str(buf, "&_"); - return; - case ConstPtrSpecialFunction: - { - FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry; - buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); - return; - } - } - zig_unreachable(); + return render_const_val_ptr(g, buf, const_val, type_entry); case TypeTableEntryIdBlock: { AstNode *node = const_val->data.x_block->source_node; @@ -5692,8 +5710,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case TypeTableEntryIdMaybe: { - if (const_val->data.x_maybe) { - render_const_value(g, buf, const_val->data.x_maybe); + if (get_codegen_ptr_type(const_val->type) != nullptr) + return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (const_val->data.x_nullable) { + render_const_value(g, buf, const_val->data.x_nullable); } else { buf_appendf(buf, "null"); } diff --git a/src/codegen.cpp b/src/codegen.cpp index fab2ad659e..65b465a519 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5020,6 +5020,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef return LLVMTypeOf(val) != type_entry->type_ref; } +static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) { + render_const_val_global(g, const_val, name); + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + { + ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; + render_const_val(g, pointee, ""); + render_const_val_global(g, pointee, ""); + ConstExprValue *other_val = pointee; + const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + case ConstPtrSpecialBaseArray: + { + ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + assert(array_const_val->type->id == TypeTableEntryIdArray); + if (array_const_val->type->zero_bits) { + // make this a null pointer + TypeTableEntry *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, + elem_index); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialBaseStruct: + { + ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; + assert(struct_const_val->type->id == TypeTableEntryIdStruct); + if (struct_const_val->type->zero_bits) { + // make this a null pointer + TypeTableEntry *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; + size_t gen_field_index = + struct_const_val->type->data.structure.fields[src_field_index].gen_index; + LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, + gen_field_index); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialHardCodedAddr: + { + uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; + TypeTableEntry *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + case ConstPtrSpecialFunction: + return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); + } + zig_unreachable(); +} + static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { TypeTableEntry *type_entry = const_val->type; assert(!type_entry->zero_bits); @@ -5068,19 +5141,15 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c { TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { - return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false); + return LLVMConstInt(LLVMInt1Type(), const_val->data.x_nullable ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { - if (const_val->data.x_maybe) { - return gen_const_val(g, const_val->data.x_maybe, ""); - } else { - return LLVMConstNull(child_type->type_ref); - } + return gen_const_val_ptr(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; bool make_unnamed_struct; - if (const_val->data.x_maybe) { - child_val = gen_const_val(g, const_val->data.x_maybe, ""); + if (const_val->data.x_nullable) { + child_val = gen_const_val(g, const_val->data.x_nullable, ""); maybe_val = LLVMConstAllOnes(LLVMInt1Type()); make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val); @@ -5270,78 +5339,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry); case TypeTableEntryIdPointer: - { - render_const_val_global(g, const_val, name); - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - { - ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; - render_const_val(g, pointee, ""); - render_const_val_global(g, pointee, ""); - ConstExprValue *other_val = pointee; - const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - case ConstPtrSpecialBaseArray: - { - ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; - assert(array_const_val->type->id == TypeTableEntryIdArray); - if (array_const_val->type->zero_bits) { - // make this a null pointer - TypeTableEntry *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, - elem_index); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); - const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); - return ptr_val; - } - case ConstPtrSpecialBaseStruct: - { - ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; - assert(struct_const_val->type->id == TypeTableEntryIdStruct); - if (struct_const_val->type->zero_bits) { - // make this a null pointer - TypeTableEntry *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; - size_t gen_field_index = - struct_const_val->type->data.structure.fields[src_field_index].gen_index; - LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, - gen_field_index); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); - const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); - return ptr_val; - } - case ConstPtrSpecialHardCodedAddr: - { - uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; - TypeTableEntry *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); - return const_val->global_refs->llvm_value; - } - case ConstPtrSpecialFunction: - return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); - } - } - zig_unreachable(); + return gen_const_val_ptr(g, const_val, name); case TypeTableEntryIdErrorUnion: { TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; diff --git a/src/ir.cpp b/src/ir.cpp index e62ec71875..13ecfd4233 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -62,6 +62,7 @@ enum ConstCastResultId { ConstCastResultIdType, ConstCastResultIdUnresolvedInferredErrSet, ConstCastResultIdAsyncAllocatorType, + ConstCastResultIdNullWrapPtr, }; struct ConstCastErrSetMismatch { @@ -90,6 +91,7 @@ struct ConstCastOnly { ConstCastOnly *error_union_error_set; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; + ConstCastOnly *null_wrap_ptr_child; ConstCastArg fn_arg; ConstCastArgNoAlias arg_no_alias; } data; @@ -7660,6 +7662,21 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry if (expected_type == actual_type) return result; + // * and [*] can do a const-cast-only to ?* and ?[*], respectively + if (expected_type->id == TypeTableEntryIdMaybe && + expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer && + actual_type->id == TypeTableEntryIdPointer) + { + ConstCastOnly child = types_match_const_cast_only(ira, + expected_type->data.maybe.child_type, actual_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdNullWrapPtr; + result.data.null_wrap_ptr_child = allocate_nonzero(1); + *result.data.null_wrap_ptr_child = child; + } + return result; + } + // pointer const if (expected_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && @@ -8741,7 +8758,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, zig_panic("TODO"); case CastOpNoop: { - copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic); + bool same_global_refs = other_val->special == ConstValSpecialStatic; + copy_const_val(const_val, other_val, same_global_refs); const_val->type = new_type; break; } @@ -9189,9 +9207,13 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_maybe = val; + if (get_codegen_ptr_type(wanted_type) != nullptr) { + copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); + } else { + const_instruction->base.value.data.x_nullable = val; + } + const_instruction->base.value.type = wanted_type; return &const_instruction->base; } @@ -9346,9 +9368,14 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so assert(val); IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); - const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_maybe = nullptr; + if (get_codegen_ptr_type(wanted_type) != nullptr) { + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + } else { + const_instruction->base.value.data.x_nullable = nullptr; + } + const_instruction->base.value.type = wanted_type; return &const_instruction->base; } @@ -10062,7 +10089,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from child type of maybe type to maybe type + // explicit cast from T to ?T + // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism if (wanted_type->id == TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { @@ -10113,7 +10141,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to %[]const T + // explicit cast from [N]T to E![]const T if (wanted_type->id == TypeTableEntryIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == TypeTableEntryIdArray) @@ -10143,7 +10171,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); } - // explicit cast from T to %?T + // explicit cast from T to E!?T if (wanted_type->id == TypeTableEntryIdErrorUnion && wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && actual_type->id != TypeTableEntryIdMaybe) @@ -10167,7 +10195,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from number literal to another type - // explicit cast from number literal to &const integer + // explicit cast from number literal to *const integer if (actual_type->id == TypeTableEntryIdComptimeFloat || actual_type->id == TypeTableEntryIdComptimeInt) { @@ -10391,6 +10419,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, source_instruction->source_node, child_type); copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst); + result->value.type = child_type; return result; } } @@ -10708,6 +10737,16 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { } } +static bool nullable_value_is_null(ConstExprValue *val) { + assert(val->special == ConstValSpecialStatic); + if (get_codegen_ptr_type(val->type) != nullptr) { + return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else { + return val->data.x_nullable == nullptr; + } +} + static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; @@ -10737,7 +10776,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; - bool is_null = (maybe_val->data.x_maybe == nullptr); + bool is_null = nullable_value_is_null(maybe_val); ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ira->codegen->builtin_types.entry_bool; @@ -12015,7 +12054,9 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_maybe = nullptr; + assert(get_codegen_ptr_type(nullable_type) != nullptr); + out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + out_val->data.x_ptr.data.hard_coded_addr.addr = 0; return nullable_type; } IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, @@ -14207,6 +14248,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { IrInstruction *ptr = load_ptr_instruction->ptr->other; + if (type_is_invalid(ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr); ir_link_new_instruction(result, &load_ptr_instruction->base); assert(result->value.type); @@ -14773,7 +14817,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr); + out_val->data.x_bool = !nullable_value_is_null(maybe_val); return ira->codegen->builtin_types.entry_bool; } @@ -14837,13 +14881,18 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val); if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - if (!maybe_val->data.x_maybe) { + if (nullable_value_is_null(maybe_val)) { ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base); out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_maybe; + out_val->data.x_ptr.mut = val->data.x_ptr.mut; + if (type_is_codegen_pointer(child_type)) { + out_val->data.x_ptr.data.ref.pointee = maybe_val; + } else { + out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_nullable; + } return result_type; } } @@ -16206,12 +16255,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop 0, 0); fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { - fn_def_fields[6].data.x_maybe = create_const_vals(1); + fn_def_fields[6].data.x_nullable = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); - init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true); + init_const_slice(ira->codegen, fn_def_fields[6].data.x_nullable, lib_name, 0, buf_len(fn_node->lib_name), true); + } else { + fn_def_fields[6].data.x_nullable = nullptr; } - else - fn_def_fields[6].data.x_maybe = nullptr; // return_type: type ensure_field_index(fn_def_val->type, "return_type", 7); fn_def_fields[7].special = ConstValSpecialStatic; @@ -16664,8 +16713,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField"); - for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) - { + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index]; @@ -16676,12 +16724,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); - if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) - inner_fields[1].data.x_maybe = nullptr; - else - { - inner_fields[1].data.x_maybe = create_const_vals(1); - make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type); + if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) { + inner_fields[1].data.x_nullable = nullptr; + } else { + inner_fields[1].data.x_nullable = create_const_vals(1); + make_enum_field_val(inner_fields[1].data.x_nullable, union_field->enum_field, type_info_enum_field_type); } inner_fields[2].special = ConstValSpecialStatic; @@ -16737,8 +16784,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); - for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) - { + for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index]; ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index]; @@ -16749,15 +16795,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); - if (!type_has_bits(struct_field->type_entry)) - inner_fields[1].data.x_maybe = nullptr; - else - { + if (!type_has_bits(struct_field->type_entry)) { + inner_fields[1].data.x_nullable = nullptr; + } else { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); - inner_fields[1].data.x_maybe = create_const_vals(1); - inner_fields[1].data.x_maybe->special = ConstValSpecialStatic; - inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize; - bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset); + inner_fields[1].data.x_nullable = create_const_vals(1); + inner_fields[1].data.x_nullable->special = ConstValSpecialStatic; + inner_fields[1].data.x_nullable->type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&inner_fields[1].data.x_nullable->data.x_bigint, byte_offset); } inner_fields[2].special = ConstValSpecialStatic; @@ -19008,9 +19053,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->builtin_types.entry_invalid; - if (target->value.type->id == TypeTableEntryIdMaybe) { - val = val->data.x_maybe; - } if (val->type->id == TypeTableEntryIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, instruction->base.source_node, usize); @@ -19936,6 +19978,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); instruction->value.type = instruction_type; + if (instruction->other) { instruction->other->value.type = instruction_type; } else { diff --git a/test/cases/cast.zig b/test/cases/cast.zig index c3ef24cd78..da3cba7d80 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -1,5 +1,6 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; test "int to ptr cast" { const x = usize(13); @@ -400,3 +401,8 @@ fn testCastPtrOfArrayToSliceAndPtr() void { assert(mem.eql(u8, array[0..], "coeu")); } +test "cast *[1][*]const u8 to [*]const ?[*]const u8" { + const window_name = [1][*]const u8{c"window name"}; + const x: [*]const ?[*]const u8 = &window_name; + assert(mem.eql(u8, std.cstr.toSliceConst(??x[0]), "window name")); +} -- cgit v1.2.3 From 9046b5eac01540a783740451a6593ef0207c181e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 11:41:59 -0400 Subject: fix assertion failure when debug printing comptime values --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 13ecfd4233..10098f3c32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -113,7 +113,7 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { - assert(const_val->type->id == TypeTableEntryIdPointer); + assert(get_codegen_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: -- cgit v1.2.3 From d464b2532200de3778ac7362e701791a11150d55 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 10 Jun 2018 04:39:22 +0200 Subject: support `--target-arch wasm32` (#1094) Add wasm32 support to the build-obj, build-exe and build-lib commands of the stage 1 compiler. Wasm64 should work transparently once it's supported in upstream LLVM. To export a function: // lib.zig - for exposition, not necessary for this example pub use @import("add.zig"); // add.zig export fn add(a: i32, b: i32) i32 { return a + b; } To import a function: // cube.zig extern fn square(x: i32) i32; export fn cube(x: i32) i32 { return x * square(x); } --- build.zig | 1 + src/link.cpp | 15 ++++++++++++++- src/target.cpp | 7 +++++-- src/zig_llvm.cpp | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/build.zig b/build.zig index 109a799ac9..08a47570ef 100644 --- a/build.zig +++ b/build.zig @@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void { exe.addObjectFile(lib); } } else { + addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); diff --git a/src/link.cpp b/src/link.cpp index d454d77aae..d2925cb5a8 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) { } } +static void construct_linker_job_wasm(LinkJob *lj) { + CodeGen *g = lj->codegen; + + lj->args.append("--relocatable"); // So lld doesn't look for _start. + lj->args.append("-o"); + lj->args.append(buf_ptr(&lj->out_file)); + + // .o files + for (size_t i = 0; i < g->link_objects.length; i += 1) { + lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); + } +} + //static bool is_target_cyg_mingw(const ZigTarget *target) { // return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || // (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); @@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) { case ZigLLVM_MachO: return construct_linker_job_macho(lj); case ZigLLVM_Wasm: - zig_panic("TODO link wasm"); + return construct_linker_job_wasm(lj); } } diff --git a/src/target.cpp b/src/target.cpp index c53ed74d14..bd4aa4d4c2 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -597,12 +597,15 @@ void resolve_target_object_format(ZigTarget *target) { case ZigLLVM_tce: case ZigLLVM_tcele: case ZigLLVM_thumbeb: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: case ZigLLVM_xcore: target->oformat= ZigLLVM_ELF; return; + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + target->oformat = ZigLLVM_Wasm; + return; + case ZigLLVM_ppc: case ZigLLVM_ppc64: if (is_os_darwin(target)) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 5905fa8167..24f2a8a343 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -838,7 +838,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ return lld::mach_o::link(array_ref_args, diag); case ZigLLVM_Wasm: - assert(false); // TODO ZigLLDLink for Wasm + return lld::wasm::link(array_ref_args, false, diag); } assert(false); // unreachable abort(); -- cgit v1.2.3 From ec1b6f66737f8c3cbc0420715c2c502c7e710081 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 9 Jun 2018 23:42:14 -0400 Subject: breaking syntax change: ??x to x.? (#1095) See #1023 This also renames Nullable/Maybe to Optional --- build.zig | 2 +- doc/codegen.md | 8 +- doc/langref.html.in | 122 ++++++++++++++-------------- example/cat/main.zig | 2 +- src-self-hosted/arg.zig | 10 +-- src-self-hosted/llvm.zig | 2 +- src-self-hosted/main.zig | 8 +- src/all_types.hpp | 44 +++++------ src/analyze.cpp | 70 ++++++++-------- src/ast_render.cpp | 6 +- src/codegen.cpp | 60 +++++++------- src/ir.cpp | 198 +++++++++++++++++++++++----------------------- src/ir_print.cpp | 16 ++-- src/parser.cpp | 21 +++-- src/tokenizer.cpp | 10 +-- src/tokenizer.hpp | 3 +- src/translate_c.cpp | 14 ++-- std/array_list.zig | 2 +- std/buf_map.zig | 6 +- std/event.zig | 4 +- std/fmt/index.zig | 6 +- std/hash_map.zig | 8 +- std/heap.zig | 4 +- std/json.zig | 12 +-- std/linked_list.zig | 8 +- std/macho.zig | 2 +- std/mem.zig | 22 +++--- std/os/child_process.zig | 18 ++--- std/os/index.zig | 4 +- std/os/linux/vdso.zig | 2 +- std/os/path.zig | 8 +- std/segmented_list.zig | 8 +- std/special/bootstrap.zig | 6 +- std/special/builtin.zig | 8 +- std/unicode.zig | 24 +++--- std/zig/ast.zig | 12 +-- std/zig/parse.zig | 35 +++++--- std/zig/parser_test.zig | 5 +- std/zig/render.zig | 25 +++--- test/cases/bugs/656.zig | 2 +- test/cases/cast.zig | 50 ++++++------ test/cases/error.zig | 2 +- test/cases/eval.zig | 2 +- test/cases/generics.zig | 2 +- test/cases/misc.zig | 2 +- test/cases/null.zig | 30 +++---- test/cases/reflection.zig | 2 +- test/cases/type_info.zig | 14 ++-- test/cases/while.zig | 12 +-- test/compile_errors.zig | 16 ++-- test/tests.zig | 12 +-- 51 files changed, 489 insertions(+), 482 deletions(-) (limited to 'src') diff --git a/build.zig b/build.zig index 08a47570ef..eada37816c 100644 --- a/build.zig +++ b/build.zig @@ -75,7 +75,7 @@ pub fn build(b: *Builder) !void { cxx_compiler, "-print-file-name=libstdc++.a", }); - const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); + const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { warn( \\Unable to determine path to libstdc++.a diff --git a/doc/codegen.md b/doc/codegen.md index 02406fae82..65f12f4875 100644 --- a/doc/codegen.md +++ b/doc/codegen.md @@ -6,7 +6,7 @@ Every type has a "handle". If a type is a simple primitive type such as i32 or f64, the handle is "by value", meaning that we pass around the value itself when we refer to a value of that type. -If a type is a container, error union, maybe type, slice, or array, then its +If a type is a container, error union, optional type, slice, or array, then its handle is a pointer, and everywhere we refer to a value of this type we refer to a pointer. @@ -19,7 +19,7 @@ Error union types are represented as: payload: T, } -Maybe types are represented as: +Optional types are represented as: struct { payload: T, @@ -28,6 +28,6 @@ Maybe types are represented as: ## Data Optimizations -Maybe pointer types are special: the 0x0 pointer value is used to represent a -null pointer. Thus, instead of the struct above, maybe pointer types are +Optional pointer types are special: the 0x0 pointer value is used to represent a +null pointer. Thus, instead of the struct above, optional pointer types are represented as a `usize` in codegen and the handle is by value. diff --git a/doc/langref.html.in b/doc/langref.html.in index 6a1f1c3102..4c4a637095 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -156,18 +156,18 @@ pub fn main() void { true or false, !true); - // nullable - var nullable_value: ?[]const u8 = null; - assert(nullable_value == null); + // optional + var optional_value: ?[]const u8 = null; + assert(optional_value == null); - warn("\nnullable 1\ntype: {}\nvalue: {}\n", - @typeName(@typeOf(nullable_value)), nullable_value); + warn("\noptional 1\ntype: {}\nvalue: {}\n", + @typeName(@typeOf(optional_value)), optional_value); - nullable_value = "hi"; - assert(nullable_value != null); + optional_value = "hi"; + assert(optional_value != null); - warn("\nnullable 2\ntype: {}\nvalue: {}\n", - @typeName(@typeOf(nullable_value)), nullable_value); + warn("\noptional 2\ntype: {}\nvalue: {}\n", + @typeName(@typeOf(optional_value)), optional_value); // error union var number_or_error: error!i32 = error.ArgNotFound; @@ -428,7 +428,7 @@ pub fn main() void { null - used to set a nullable type to null + used to set an optional type to null undefined @@ -440,7 +440,7 @@ pub fn main() void { - {#see_also|Nullables|this#} + {#see_also|Optionals|this#} {#header_close#} {#header_open|String Literals#} {#code_begin|test#} @@ -988,7 +988,7 @@ a ^= b

    a ?? b
      -
    • {#link|Nullables#}
    • +
    • {#link|Optionals#}
    If a is null, @@ -1003,10 +1003,10 @@ unwrapped == 1234 -
    ??a
    +
    a.?
      -
    • {#link|Nullables#}
    • +
    • {#link|Optionals#}
    @@ -1015,7 +1015,7 @@ unwrapped == 1234
    const value: ?u32 = 5678;
    -??value == 5678
    +value.? == 5678 @@ -1103,7 +1103,7 @@ unwrapped == 1234
    a == null
      -
    • {#link|Nullables#}
    • +
    • {#link|Optionals#}
    @@ -1267,8 +1267,8 @@ x.* == 1234
    {#header_open|Precedence#}
    x() x[] x.y
     a!b
    -!x -x -%x ~x &x ?x ??x
    -x{} x.*
    +!x -x -%x ~x &x ?x
    +x{} x.* x.?
     ! * / % ** *%
     + - ++ +% -%
     << >>
    @@ -1483,17 +1483,17 @@ test "volatile" {
         assert(@typeOf(mmio_ptr) == *volatile u8);
     }
     
    -test "nullable pointers" {
    -    // Pointers cannot be null. If you want a null pointer, use the nullable
    -    // prefix `?` to make the pointer type nullable.
    +test "optional pointers" {
    +    // Pointers cannot be null. If you want a null pointer, use the optional
    +    // prefix `?` to make the pointer type optional.
         var ptr: ?*i32 = null;
     
         var x: i32 = 1;
         ptr = &x;
     
    -    assert((??ptr).* == 1);
    +    assert(ptr.?.* == 1);
     
    -    // Nullable pointers are the same size as normal pointers, because pointer
    +    // Optional pointers are the same size as normal pointers, because pointer
         // value 0 is used as the null value.
         assert(@sizeOf(?*i32) == @sizeOf(*i32));
     }
    @@ -1832,7 +1832,7 @@ test "linked list" {
             .last = &node,
             .len = 1,
         };
    -    assert((??list2.first).data == 1234);
    +    assert(list2.first.?.data == 1234);
     }
           {#code_end#}
           {#see_also|comptime|@fieldParentPtr#}
    @@ -2270,7 +2270,7 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
     }
     
     test "while null capture" {
    -    // Just like if expressions, while loops can take a nullable as the
    +    // Just like if expressions, while loops can take an optional as the
         // condition and capture the payload. When null is encountered the loop
         // exits.
         var sum1: u32 = 0;
    @@ -2280,7 +2280,7 @@ test "while null capture" {
         }
         assert(sum1 == 3);
     
    -    // The else branch is allowed on nullable iteration. In this case, it will
    +    // The else branch is allowed on optional iteration. In this case, it will
         // be executed on the first null value encountered.
         var sum2: u32 = 0;
         numbers_left = 3;
    @@ -2340,7 +2340,7 @@ fn typeNameLength(comptime T: type) usize {
         return @typeName(T).len;
     }
           {#code_end#}
    -      {#see_also|if|Nullables|Errors|comptime|unreachable#}
    +      {#see_also|if|Optionals|Errors|comptime|unreachable#}
           {#header_close#}
           {#header_open|for#}
           {#code_begin|test|for#}
    @@ -2400,7 +2400,7 @@ test "for else" {
             if (value == null) {
                 break 9;
             } else {
    -            sum += ??value;
    +            sum += value.?;
             }
         } else blk: {
             assert(sum == 7);
    @@ -2461,7 +2461,7 @@ test "if boolean" {
         assert(result == 47);
     }
     
    -test "if nullable" {
    +test "if optional" {
         // If expressions test for null.
     
         const a: ?u32 = 0;
    @@ -2544,7 +2544,7 @@ test "if error union" {
         }
     }
           {#code_end#}
    -      {#see_also|Nullables|Errors#}
    +      {#see_also|Optionals|Errors#}
           {#header_close#}
           {#header_open|defer#}
           {#code_begin|test|defer#}
    @@ -3167,24 +3167,24 @@ test "inferred error set" {
           

    TODO

    {#header_close#} {#header_close#} - {#header_open|Nullables#} + {#header_open|Optionals#}

    One area that Zig provides safety without compromising efficiency or - readability is with the nullable type. + readability is with the optional type.

    - The question mark symbolizes the nullable type. You can convert a type to a nullable + The question mark symbolizes the optional type. You can convert a type to an optional type by putting a question mark in front of it, like this:

    {#code_begin|syntax#} // normal integer const normal_int: i32 = 1234; -// nullable integer -const nullable_int: ?i32 = 5678; +// optional integer +const optional_int: ?i32 = 5678; {#code_end#}

    - Now the variable nullable_int could be an i32, or null. + Now the variable optional_int could be an i32, or null.

    Instead of integers, let's talk about pointers. Null references are the source of many runtime @@ -3193,8 +3193,8 @@ const nullable_int: ?i32 = 5678;

    Zig does not have them.

    - Instead, you can use a nullable pointer. This secretly compiles down to a normal pointer, - since we know we can use 0 as the null value for the nullable type. But the compiler + Instead, you can use an optional pointer. This secretly compiles down to a normal pointer, + since we know we can use 0 as the null value for the optional type. But the compiler can check your work and make sure you don't assign null to something that can't be null.

    @@ -3226,7 +3226,7 @@ fn doAThing() ?*Foo {

    Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" is *u8 not ?*u8. The ?? operator - unwrapped the nullable type and therefore ptr is guaranteed to be non-null everywhere + unwrapped the optional type and therefore ptr is guaranteed to be non-null everywhere it is used in the function.

    @@ -3245,10 +3245,10 @@ fn doAThing() ?*Foo { In Zig you can accomplish the same thing:

    {#code_begin|syntax#} -fn doAThing(nullable_foo: ?*Foo) void { +fn doAThing(optional_foo: ?*Foo) void { // do some stuff - if (nullable_foo) |foo| { + if (optional_foo) |foo| { doSomethingWithFoo(foo); } @@ -3257,7 +3257,7 @@ fn doAThing(nullable_foo: ?*Foo) void { {#code_end#}

    Once again, the notable thing here is that inside the if block, - foo is no longer a nullable pointer, it is a pointer, which + foo is no longer an optional pointer, it is a pointer, which cannot be null.

    @@ -3267,20 +3267,20 @@ fn doAThing(nullable_foo: ?*Foo) void { The optimizer can sometimes make better decisions knowing that pointer arguments cannot be null.

    - {#header_open|Nullable Type#} -

    A nullable is created by putting ? in front of a type. You can use compile-time - reflection to access the child type of a nullable:

    + {#header_open|Optional Type#} +

    An optional is created by putting ? in front of a type. You can use compile-time + reflection to access the child type of an optional:

    {#code_begin|test#} const assert = @import("std").debug.assert; -test "nullable type" { - // Declare a nullable and implicitly cast from null: +test "optional type" { + // Declare an optional and implicitly cast from null: var foo: ?i32 = null; - // Implicitly cast from child type of a nullable + // Implicitly cast from child type of an optional foo = 1234; - // Use compile-time reflection to access the child type of the nullable: + // Use compile-time reflection to access the child type of the optional: comptime assert(@typeOf(foo).Child == i32); } {#code_end#} @@ -4888,7 +4888,7 @@ pub const TypeId = enum { ComptimeInt, Undefined, Null, - Nullable, + Optional, ErrorUnion, Error, Enum, @@ -4922,7 +4922,7 @@ pub const TypeInfo = union(TypeId) { ComptimeInt: void, Undefined: void, Null: void, - Nullable: Nullable, + Optional: Optional, ErrorUnion: ErrorUnion, ErrorSet: ErrorSet, Enum: Enum, @@ -4975,7 +4975,7 @@ pub const TypeInfo = union(TypeId) { defs: []Definition, }; - pub const Nullable = struct { + pub const Optional = struct { child: type, }; @@ -5366,8 +5366,8 @@ comptime {

    At compile-time:

    {#code_begin|test_err|unable to unwrap null#} comptime { - const nullable_number: ?i32 = null; - const number = ??nullable_number; + const optional_number: ?i32 = null; + const number = optional_number.?; } {#code_end#}

    At runtime crashes with the message attempt to unwrap null and a stack trace.

    @@ -5376,9 +5376,9 @@ comptime { {#code_begin|exe|test#} const warn = @import("std").debug.warn; pub fn main() void { - const nullable_number: ?i32 = null; + const optional_number: ?i32 = null; - if (nullable_number) |number| { + if (optional_number) |number| { warn("got number: {}\n", number); } else { warn("it's null\n"); @@ -5939,9 +5939,9 @@ AsmInputItem = "[" Symbol "]" String "(" Expression ")" AsmClobbers= ":" list(String, ",") -UnwrapExpression = BoolOrExpression (UnwrapNullable | UnwrapError) | BoolOrExpression +UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression -UnwrapNullable = "??" Expression +UnwrapOptional = "??" Expression UnwrapError = "catch" option("|" Symbol "|") Expression @@ -6015,12 +6015,10 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") FieldAccessExpression = "." Symbol -PtrDerefExpression = ".*" - FnCallExpression = "(" list(Expression, ",") ")" ArrayAccessExpression = "[" Expression "]" @@ -6033,7 +6031,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField = "." Symbol "=" Expression -PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" +PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "-%" | "try" | "await" PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType diff --git a/example/cat/main.zig b/example/cat/main.zig index 1b34cb22eb..27690d2695 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -7,7 +7,7 @@ const allocator = std.debug.global_allocator; pub fn main() !void { var args_it = os.args(); - const exe = try unwrapArg(??args_it.next(allocator)); + const exe = try unwrapArg(args_it.next(allocator).?); var catted_anything = false; var stdout_file = try io.getStdOut(); diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index df2c04ef1f..dc89483213 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -99,7 +99,7 @@ pub const Args = struct { error.ArgumentNotInAllowedSet => { std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg); std.debug.warn("allowed options are "); - for (??flag.allowed_set) |possible| { + for (flag.allowed_set.?) |possible| { std.debug.warn("'{}' ", possible); } std.debug.warn("\n"); @@ -276,14 +276,14 @@ test "parse arguments" { debug.assert(!args.present("help2")); debug.assert(!args.present("init")); - debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig")); - debug.assert(mem.eql(u8, ??args.single("color"), "on")); + debug.assert(mem.eql(u8, args.single("build-file").?, "build.zig")); + debug.assert(mem.eql(u8, args.single("color").?, "on")); - const objects = ??args.many("object"); + const objects = args.many("object").?; debug.assert(mem.eql(u8, objects[0], "obj1")); debug.assert(mem.eql(u8, objects[1], "obj2")); - debug.assert(mem.eql(u8, ??args.single("library"), "lib2")); + debug.assert(mem.eql(u8, args.single("library").?, "lib2")); const pos = args.positionals.toSliceConst(); debug.assert(mem.eql(u8, pos[0], "build")); diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 16c359adcf..391a92cd63 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -8,6 +8,6 @@ pub const ContextRef = removeNullability(c.LLVMContextRef); pub const BuilderRef = removeNullability(c.LLVMBuilderRef); fn removeNullability(comptime T: type) type { - comptime assert(@typeId(T) == builtin.TypeId.Nullable); + comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; } diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index a264b5484a..64734f077a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -490,7 +490,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo try stderr.print("encountered --pkg-end with no matching --pkg-begin\n"); os.exit(1); } - cur_pkg = ??cur_pkg.parent; + cur_pkg = cur_pkg.parent.?; } } @@ -514,7 +514,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo }, } - const basename = os.path.basename(??in_file); + const basename = os.path.basename(in_file.?); var it = mem.split(basename, "."); const root_name = it.next() ?? { try stderr.write("file name cannot be empty\n"); @@ -523,12 +523,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const asm_a = flags.many("assembly"); const obj_a = flags.many("object"); - if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) { + if (in_file == null and (obj_a == null or obj_a.?.len == 0) and (asm_a == null or asm_a.?.len == 0)) { try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); os.exit(1); } - if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) { + if (out_type == Module.Kind.Obj and (obj_a != null and obj_a.?.len != 0)) { try stderr.write("When building an object file, --object arguments are invalid\n"); os.exit(1); } diff --git a/src/all_types.hpp b/src/all_types.hpp index 14a44ea768..2a5a0ad740 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -145,8 +145,8 @@ enum ConstPtrSpecial { // emit a binary with a compile time known address. // In this case index is the numeric address value. // We also use this for null pointer. We need the data layout for ConstCastOnly == true - // types to be the same, so all nullables of pointer types use x_ptr - // instead of x_nullable + // types to be the same, so all optionals of pointer types use x_ptr + // instead of x_optional ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. @@ -222,10 +222,10 @@ enum RuntimeHintErrorUnion { RuntimeHintErrorUnionNonError, }; -enum RuntimeHintMaybe { - RuntimeHintMaybeUnknown, - RuntimeHintMaybeNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known. - RuntimeHintMaybeNonNull, +enum RuntimeHintOptional { + RuntimeHintOptionalUnknown, + RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known. + RuntimeHintOptionalNonNull, }; enum RuntimeHintPtr { @@ -254,7 +254,7 @@ struct ConstExprValue { bool x_bool; ConstBoundFnValue x_bound_fn; TypeTableEntry *x_type; - ConstExprValue *x_nullable; + ConstExprValue *x_optional; ConstErrValue x_err_union; ErrorTableEntry *x_err_set; BigInt x_enum_tag; @@ -268,7 +268,7 @@ struct ConstExprValue { // populated if special == ConstValSpecialRuntime RuntimeHintErrorUnion rh_error_union; - RuntimeHintMaybe rh_maybe; + RuntimeHintOptional rh_maybe; RuntimeHintPtr rh_ptr; } data; }; @@ -556,7 +556,7 @@ enum BinOpType { BinOpTypeMultWrap, BinOpTypeDiv, BinOpTypeMod, - BinOpTypeUnwrapMaybe, + BinOpTypeUnwrapOptional, BinOpTypeArrayCat, BinOpTypeArrayMult, BinOpTypeErrorUnion, @@ -623,8 +623,8 @@ enum PrefixOp { PrefixOpBinNot, PrefixOpNegation, PrefixOpNegationWrap, - PrefixOpMaybe, - PrefixOpUnwrapMaybe, + PrefixOpOptional, + PrefixOpUnwrapOptional, PrefixOpAddrOf, }; @@ -1052,7 +1052,7 @@ struct TypeTableEntryStruct { HashMap fields_by_name; }; -struct TypeTableEntryMaybe { +struct TypeTableEntryOptional { TypeTableEntry *child_type; }; @@ -1175,7 +1175,7 @@ enum TypeTableEntryId { TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefined, TypeTableEntryIdNull, - TypeTableEntryIdMaybe, + TypeTableEntryIdOptional, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, TypeTableEntryIdEnum, @@ -1206,7 +1206,7 @@ struct TypeTableEntry { TypeTableEntryFloat floating; TypeTableEntryArray array; TypeTableEntryStruct structure; - TypeTableEntryMaybe maybe; + TypeTableEntryOptional maybe; TypeTableEntryErrorUnion error_union; TypeTableEntryErrorSet error_set; TypeTableEntryEnum enumeration; @@ -1402,7 +1402,7 @@ enum PanicMsgId { PanicMsgIdRemainderDivisionByZero, PanicMsgIdExactDivisionRemainder, PanicMsgIdSliceWidenRemainder, - PanicMsgIdUnwrapMaybeFail, + PanicMsgIdUnwrapOptionalFail, PanicMsgIdInvalidErrorCode, PanicMsgIdIncorrectAlignment, PanicMsgIdBadUnionField, @@ -2016,8 +2016,8 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapMaybe, - IrInstructionIdMaybeWrap, + IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, IrInstructionIdCtz, @@ -2184,7 +2184,7 @@ enum IrUnOp { IrUnOpNegation, IrUnOpNegationWrap, IrUnOpDereference, - IrUnOpMaybe, + IrUnOpOptional, }; struct IrInstructionUnOp { @@ -2487,7 +2487,7 @@ struct IrInstructionTestNonNull { IrInstruction *value; }; -struct IrInstructionUnwrapMaybe { +struct IrInstructionUnwrapOptional { IrInstruction base; IrInstruction *value; @@ -2745,7 +2745,7 @@ struct IrInstructionUnwrapErrPayload { bool safety_check_on; }; -struct IrInstructionMaybeWrap { +struct IrInstructionOptionalWrap { IrInstruction base; IrInstruction *value; @@ -2954,10 +2954,10 @@ struct IrInstructionExport { struct IrInstructionErrorReturnTrace { IrInstruction base; - enum Nullable { + enum Optional { Null, NonNull, - } nullable; + } optional; }; struct IrInstructionErrorUnion { diff --git a/src/analyze.cpp b/src/analyze.cpp index 16b2cb0590..ed261148ea 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -236,7 +236,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -272,7 +272,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -520,7 +520,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { } else { ensure_complete_type(g, child_type); - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional); assert(child_type->type_ref || child_type->zero_bits); assert(child_type->di_type); entry->is_copyable = type_is_copyable(g, child_type); @@ -1361,7 +1361,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { return type_entry->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdUnion: return type_entry->data.unionation.layout == ContainerLayoutPacked; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return type_is_codegen_pointer(child_type); @@ -1415,7 +1415,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { return type_allowed_in_extern(g, type_entry->data.pointer.child_type); case TypeTableEntryIdStruct: return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; @@ -1538,7 +1538,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -1632,7 +1632,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -2985,8 +2985,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); - if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) { + TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); + if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { return wrong_panic_prototype(g, proto_node, fn_type); } @@ -3368,7 +3368,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -3746,7 +3746,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -3805,7 +3805,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: @@ -3824,7 +3824,7 @@ TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) { if (type->id == TypeTableEntryIdPointer) return type; if (type->id == TypeTableEntryIdFn) return type; if (type->id == TypeTableEntryIdPromise) return type; - if (type->id == TypeTableEntryIdMaybe) { + if (type->id == TypeTableEntryIdOptional) { if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type; @@ -4331,7 +4331,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { return type_has_bits(type_entry); case TypeTableEntryIdErrorUnion: return type_has_bits(type_entry->data.error_union.payload_type); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && !type_is_codegen_pointer(type_entry->data.maybe.child_type); case TypeTableEntryIdUnion: @@ -4709,12 +4709,12 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdUnion: // TODO better hashing algorithm return 2709806591; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { return hash_const_val(const_val) * 1992916303; } else { - if (const_val->data.x_nullable) { - return hash_const_val(const_val->data.x_nullable) * 1992916303; + if (const_val->data.x_optional) { + return hash_const_val(const_val->data.x_optional) * 1992916303; } else { return 4016830364; } @@ -4817,12 +4817,12 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { } return false; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: if (get_codegen_ptr_type(value->type) != nullptr) return value->data.x_ptr.mut == ConstPtrMutComptimeVar; - if (value->data.x_nullable == nullptr) + if (value->data.x_optional == nullptr) return false; - return can_mutate_comptime_var_state(value->data.x_nullable); + return can_mutate_comptime_var_state(value->data.x_optional); case TypeTableEntryIdErrorUnion: if (value->data.x_err_union.err != nullptr) @@ -4869,7 +4869,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) { case TypeTableEntryIdUnion: return false; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return return_type_is_cacheable(return_type->data.maybe.child_type); case TypeTableEntryIdErrorUnion: @@ -4978,7 +4978,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdUnion: assert(type_has_zero_bits_known(type_entry)); return type_entry->data.unionation.requires_comptime; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return type_requires_comptime(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: return type_requires_comptime(type_entry->data.error_union.payload_type); @@ -5460,13 +5460,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { zig_panic("TODO"); case TypeTableEntryIdNull: zig_panic("TODO"); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: if (get_codegen_ptr_type(a->type) != nullptr) return const_values_equal_ptr(a, b); - if (a->data.x_nullable == nullptr || b->data.x_nullable == nullptr) { - return (a->data.x_nullable == nullptr && b->data.x_nullable == nullptr); + if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) { + return (a->data.x_optional == nullptr && b->data.x_optional == nullptr); } else { - return const_values_equal(a->data.x_nullable, b->data.x_nullable); + return const_values_equal(a->data.x_optional, b->data.x_optional); } case TypeTableEntryIdErrorUnion: zig_panic("TODO"); @@ -5708,12 +5708,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "undefined"); return; } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); - if (const_val->data.x_nullable) { - render_const_value(g, buf, const_val->data.x_nullable); + if (const_val->data.x_optional) { + render_const_value(g, buf, const_val->data.x_optional); } else { buf_appendf(buf, "null"); } @@ -5819,7 +5819,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: @@ -5865,7 +5865,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdPromise: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -5987,7 +5987,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdComptimeInt, TypeTableEntryIdUndefined, TypeTableEntryIdNull, - TypeTableEntryIdMaybe, + TypeTableEntryIdOptional, TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorSet, TypeTableEntryIdEnum, @@ -6042,7 +6042,7 @@ size_t type_id_index(TypeTableEntry *entry) { return 11; case TypeTableEntryIdNull: return 12; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: return 13; case TypeTableEntryIdErrorUnion: return 14; @@ -6100,8 +6100,8 @@ const char *type_id_name(TypeTableEntryId id) { return "Undefined"; case TypeTableEntryIdNull: return "Null"; - case TypeTableEntryIdMaybe: - return "Nullable"; + case TypeTableEntryIdOptional: + return "Optional"; case TypeTableEntryIdErrorUnion: return "ErrorUnion"; case TypeTableEntryIdErrorSet: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3785cb6ca1..2c8c03b226 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) { case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitOr: return "|="; case BinOpTypeAssignMergeErrorSets: return "||="; - case BinOpTypeUnwrapMaybe: return "??"; + case BinOpTypeUnwrapOptional: return "??"; case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayMult: return "**"; case BinOpTypeErrorUnion: return "!"; @@ -66,8 +66,8 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; - case PrefixOpMaybe: return "?"; - case PrefixOpUnwrapMaybe: return "??"; + case PrefixOpOptional: return "?"; + case PrefixOpUnwrapOptional: return "??"; case PrefixOpAddrOf: return "&"; } zig_unreachable(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 65b465a519..da08ecfc9e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -865,7 +865,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("exact division produced remainder"); case PanicMsgIdSliceWidenRemainder: return buf_create_from_str("slice widening size mismatch"); - case PanicMsgIdUnwrapMaybeFail: + case PanicMsgIdUnwrapOptionalFail: return buf_create_from_str("attempt to unwrap null"); case PanicMsgIdUnreachable: return buf_create_from_str("reached unreachable code"); @@ -2734,7 +2734,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst switch (op_id) { case IrUnOpInvalid: - case IrUnOpMaybe: + case IrUnOpOptional: case IrUnOpDereference: zig_unreachable(); case IrUnOpNegation: @@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru } static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) { - assert(maybe_type->id == TypeTableEntryIdMaybe); + assert(maybe_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; if (child_type->zero_bits) { return maybe_handle; @@ -3355,23 +3355,23 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable } static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, - IrInstructionUnwrapMaybe *instruction) + IrInstructionUnwrapOptional *instruction) { TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type; - assert(maybe_type->id == TypeTableEntryIdMaybe); + assert(maybe_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdUnwrapMaybeFail); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); LLVMPositionBuilderAtEnd(g->builder, ok_block); } @@ -3593,17 +3593,17 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I } else if (target_type->id == TypeTableEntryIdFn) { align_bytes = target_type->data.fn.fn_type_id.alignment; ptr_val = target_val; - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) { align_bytes = target_type->data.maybe.child_type->data.pointer.alignment; ptr_val = target_val; - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdFn) { align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; ptr_val = target_val; - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdPromise) { zig_panic("TODO audit this function"); @@ -3705,7 +3705,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn success_order, failure_order, instruction->is_weak); TypeTableEntry *maybe_type = instruction->base.value.type; - assert(maybe_type->id == TypeTableEntryIdMaybe); + assert(maybe_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = maybe_type->data.maybe.child_type; if (type_is_codegen_pointer(child_type)) { @@ -4115,10 +4115,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu } } -static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionMaybeWrap *instruction) { +static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionOptionalWrap *instruction) { TypeTableEntry *wanted_type = instruction->base.value.type; - assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(wanted_type->id == TypeTableEntryIdOptional); TypeTableEntry *child_type = wanted_type->data.maybe.child_type; @@ -4699,8 +4699,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapMaybe: - return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction); + case IrInstructionIdUnwrapOptional: + return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -4741,8 +4741,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction); case IrInstructionIdUnwrapErrPayload: return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction); - case IrInstructionIdMaybeWrap: - return ir_render_maybe_wrap(g, executable, (IrInstructionMaybeWrap *)instruction); + case IrInstructionIdOptionalWrap: + return ir_render_maybe_wrap(g, executable, (IrInstructionOptionalWrap *)instruction); case IrInstructionIdErrWrapCode: return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction); case IrInstructionIdErrWrapPayload: @@ -4972,7 +4972,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } case TypeTableEntryIdPointer: case TypeTableEntryIdFn: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdPromise: { LLVMValueRef ptr_val = gen_const_val(g, const_val, ""); @@ -5137,19 +5137,19 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c } else { return LLVMConstNull(LLVMInt1Type()); } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { - return LLVMConstInt(LLVMInt1Type(), const_val->data.x_nullable ? 1 : 0, false); + return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; bool make_unnamed_struct; - if (const_val->data.x_nullable) { - child_val = gen_const_val(g, const_val->data.x_nullable, ""); + if (const_val->data.x_optional) { + child_val = gen_const_val(g, const_val->data.x_optional, ""); maybe_val = LLVMConstAllOnes(LLVMInt1Type()); make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val); @@ -5755,8 +5755,8 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdSlice) { IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; slot = &slice_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdMaybeWrap) { - IrInstructionMaybeWrap *maybe_wrap_instruction = (IrInstructionMaybeWrap *)instruction; + } else if (instruction->id == IrInstructionIdOptionalWrap) { + IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction; slot = &maybe_wrap_instruction->tmp_ptr; } else if (instruction->id == IrInstructionIdErrWrapPayload) { IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction; @@ -6511,7 +6511,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " ComptimeInt: void,\n" " Undefined: void,\n" " Null: void,\n" - " Nullable: Nullable,\n" + " Optional: Optional,\n" " ErrorUnion: ErrorUnion,\n" " ErrorSet: ErrorSet,\n" " Enum: Enum,\n" @@ -6570,7 +6570,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " defs: []Definition,\n" " };\n" "\n" - " pub const Nullable = struct {\n" + " pub const Optional = struct {\n" " child: type,\n" " };\n" "\n" @@ -7145,7 +7145,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdArray: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); return; - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); return; case TypeTableEntryIdFn: @@ -7234,7 +7234,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf)); break; } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; if (child_type->zero_bits) { @@ -7448,7 +7448,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: zig_unreachable(); diff --git a/src/ir.cpp b/src/ir.cpp index 10098f3c32..02606fc4aa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -47,7 +47,7 @@ enum ConstCastResultId { ConstCastResultIdErrSetGlobal, ConstCastResultIdPointerChild, ConstCastResultIdSliceChild, - ConstCastResultIdNullableChild, + ConstCastResultIdOptionalChild, ConstCastResultIdErrorUnionPayload, ConstCastResultIdErrorUnionErrorSet, ConstCastResultIdFnAlign, @@ -86,7 +86,7 @@ struct ConstCastOnly { ConstCastErrSetMismatch error_set; ConstCastOnly *pointer_child; ConstCastOnly *slice_child; - ConstCastOnly *nullable_child; + ConstCastOnly *optional_child; ConstCastOnly *error_union_payload; ConstCastOnly *error_union_error_set; ConstCastOnly *return_type; @@ -372,8 +372,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) { - return IrInstructionIdUnwrapMaybe; +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { + return IrInstructionIdUnwrapOptional; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -524,8 +524,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload return IrInstructionIdUnwrapErrPayload; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionMaybeWrap *) { - return IrInstructionIdMaybeWrap; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalWrap *) { + return IrInstructionIdOptionalWrap; } static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) { @@ -1571,7 +1571,7 @@ static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction * static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, bool safety_check_on) { - IrInstructionUnwrapMaybe *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; instruction->safety_check_on = safety_check_on; @@ -1590,7 +1590,7 @@ static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction * } static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { - IrInstructionMaybeWrap *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionOptionalWrap *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; ir_ref_instruction(value, irb->current_basic_block); @@ -2496,9 +2496,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) { +static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Optional optional) { IrInstructionErrorReturnTrace *instruction = ir_build_instruction(irb, scope, source_node); - instruction->nullable = nullable; + instruction->optional = optional; return &instruction->base; } @@ -3295,9 +3295,9 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); } - IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "MaybeNonNull"); - IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "MaybeNull"); - IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "MaybeEnd"); + IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); + IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); + IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, null_block); @@ -3426,7 +3426,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); - case BinOpTypeUnwrapMaybe: + case BinOpTypeUnwrapOptional: return ir_gen_maybe_ok_or(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); @@ -4703,9 +4703,9 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval); case PrefixOpNegationWrap: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); - case PrefixOpMaybe: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); - case PrefixOpUnwrapMaybe: + case PrefixOpOptional: + return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); + case PrefixOpUnwrapOptional: return ir_gen_maybe_assert_ok(irb, scope, node, lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; @@ -5370,9 +5370,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val); - IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "MaybeThen"); - IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "MaybeElse"); - IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "MaybeEndIf"); + IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); + IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "OptionalElse"); + IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf"); IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -7519,7 +7519,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc } } else if (const_val_fits_in_num_lit(const_val, other_type)) { return true; - } else if (other_type->id == TypeTableEntryIdMaybe) { + } else if (other_type->id == TypeTableEntryIdOptional) { TypeTableEntry *child_type = other_type->data.maybe.child_type; if (const_val_fits_in_num_lit(const_val, child_type)) { return true; @@ -7663,7 +7663,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; // * and [*] can do a const-cast-only to ?* and ?[*], respectively - if (expected_type->id == TypeTableEntryIdMaybe && + if (expected_type->id == TypeTableEntryIdOptional && expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { @@ -7718,12 +7718,12 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // maybe - if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node); if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdNullableChild; - result.data.nullable_child = allocate_nonzero(1); - *result.data.nullable_child = child; + result.id = ConstCastResultIdOptionalChild; + result.data.optional_child = allocate_nonzero(1); + *result.data.optional_child = child; } return result; } @@ -7925,7 +7925,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from ?T to ?U - if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, value); if (res != ImplicitCastMatchResultNo) @@ -7933,7 +7933,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdMaybe) { + if (expected_type->id == TypeTableEntryIdOptional) { ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value); if (res != ImplicitCastMatchResultNo) @@ -7941,7 +7941,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from null literal to maybe type - if (expected_type->id == TypeTableEntryIdMaybe && + if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ImplicitCastMatchResultYes; @@ -7963,7 +7963,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit conversion from T to U!?T if (expected_type->id == TypeTableEntryIdErrorUnion && - expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && + expected_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type->data.maybe.child_type, actual_type, value)) @@ -8072,7 +8072,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit [N]T to ?[]const T - if (expected_type->id == TypeTableEntryIdMaybe && + if (expected_type->id == TypeTableEntryIdOptional && is_slice(expected_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) { @@ -8552,13 +8552,13 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdMaybe && + if (prev_type->id == TypeTableEntryIdOptional && types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } - if (cur_type->id == TypeTableEntryIdMaybe && + if (cur_type->id == TypeTableEntryIdOptional && types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; @@ -8711,7 +8711,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ir_add_error_node(ira, source_node, buf_sprintf("unable to make maybe out of number literal")); return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdMaybe) { + } else if (prev_inst->value.type->id == TypeTableEntryIdOptional) { return prev_inst->value.type; } else { return get_maybe_type(ira->codegen, prev_inst->value.type); @@ -9193,7 +9193,7 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { } static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { - assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(wanted_type->id == TypeTableEntryIdOptional); if (instr_is_comptime(value)) { TypeTableEntry *payload_type = wanted_type->data.maybe.child_type; @@ -9211,7 +9211,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc if (get_codegen_ptr_type(wanted_type) != nullptr) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { - const_instruction->base.value.data.x_nullable = val; + const_instruction->base.value.data.x_optional = val; } const_instruction->base.value.type = wanted_type; return &const_instruction->base; @@ -9219,7 +9219,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value); result->value.type = wanted_type; - result->value.data.rh_maybe = RuntimeHintMaybeNonNull; + result->value.data.rh_maybe = RuntimeHintOptionalNonNull; ir_add_alloca(ira, result, wanted_type); return result; } @@ -9361,7 +9361,7 @@ static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_ } static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { - assert(wanted_type->id == TypeTableEntryIdMaybe); + assert(wanted_type->id == TypeTableEntryIdOptional); assert(instr_is_comptime(value)); ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -9373,7 +9373,7 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; } else { - const_instruction->base.value.data.x_nullable = nullptr; + const_instruction->base.value.data.x_optional = nullptr; } const_instruction->base.value.type = wanted_type; return &const_instruction->base; @@ -9992,7 +9992,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from [N]T to ?[]const N - if (wanted_type->id == TypeTableEntryIdMaybe && + if (wanted_type->id == TypeTableEntryIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) { @@ -10091,7 +10091,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from T to ?T // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism - if (wanted_type->id == TypeTableEntryIdMaybe) { + if (wanted_type->id == TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); @@ -10120,7 +10120,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit cast from null literal to maybe type - if (wanted_type->id == TypeTableEntryIdMaybe && + if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); @@ -10173,8 +10173,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from T to E!?T if (wanted_type->id == TypeTableEntryIdErrorUnion && - wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && - actual_type->id != TypeTableEntryIdMaybe) + wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && + actual_type->id != TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || @@ -10737,13 +10737,13 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { } } -static bool nullable_value_is_null(ConstExprValue *val) { +static bool optional_value_is_null(ConstExprValue *val) { assert(val->special == ConstValSpecialStatic); if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; } else { - return val->data.x_nullable == nullptr; + return val->data.x_optional == nullptr; } } @@ -10755,8 +10755,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); if (is_equality_cmp && - ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdMaybe) || - (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdMaybe) || + ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdOptional) || + (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdOptional) || (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull))) { if (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull) { @@ -10776,7 +10776,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; - bool is_null = nullable_value_is_null(maybe_val); + bool is_null = optional_value_is_null(maybe_val); ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null; return ira->codegen->builtin_types.entry_bool; @@ -10925,7 +10925,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdStruct: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: ir_add_error_node(ira, source_node, @@ -11998,7 +11998,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: @@ -12022,7 +12022,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); @@ -12049,24 +12049,24 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) { static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, IrInstructionErrorReturnTrace *instruction) { - if (instruction->nullable == IrInstructionErrorReturnTrace::Null) { + if (instruction->optional == IrInstructionErrorReturnTrace::Null) { TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); - TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); + TypeTableEntry *optional_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - assert(get_codegen_ptr_type(nullable_type) != nullptr); + assert(get_codegen_ptr_type(optional_type) != nullptr); out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; out_val->data.x_ptr.data.hard_coded_addr.addr = 0; - return nullable_type; + return optional_type; } IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, instruction->nullable); + instruction->base.source_node, instruction->optional); ir_link_new_instruction(new_instruction, &instruction->base); - return nullable_type; + return optional_type; } else { assert(ira->codegen->have_err_ret_tracing); IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, instruction->nullable); + instruction->base.source_node, instruction->optional); ir_link_new_instruction(new_instruction, &instruction->base); return get_ptr_to_stack_trace_type(ira->codegen); } @@ -12998,7 +12998,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -13017,7 +13017,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdUnreachable: case TypeTableEntryIdOpaque: ir_add_error_node(ira, un_op_instruction->base.source_node, - buf_sprintf("type '%s' not nullable", buf_ptr(&type_entry->name))); + buf_sprintf("type '%s' not optional", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } zig_unreachable(); @@ -13109,7 +13109,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio return ir_analyze_negation(ira, un_op_instruction); case IrUnOpDereference: return ir_analyze_dereference(ira, un_op_instruction); - case IrUnOpMaybe: + case IrUnOpOptional: return ir_analyze_maybe(ira, un_op_instruction); } zig_unreachable(); @@ -14155,7 +14155,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; } - } else if (child_type->id == TypeTableEntryIdMaybe) { + } else if (child_type->id == TypeTableEntryIdOptional) { if (buf_eql_str(field_name, "Child")) { bool ptr_is_const = true; bool ptr_is_volatile = false; @@ -14339,7 +14339,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14607,7 +14607,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14715,7 +14715,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14786,7 +14786,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -14810,14 +14810,14 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn TypeTableEntry *type_entry = value->value.type; - if (type_entry->id == TypeTableEntryIdMaybe) { + if (type_entry->id == TypeTableEntryIdOptional) { if (instr_is_comptime(value)) { ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad); if (!maybe_val) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = !nullable_value_is_null(maybe_val); + out_val->data.x_bool = !optional_value_is_null(maybe_val); return ira->codegen->builtin_types.entry_bool; } @@ -14835,7 +14835,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn } static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, - IrInstructionUnwrapMaybe *unwrap_maybe_instruction) + IrInstructionUnwrapOptional *unwrap_maybe_instruction) { IrInstruction *value = unwrap_maybe_instruction->value->other; if (type_is_invalid(value->value.type)) @@ -14863,9 +14863,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile); ir_link_new_instruction(result_instr, &unwrap_maybe_instruction->base); return result_instr->value.type; - } else if (type_entry->id != TypeTableEntryIdMaybe) { + } else if (type_entry->id != TypeTableEntryIdOptional) { ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, - buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name))); + buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->builtin_types.entry_invalid; } TypeTableEntry *child_type = type_entry->data.maybe.child_type; @@ -14881,7 +14881,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val); if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - if (nullable_value_is_null(maybe_val)) { + if (optional_value_is_null(maybe_val)) { ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); return ira->codegen->builtin_types.entry_invalid; } @@ -14891,7 +14891,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, if (type_is_codegen_pointer(child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { - out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_nullable; + out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; } return result_type; } @@ -15216,7 +15216,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: @@ -15737,7 +15737,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdComptimeInt: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdUnion: @@ -16255,11 +16255,11 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop 0, 0); fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { - fn_def_fields[6].data.x_nullable = create_const_vals(1); + fn_def_fields[6].data.x_optional = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); - init_const_slice(ira->codegen, fn_def_fields[6].data.x_nullable, lib_name, 0, buf_len(fn_node->lib_name), true); + init_const_slice(ira->codegen, fn_def_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true); } else { - fn_def_fields[6].data.x_nullable = nullptr; + fn_def_fields[6].data.x_optional = nullptr; } // return_type: type ensure_field_index(fn_def_val->type, "return_type", 7); @@ -16507,11 +16507,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t break; } - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: { result = create_const_vals(1); result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Nullable"); + result->type = ir_type_info_get_type(ira, "Optional"); ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; @@ -16725,10 +16725,10 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) { - inner_fields[1].data.x_nullable = nullptr; + inner_fields[1].data.x_optional = nullptr; } else { - inner_fields[1].data.x_nullable = create_const_vals(1); - make_enum_field_val(inner_fields[1].data.x_nullable, union_field->enum_field, type_info_enum_field_type); + inner_fields[1].data.x_optional = create_const_vals(1); + make_enum_field_val(inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type); } inner_fields[2].special = ConstValSpecialStatic; @@ -16796,13 +16796,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); if (!type_has_bits(struct_field->type_entry)) { - inner_fields[1].data.x_nullable = nullptr; + inner_fields[1].data.x_optional = nullptr; } else { size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index); - inner_fields[1].data.x_nullable = create_const_vals(1); - inner_fields[1].data.x_nullable->special = ConstValSpecialStatic; - inner_fields[1].data.x_nullable->type = ira->codegen->builtin_types.entry_usize; - bigint_init_unsigned(&inner_fields[1].data.x_nullable->data.x_bigint, byte_offset); + inner_fields[1].data.x_optional = create_const_vals(1); + inner_fields[1].data.x_optional->special = ConstValSpecialStatic; + inner_fields[1].data.x_optional->type = ira->codegen->builtin_types.entry_usize; + bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset); } inner_fields[2].special = ConstValSpecialStatic; @@ -18027,7 +18027,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdPromise: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: @@ -18591,7 +18591,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 old_align_bytes = fn_type_id.alignment; fn_type_id.alignment = align_bytes; result_type = get_fn_type(ira->codegen, &fn_type_id); - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) { TypeTableEntry *ptr_type = target_type->data.maybe.child_type; @@ -18599,7 +18599,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); result_type = get_maybe_type(ira->codegen, better_ptr_type); - } else if (target_type->id == TypeTableEntryIdMaybe && + } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdFn) { FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id; @@ -18757,7 +18757,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue return; case TypeTableEntryIdStruct: zig_panic("TODO buf_write_value_bytes struct type"); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case TypeTableEntryIdErrorUnion: zig_panic("TODO buf_write_value_bytes error union"); @@ -18815,7 +18815,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_read_value_bytes array type"); case TypeTableEntryIdStruct: zig_panic("TODO buf_read_value_bytes struct type"); - case TypeTableEntryIdMaybe: + case TypeTableEntryIdOptional: zig_panic("TODO buf_read_value_bytes maybe type"); case TypeTableEntryIdErrorUnion: zig_panic("TODO buf_read_value_bytes error union"); @@ -19731,7 +19731,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: case IrInstructionIdUnionFieldPtr: - case IrInstructionIdMaybeWrap: + case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: @@ -19791,8 +19791,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapMaybe: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction); + case IrInstructionIdUnwrapOptional: + return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -20128,7 +20128,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapMaybe: + case IrInstructionIdUnwrapOptional: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdSwitchVar: @@ -20150,7 +20150,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFrameAddress: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: - case IrInstructionIdMaybeWrap: + case IrInstructionIdOptionalWrap: case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 776ef64566..43907fa9d4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -148,7 +148,7 @@ static const char *ir_un_op_id_str(IrUnOp op_id) { return "-%"; case IrUnOpDereference: return "*"; - case IrUnOpMaybe: + case IrUnOpOptional: return "?"; } zig_unreachable(); @@ -481,7 +481,7 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi fprintf(irp->f, " != null"); } -static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) { +static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { fprintf(irp->f, "&??*"); ir_print_other_instruction(irp, instruction->value); if (!instruction->safety_check_on) { @@ -777,7 +777,7 @@ static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayl } } -static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) { +static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionOptionalWrap *instruction) { fprintf(irp->f, "@maybeWrap("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); @@ -1032,7 +1032,7 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { fprintf(irp->f, "@errorReturnTrace("); - switch (instruction->nullable) { + switch (instruction->optional) { case IrInstructionErrorReturnTrace::Null: fprintf(irp->f, "Null"); break; @@ -1348,8 +1348,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestNonNull: ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); break; - case IrInstructionIdUnwrapMaybe: - ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction); + case IrInstructionIdUnwrapOptional: + ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); @@ -1465,8 +1465,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdUnwrapErrPayload: ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction); break; - case IrInstructionIdMaybeWrap: - ir_print_maybe_wrap(irp, (IrInstructionMaybeWrap *)instruction); + case IrInstructionIdOptionalWrap: + ir_print_maybe_wrap(irp, (IrInstructionOptionalWrap *)instruction); break; case IrInstructionIdErrWrapCode: ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction); diff --git a/src/parser.cpp b/src/parser.cpp index 3ad2de906b..2ee69f81ab 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1046,12 +1046,11 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index } /* -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) +SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) ArrayAccessExpression : token(LBracket) Expression token(RBracket) SliceExpression = "[" Expression ".." option(Expression) "]" FieldAccessExpression : token(Dot) token(Symbol) -PtrDerefExpression = ".*" StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -1148,6 +1147,14 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); node->data.ptr_deref_expr.target = primary_expr; + primary_expr = node; + } else if (token->id == TokenIdQuestion) { + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, first_token); + node->data.prefix_op_expr.prefix_op = PrefixOpUnwrapOptional; + node->data.prefix_op_expr.primary_expr = primary_expr; + primary_expr = node; } else { ast_invalid_token_error(pc, token); @@ -1165,8 +1172,8 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdDash: return PrefixOpNegation; case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; - case TokenIdMaybe: return PrefixOpMaybe; - case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; + case TokenIdQuestion: return PrefixOpOptional; + case TokenIdDoubleQuestion: return PrefixOpUnwrapOptional; case TokenIdAmpersand: return PrefixOpAddrOf; default: return PrefixOpInvalid; } @@ -2304,8 +2311,8 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma } /* -UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression -UnwrapMaybe : "??" BoolOrExpression +UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression +UnwrapOptional : "??" BoolOrExpression UnwrapError = "catch" option("|" Symbol "|") Expression */ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -2322,7 +2329,7 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; + node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional; node->data.bin_op_expr.op2 = rhs; return node; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index badbd695ec..cfabdf11ad 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -625,7 +625,7 @@ void tokenize(Buf *buf, Tokenization *out) { t.state = TokenizeStateSawDot; break; case '?': - begin_token(&t, TokenIdMaybe); + begin_token(&t, TokenIdQuestion); t.state = TokenizeStateSawQuestionMark; break; default: @@ -639,11 +639,6 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; - case '=': - set_token_id(&t, t.cur_tok, TokenIdMaybeAssign); - end_token(&t); - t.state = TokenizeStateStart; - break; default: t.pos -= 1; end_token(&t); @@ -1609,8 +1604,7 @@ const char * token_name(TokenId id) { case TokenIdLBrace: return "{"; case TokenIdLBracket: return "["; case TokenIdLParen: return "("; - case TokenIdMaybe: return "?"; - case TokenIdMaybeAssign: return "?="; + case TokenIdQuestion: return "?"; case TokenIdMinusEq: return "-="; case TokenIdMinusPercent: return "-%"; case TokenIdMinusPercentEq: return "-%="; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index d0089909cd..7c617f85c6 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -100,8 +100,7 @@ enum TokenId { TokenIdLBrace, TokenIdLBracket, TokenIdLParen, - TokenIdMaybe, - TokenIdMaybeAssign, + TokenIdQuestion, TokenIdMinusEq, TokenIdMinusPercent, TokenIdMinusPercentEq, diff --git a/src/translate_c.cpp b/src/translate_c.cpp index d78bd1fa70..aaaf5a1edb 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -382,7 +382,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r fn_def->data.fn_def.fn_proto = fn_proto; fn_proto->data.fn_proto.fn_def_node = fn_def; - AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node); + AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, ref_node); AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr); fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node; @@ -410,7 +410,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r } static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) { - return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child); + return trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, child); } static AstNode *get_global(Context *c, Buf *name) { @@ -879,14 +879,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou } if (qual_type_child_is_fn_proto(child_qt)) { - return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); + return trans_create_node_prefix_op(c, PrefixOpOptional, child_node); } PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown; AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), child_qt.isVolatileQualified(), child_node, ptr_len); - return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); + return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node); } case Type::Typedef: { @@ -1963,7 +1963,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType()); if (is_fn_ptr) return value_node; - AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); + AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, value_node); return trans_create_node_ptr_deref(c, unwrapped); } case UO_Plus: @@ -2587,7 +2587,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * } } if (callee_node == nullptr) { - callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node); + callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, callee_raw_node); } } else { callee_node = callee_raw_node; @@ -4301,7 +4301,7 @@ static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) { return nullptr; if (prefix_node->type != NodeTypePrefixOpExpr) return nullptr; - if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe) + if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpOptional) return nullptr; AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr; diff --git a/std/array_list.zig b/std/array_list.zig index 30715f4d6f..1a235d28a3 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -258,7 +258,7 @@ test "iterator ArrayList test" { } it.reset(); - assert(??it.next() == 1); + assert(it.next().? == 1); } test "insert ArrayList test" { diff --git a/std/buf_map.zig b/std/buf_map.zig index 22d821ae7b..0d4f3a6d5e 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -72,15 +72,15 @@ test "BufMap" { defer bufmap.deinit(); try bufmap.set("x", "1"); - assert(mem.eql(u8, ??bufmap.get("x"), "1")); + assert(mem.eql(u8, bufmap.get("x").?, "1")); assert(1 == bufmap.count()); try bufmap.set("x", "2"); - assert(mem.eql(u8, ??bufmap.get("x"), "2")); + assert(mem.eql(u8, bufmap.get("x").?, "2")); assert(1 == bufmap.count()); try bufmap.set("x", "3"); - assert(mem.eql(u8, ??bufmap.get("x"), "3")); + assert(mem.eql(u8, bufmap.get("x").?, "3")); assert(1 == bufmap.count()); bufmap.delete("x"); diff --git a/std/event.zig b/std/event.zig index 89ab816bb6..0821c789b7 100644 --- a/std/event.zig +++ b/std/event.zig @@ -40,9 +40,9 @@ pub const TcpServer = struct { self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd)); self.accept_coro = try async TcpServer.handler(self); - errdefer cancel ??self.accept_coro; + errdefer cancel self.accept_coro.?; - try self.loop.addFd(self.sockfd, ??self.accept_coro); + try self.loop.addFd(self.sockfd, self.accept_coro.?); errdefer self.loop.removeFd(self.sockfd); } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 3844fbb10a..b52625e26e 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -111,7 +111,7 @@ pub fn formatType( builtin.TypeId.Bool => { return output(context, if (value) "true" else "false"); }, - builtin.TypeId.Nullable => { + builtin.TypeId.Optional => { if (value) |payload| { return formatType(payload, fmt, context, Errors, output); } else { @@ -819,11 +819,11 @@ test "parse unsigned comptime" { test "fmt.format" { { const value: ?i32 = 1234; - try testFmt("nullable: 1234\n", "nullable: {}\n", value); + try testFmt("optional: 1234\n", "optional: {}\n", value); } { const value: ?i32 = null; - try testFmt("nullable: null\n", "nullable: {}\n", value); + try testFmt("optional: null\n", "optional: {}\n", value); } { const value: error!i32 = 1234; diff --git a/std/hash_map.zig b/std/hash_map.zig index a323cdc197..3bd03d4f28 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -265,11 +265,11 @@ test "basic hash map usage" { assert((map.put(4, 44) catch unreachable) == null); assert((map.put(5, 55) catch unreachable) == null); - assert(??(map.put(5, 66) catch unreachable) == 55); - assert(??(map.put(5, 55) catch unreachable) == 66); + assert((map.put(5, 66) catch unreachable).? == 55); + assert((map.put(5, 55) catch unreachable).? == 66); assert(map.contains(2)); - assert((??map.get(2)).value == 22); + assert(map.get(2).?.value == 22); _ = map.remove(2); assert(map.remove(2) == null); assert(map.get(2) == null); @@ -317,7 +317,7 @@ test "iterator hash map" { } it.reset(); - var entry = ??it.next(); + var entry = it.next().?; assert(entry.key == keys[0]); assert(entry.value == values[0]); } diff --git a/std/heap.zig b/std/heap.zig index 5d430bc761..d1fbf9ca0a 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -142,7 +142,7 @@ pub const DirectAllocator = struct { const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; const old_ptr = @intToPtr(*c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); - const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { + const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) ?? blk: { if (new_size > old_mem.len) return error.OutOfMemory; const new_record_addr = old_record_addr - new_size + old_mem.len; @intToPtr(*align(1) usize, new_record_addr).* = root_addr; @@ -171,7 +171,7 @@ pub const DirectAllocator = struct { const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const root_addr = @intToPtr(*align(1) usize, record_addr).*; const ptr = @intToPtr(*c_void, root_addr); - _ = os.windows.HeapFree(??self.heap_handle, 0, ptr); + _ = os.windows.HeapFree(self.heap_handle.?, 0, ptr); }, else => @compileError("Unsupported OS"), } diff --git a/std/json.zig b/std/json.zig index 03b19a7fa4..75ea2eee1c 100644 --- a/std/json.zig +++ b/std/json.zig @@ -908,7 +908,7 @@ pub const TokenStream = struct { }; fn checkNext(p: *TokenStream, id: Token.Id) void { - const token = ??(p.next() catch unreachable); + const token = (p.next() catch unreachable).?; debug.assert(token.id == id); } @@ -1376,17 +1376,17 @@ test "json parser dynamic" { var root = tree.root; - var image = (??root.Object.get("Image")).value; + var image = root.Object.get("Image").?.value; - const width = (??image.Object.get("Width")).value; + const width = image.Object.get("Width").?.value; debug.assert(width.Integer == 800); - const height = (??image.Object.get("Height")).value; + const height = image.Object.get("Height").?.value; debug.assert(height.Integer == 600); - const title = (??image.Object.get("Title")).value; + const title = image.Object.get("Title").?.value; debug.assert(mem.eql(u8, title.String, "View from 15th Floor")); - const animated = (??image.Object.get("Animated")).value; + const animated = image.Object.get("Animated").?.value; debug.assert(animated.Bool == false); } diff --git a/std/linked_list.zig b/std/linked_list.zig index fbc0a0c42a..536c6d24d0 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -270,8 +270,8 @@ test "basic linked list test" { var last = list.pop(); // {2, 3, 4} list.remove(three); // {2, 4} - assert((??list.first).data == 2); - assert((??list.last).data == 4); + assert(list.first.?.data == 2); + assert(list.last.?.data == 4); assert(list.len == 2); } @@ -336,7 +336,7 @@ test "basic intrusive linked list test" { var last = list.pop(); // {2, 3, 4} list.remove(&three.link); // {2, 4} - assert((??list.first).toData().value == 2); - assert((??list.last).toData().value == 4); + assert(list.first.?.toData().value == 2); + assert(list.last.?.toData().value == 4); assert(list.len == 2); } diff --git a/std/macho.zig b/std/macho.zig index d6eef9a325..64f78ae4a3 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -130,7 +130,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable for (syms) |sym| { if (!isSymbol(sym)) continue; const start = sym.n_strx; - const end = ??mem.indexOfScalarPos(u8, strings, start, 0); + const end = mem.indexOfScalarPos(u8, strings, start, 0).?; const name = strings[start..end]; const address = sym.n_value; symbols[nsym] = Symbol{ .name = name, .address = address }; diff --git a/std/mem.zig b/std/mem.zig index 423460e73b..f961c7862b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -304,20 +304,20 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee } test "mem.indexOf" { - assert(??indexOf(u8, "one two three four", "four") == 14); - assert(??lastIndexOf(u8, "one two three two four", "two") == 14); + assert(indexOf(u8, "one two three four", "four").? == 14); + assert(lastIndexOf(u8, "one two three two four", "two").? == 14); assert(indexOf(u8, "one two three four", "gour") == null); assert(lastIndexOf(u8, "one two three four", "gour") == null); - assert(??indexOf(u8, "foo", "foo") == 0); - assert(??lastIndexOf(u8, "foo", "foo") == 0); + assert(indexOf(u8, "foo", "foo").? == 0); + assert(lastIndexOf(u8, "foo", "foo").? == 0); assert(indexOf(u8, "foo", "fool") == null); assert(lastIndexOf(u8, "foo", "lfoo") == null); assert(lastIndexOf(u8, "foo", "fool") == null); - assert(??indexOf(u8, "foo foo", "foo") == 0); - assert(??lastIndexOf(u8, "foo foo", "foo") == 4); - assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6); - assert(??lastIndexOfScalar(u8, "boo", 'o') == 2); + assert(indexOf(u8, "foo foo", "foo").? == 0); + assert(lastIndexOf(u8, "foo foo", "foo").? == 4); + assert(lastIndexOfAny(u8, "boo, cat", "abo").? == 6); + assert(lastIndexOfScalar(u8, "boo", 'o').? == 2); } /// Reads an integer from memory with size equal to bytes.len. @@ -432,9 +432,9 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { test "mem.split" { var it = split(" abc def ghi ", " "); - assert(eql(u8, ??it.next(), "abc")); - assert(eql(u8, ??it.next(), "def")); - assert(eql(u8, ??it.next(), "ghi")); + assert(eql(u8, it.next().?, "abc")); + assert(eql(u8, it.next().?, "def")); + assert(eql(u8, it.next().?, "ghi")); assert(it.next() == null); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 822ade2eb8..1e3a732498 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -156,7 +156,7 @@ pub const ChildProcess = struct { }; } try self.waitUnwrappedWindows(); - return ??self.term; + return self.term.?; } pub fn killPosix(self: *ChildProcess) !Term { @@ -175,7 +175,7 @@ pub const ChildProcess = struct { }; } self.waitUnwrapped(); - return ??self.term; + return self.term.?; } /// Blocks until child process terminates and then cleans up all resources. @@ -212,8 +212,8 @@ pub const ChildProcess = struct { defer Buffer.deinit(&stdout); defer Buffer.deinit(&stderr); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); @@ -232,7 +232,7 @@ pub const ChildProcess = struct { } try self.waitUnwrappedWindows(); - return ??self.term; + return self.term.?; } fn waitPosix(self: *ChildProcess) !Term { @@ -242,7 +242,7 @@ pub const ChildProcess = struct { } self.waitUnwrapped(); - return ??self.term; + return self.term.?; } pub fn deinit(self: *ChildProcess) void { @@ -619,13 +619,13 @@ pub const ChildProcess = struct { self.term = null; if (self.stdin_behavior == StdIo.Pipe) { - os.close(??g_hChildStd_IN_Rd); + os.close(g_hChildStd_IN_Rd.?); } if (self.stderr_behavior == StdIo.Pipe) { - os.close(??g_hChildStd_ERR_Wr); + os.close(g_hChildStd_ERR_Wr.?); } if (self.stdout_behavior == StdIo.Pipe) { - os.close(??g_hChildStd_OUT_Wr); + os.close(g_hChildStd_OUT_Wr.?); } } diff --git a/std/os/index.zig b/std/os/index.zig index fe5ecc38ba..807b2c398b 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -422,7 +422,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: const exe_path = argv[0]; if (mem.indexOfScalar(u8, exe_path, '/') != null) { - return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr))); + return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); } const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin"; @@ -1729,7 +1729,7 @@ test "windows arg parsing" { fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); for (expected_args) |expected_arg| { - const arg = ??it.next(debug.global_allocator) catch unreachable; + const arg = it.next(debug.global_allocator).? catch unreachable; assert(mem.eql(u8, arg, expected_arg)); } assert(it.next(debug.global_allocator) == null); diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 2ab4d0cbc1..1414b8185b 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -67,7 +67,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { if (0 == syms[i].st_shndx) continue; if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { - if (!checkver(??maybe_verdef, versym[i], vername, strings)) + if (!checkver(maybe_verdef.?, versym[i], vername, strings)) continue; } return base + syms[i].st_value; diff --git a/std/os/path.zig b/std/os/path.zig index 4df6179bf5..430dda2934 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -265,7 +265,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool { var it2 = mem.split(ns2, []u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. - return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); + return asciiEqlIgnoreCase(it1.next().?, it2.next().?); } fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool { @@ -286,7 +286,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8 var it2 = mem.split(p2, []u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. - return asciiEqlIgnoreCase(??it1.next(), ??it2.next()) and asciiEqlIgnoreCase(??it1.next(), ??it2.next()); + return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?); }, } } @@ -414,8 +414,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { WindowsPath.Kind.NetworkShare => { result = try allocator.alloc(u8, max_size); var it = mem.split(paths[first_index], "/\\"); - const server_name = ??it.next(); - const other_name = ??it.next(); + const server_name = it.next().?; + const other_name = it.next().?; result[result_index] = '\\'; result_index += 1; diff --git a/std/segmented_list.zig b/std/segmented_list.zig index a2f3607ad8..9f10f4d44a 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -364,7 +364,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { assert(x == 0); } - assert(??list.pop() == 100); + assert(list.pop().? == 100); assert(list.len == 99); try list.pushMany([]i32{ @@ -373,9 +373,9 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { 3, }); assert(list.len == 102); - assert(??list.pop() == 3); - assert(??list.pop() == 2); - assert(??list.pop() == 1); + assert(list.pop().? == 3); + assert(list.pop().? == 2); + assert(list.pop().? == 1); assert(list.len == 99); try list.pushMany([]const i32{}); diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 8aefe4751f..dd37f1edb6 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -54,10 +54,10 @@ fn posixCallMainAndExit() noreturn { const argc = argc_ptr[0]; const argv = @ptrCast([*][*]u8, argc_ptr + 1); - const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); + const envp_optional = @ptrCast([*]?[*]u8, argv + argc + 1); var envp_count: usize = 0; - while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} - const envp = @ptrCast([*][*]u8, envp_nullable)[0..envp_count]; + while (envp_optional[envp_count]) |_| : (envp_count += 1) {} + const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count]; if (builtin.os == builtin.Os.linux) { const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); var i: usize = 0; diff --git a/std/special/builtin.zig b/std/special/builtin.zig index e537078924..e97b0a89e4 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -19,7 +19,7 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 { var index: usize = 0; while (index != n) : (index += 1) - (??dest)[index] = c; + dest.?[index] = c; return dest; } @@ -29,7 +29,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*] var index: usize = 0; while (index != n) : (index += 1) - (??dest)[index] = (??src)[index]; + dest.?[index] = src.?[index]; return dest; } @@ -40,13 +40,13 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 { if (@ptrToInt(dest) < @ptrToInt(src)) { var index: usize = 0; while (index != n) : (index += 1) { - (??dest)[index] = (??src)[index]; + dest.?[index] = src.?[index]; } } else { var index = n; while (index != 0) { index -= 1; - (??dest)[index] = (??src)[index]; + dest.?[index] = src.?[index]; } } diff --git a/std/unicode.zig b/std/unicode.zig index 3d1bebdb55..21ae12f59c 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -286,15 +286,15 @@ fn testUtf8IteratorOnAscii() void { const s = Utf8View.initComptime("abc"); var it1 = s.iterator(); - debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice())); + debug.assert(std.mem.eql(u8, "a", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "b", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "c", it1.nextCodepointSlice().?)); debug.assert(it1.nextCodepointSlice() == null); var it2 = s.iterator(); - debug.assert(??it2.nextCodepoint() == 'a'); - debug.assert(??it2.nextCodepoint() == 'b'); - debug.assert(??it2.nextCodepoint() == 'c'); + debug.assert(it2.nextCodepoint().? == 'a'); + debug.assert(it2.nextCodepoint().? == 'b'); + debug.assert(it2.nextCodepoint().? == 'c'); debug.assert(it2.nextCodepoint() == null); } @@ -321,15 +321,15 @@ fn testUtf8ViewOk() void { const s = Utf8View.initComptime("東京市"); var it1 = s.iterator(); - debug.assert(std.mem.eql(u8, "東", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "京", ??it1.nextCodepointSlice())); - debug.assert(std.mem.eql(u8, "市", ??it1.nextCodepointSlice())); + debug.assert(std.mem.eql(u8, "東", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "京", it1.nextCodepointSlice().?)); + debug.assert(std.mem.eql(u8, "市", it1.nextCodepointSlice().?)); debug.assert(it1.nextCodepointSlice() == null); var it2 = s.iterator(); - debug.assert(??it2.nextCodepoint() == 0x6771); - debug.assert(??it2.nextCodepoint() == 0x4eac); - debug.assert(??it2.nextCodepoint() == 0x5e02); + debug.assert(it2.nextCodepoint().? == 0x6771); + debug.assert(it2.nextCodepoint().? == 0x4eac); + debug.assert(it2.nextCodepoint().? == 0x5e02); debug.assert(it2.nextCodepoint() == null); } diff --git a/std/zig/ast.zig b/std/zig/ast.zig index a4b64d5db2..defaded78a 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1417,7 +1417,7 @@ pub const Node = struct { Range, Sub, SubWrap, - UnwrapMaybe, + UnwrapOptional, }; pub fn iterate(self: *InfixOp, index: usize) ?*Node { @@ -1475,7 +1475,7 @@ pub const Node = struct { Op.Range, Op.Sub, Op.SubWrap, - Op.UnwrapMaybe, + Op.UnwrapOptional, => {}, } @@ -1507,14 +1507,13 @@ pub const Node = struct { BitNot, BoolNot, Cancel, - MaybeType, + OptionalType, Negation, NegationWrap, Resume, PtrType: PtrInfo, SliceType: PtrInfo, Try, - UnwrapMaybe, }; pub const PtrInfo = struct { @@ -1557,12 +1556,12 @@ pub const Node = struct { Op.BitNot, Op.BoolNot, Op.Cancel, - Op.MaybeType, + Op.OptionalType, Op.Negation, Op.NegationWrap, Op.Try, Op.Resume, - Op.UnwrapMaybe, + Op.UnwrapOptional, Op.PointerType, => {}, } @@ -1619,6 +1618,7 @@ pub const Node = struct { ArrayInitializer: InitList, StructInitializer: InitList, Deref, + UnwrapOptional, pub const InitList = SegmentedList(*Node, 2); diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 7faca8e11b..9f8ef3c3d6 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -711,7 +711,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { else => { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_error) { - if ((??tok_it.peek()).id == Token.Id.LBrace) { + if (tok_it.peek().?.id == Token.Id.LBrace) { const error_type_node = try arena.construct(ast.Node.ErrorType{ .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, @@ -1434,8 +1434,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ .id = Token.Id.AngleBracketRight, - .ptr = &??async_node.rangle_bracket, - }, + .ptr = &async_node.rangle_bracket.? }, }); try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } }); continue; @@ -1567,7 +1566,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .bit_range = null, }; // TODO https://github.com/ziglang/zig/issues/1022 - const align_info = &??addr_of_info.align_info; + const align_info = &addr_of_info.align_info.?; try stack.append(State{ .AlignBitRange = align_info }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } }); @@ -1604,7 +1603,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { switch (token.ptr.id) { Token.Id.Colon => { align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined); - const bit_range = &??align_info.bit_range; + const bit_range = &align_info.bit_range.?; try stack.append(State{ .ExpectToken = Token.Id.RParen }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } }); @@ -2144,7 +2143,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.CurlySuffixExpressionEnd => |opt_ctx| { const lhs = opt_ctx.get() ?? continue; - if ((??tok_it.peek()).id == Token.Id.Period) { + if (tok_it.peek().?.id == Token.Id.Period) { const node = try arena.construct(ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, @@ -2326,6 +2325,17 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } + if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| { + const node = try arena.construct(ast.Node.SuffixOp{ + .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, + .lhs = lhs, + .op = ast.Node.SuffixOp.Op.UnwrapOptional, + .rtoken = question_token, + }); + opt_ctx.store(&node.base); + stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; + continue; + } const node = try arena.construct(ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, @@ -2403,7 +2413,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .arrow_token = next_token_index, .return_type = undefined, }; - const return_type_ptr = &((??node.result).return_type); + const return_type_ptr = &node.result.?.return_type; try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } }); continue; }, @@ -2875,7 +2885,7 @@ const OptionalCtx = union(enum) { pub fn get(self: *const OptionalCtx) ?*ast.Node { switch (self.*) { OptionalCtx.Optional => |ptr| return ptr.*, - OptionalCtx.RequiredNull => |ptr| return ??ptr.*, + OptionalCtx.RequiredNull => |ptr| return ptr.*.?, OptionalCtx.Required => |ptr| return ptr.*, } } @@ -3237,7 +3247,7 @@ fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op { fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapMaybe = void{} }, + Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapOptional = void{} }, else => null, }; } @@ -3299,8 +3309,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op { .volatile_token = null, }, }, - Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .MaybeType = void{} }, - Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op{ .UnwrapMaybe = void{} }, + Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .OptionalType = void{} }, Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} }, Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} }, else => null, @@ -3322,7 +3331,7 @@ fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, compti } fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { - const token = ??tok_it.peek(); + const token = tok_it.peek().?; if (token.id == id) { return nextToken(tok_it, tree).index; @@ -3334,7 +3343,7 @@ fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType( fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken { const result = AnnotatedToken{ .index = tok_it.index, - .ptr = ??tok_it.next(), + .ptr = tok_it.next().?, }; assert(result.ptr.id != Token.Id.LineComment); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 91a56de827..ea3a4858b0 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -650,9 +650,10 @@ test "zig fmt: statements with empty line between" { ); } -test "zig fmt: ptr deref operator" { +test "zig fmt: ptr deref operator and unwrap optional operator" { try testCanonical( \\const a = b.*; + \\const a = b.?; \\ ); } @@ -1209,7 +1210,7 @@ test "zig fmt: precedence" { test "zig fmt: prefix operators" { try testCanonical( \\test "prefix operators" { - \\ try return --%~??!*&0; + \\ try return --%~!*&0; \\} \\ ); diff --git a/std/zig/render.zig b/std/zig/render.zig index 7c9b53b77a..0b8e4d1453 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -222,7 +222,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i } } - const value_expr = ??tag.value_expr; + const value_expr = tag.value_expr.?; try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // = try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value, }, @@ -465,8 +465,7 @@ fn renderExpression( ast.Node.PrefixOp.Op.BoolNot, ast.Node.PrefixOp.Op.Negation, ast.Node.PrefixOp.Op.NegationWrap, - ast.Node.PrefixOp.Op.UnwrapMaybe, - ast.Node.PrefixOp.Op.MaybeType, + ast.Node.PrefixOp.Op.OptionalType, ast.Node.PrefixOp.Op.AddressOf, => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); @@ -513,7 +512,7 @@ fn renderExpression( var it = call_info.params.iterator(0); while (true) { - const param_node = ??it.next(); + const param_node = it.next().?; const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: { break :blk indent; @@ -559,10 +558,10 @@ fn renderExpression( return renderToken(tree, stream, rbracket, indent, start_col, space); // ] }, - ast.Node.SuffixOp.Op.Deref => { + ast.Node.SuffixOp.Op.Deref, ast.Node.SuffixOp.Op.UnwrapOptional => { try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // . - return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * + return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * or ? }, @TagType(ast.Node.SuffixOp.Op).Slice => |range| { @@ -595,7 +594,7 @@ fn renderExpression( } if (field_inits.len == 1) blk: { - const field_init = ??field_inits.at(0).*.cast(ast.Node.FieldInitializer); + const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?; if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| { if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) { @@ -688,7 +687,7 @@ fn renderExpression( var count: usize = 1; var it = exprs.iterator(0); while (true) { - const expr = (??it.next()).*; + const expr = it.next().?.*; if (it.peek()) |next_expr| { const expr_last_token = expr.*.lastToken() + 1; const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken()); @@ -806,7 +805,7 @@ fn renderExpression( }, } - return renderExpression(allocator, stream, tree, indent, start_col, ??flow_expr.rhs, space); + return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space); }, ast.Node.Id.Payload => { @@ -1245,7 +1244,7 @@ fn renderExpression( } else { var it = switch_case.items.iterator(0); while (true) { - const node = ??it.next(); + const node = it.next().?; if (it.peek()) |next_node| { try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); @@ -1550,7 +1549,7 @@ fn renderExpression( var it = asm_node.outputs.iterator(0); while (true) { - const asm_output = ??it.next(); + const asm_output = it.next().?; const node = &(asm_output.*).base; if (it.peek()) |next_asm_output| { @@ -1588,7 +1587,7 @@ fn renderExpression( var it = asm_node.inputs.iterator(0); while (true) { - const asm_input = ??it.next(); + const asm_input = it.next().?; const node = &(asm_input.*).base; if (it.peek()) |next_asm_input| { @@ -1620,7 +1619,7 @@ fn renderExpression( var it = asm_node.clobbers.iterator(0); while (true) { - const clobber_token = ??it.next(); + const clobber_token = it.next().?; if (it.peek() == null) { try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline); diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig index a6035d51bb..f93f0ac4d5 100644 --- a/test/cases/bugs/656.zig +++ b/test/cases/bugs/656.zig @@ -9,7 +9,7 @@ const Value = struct { align_expr: ?u32, }; -test "nullable if after an if in a switch prong of a switch with 2 prongs in an else" { +test "optional if after an if in a switch prong of a switch with 2 prongs in an else" { foo(false, true); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index da3cba7d80..a56c470408 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -109,16 +109,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const Self = this; x: u8, fn constConst(p: *const *const Self) u8 { - return (p.*).x; + return p.*.x; } fn maybeConstConst(p: ?*const *const Self) u8 { - return ((??p).*).x; + return p.?.*.x; } fn constConstConst(p: *const *const *const Self) u8 { - return (p.*.*).x; + return p.*.*.x; } fn maybeConstConstConst(p: ?*const *const *const Self) u8 { - return ((??p).*.*).x; + return p.?.*.*.x; } }; const s = S{ .x = 42 }; @@ -177,56 +177,56 @@ test "string literal to &const []const u8" { } test "implicitly cast from T to error!?T" { - castToMaybeTypeError(1); - comptime castToMaybeTypeError(1); + castToOptionalTypeError(1); + comptime castToOptionalTypeError(1); } const A = struct { a: i32, }; -fn castToMaybeTypeError(z: i32) void { +fn castToOptionalTypeError(z: i32) void { const x = i32(1); const y: error!?i32 = x; - assert(??(try y) == 1); + assert((try y).? == 1); const f = z; const g: error!?i32 = f; const a = A{ .a = z }; const b: error!?A = a; - assert((??(b catch unreachable)).a == 1); + assert((b catch unreachable).?.a == 1); } test "implicitly cast from int to error!?T" { - implicitIntLitToMaybe(); - comptime implicitIntLitToMaybe(); + implicitIntLitToOptional(); + comptime implicitIntLitToOptional(); } -fn implicitIntLitToMaybe() void { +fn implicitIntLitToOptional() void { const f: ?i32 = 1; const g: error!?i32 = 1; } test "return null from fn() error!?&T" { - const a = returnNullFromMaybeTypeErrorRef(); - const b = returnNullLitFromMaybeTypeErrorRef(); + const a = returnNullFromOptionalTypeErrorRef(); + const b = returnNullLitFromOptionalTypeErrorRef(); assert((try a) == null and (try b) == null); } -fn returnNullFromMaybeTypeErrorRef() error!?*A { +fn returnNullFromOptionalTypeErrorRef() error!?*A { const a: ?*A = null; return a; } -fn returnNullLitFromMaybeTypeErrorRef() error!?*A { +fn returnNullLitFromOptionalTypeErrorRef() error!?*A { return null; } test "peer type resolution: ?T and T" { - assert(??peerTypeTAndMaybeT(true, false) == 0); - assert(??peerTypeTAndMaybeT(false, false) == 3); + assert(peerTypeTAndOptionalT(true, false).? == 0); + assert(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(??peerTypeTAndMaybeT(true, false) == 0); - assert(??peerTypeTAndMaybeT(false, false) == 3); + assert(peerTypeTAndOptionalT(true, false).? == 0); + assert(peerTypeTAndOptionalT(false, false).? == 3); } } -fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize { +fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { if (c) { return if (b) null else usize(0); } @@ -251,11 +251,11 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, ??castToMaybeSlice(), "hi")); - comptime assert(mem.eql(u8, ??castToMaybeSlice(), "hi")); + assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); } -fn castToMaybeSlice() ?[]const u8 { +fn castToOptionalSlice() ?[]const u8 { return "hi"; } @@ -404,5 +404,5 @@ fn testCastPtrOfArrayToSliceAndPtr() void { test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const window_name = [1][*]const u8{c"window name"}; const x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(??x[0]), "window name")); + assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } diff --git a/test/cases/error.zig b/test/cases/error.zig index ced49419d5..693631fe2d 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -140,7 +140,7 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { if (x) |v| assert(v == 1234) else |err| @compileError("bad"); } -test "syntax: nullable operator in front of error union operator" { +test "syntax: optional operator in front of error union operator" { comptime { assert(?error!i32 == ?(error!i32)); } diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 9612466a86..08d3f3a841 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -12,7 +12,7 @@ fn fibonacci(x: i32) i32 { } fn unwrapAndAddOne(blah: ?i32) i32 { - return ??blah + 1; + return blah.? + 1; } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { diff --git a/test/cases/generics.zig b/test/cases/generics.zig index a76990e2a1..52aa013989 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -127,7 +127,7 @@ test "generic fn with implicit cast" { }) == 0); } fn getByte(ptr: ?*const u8) u8 { - return (??ptr).*; + return ptr.?.*; } fn getFirstByte(comptime T: type, mem: []const T) u8 { return getByte(@ptrCast(*const u8, &mem[0])); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 369d8e5cf3..beb0d6d456 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -505,7 +505,7 @@ test "@typeId" { assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(undefined)) == Tid.Undefined); assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Nullable); + assert(@typeId(?i32) == Tid.Optional); assert(@typeId(error!i32) == Tid.ErrorUnion); assert(@typeId(error) == Tid.ErrorSet); assert(@typeId(AnEnum) == Tid.Enum); diff --git a/test/cases/null.zig b/test/cases/null.zig index bd78990ff4..62565784ac 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -test "nullable type" { +test "optional type" { const x: ?bool = true; if (x) |y| { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(??maybe_bool == false); + assert(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(??foo(1235)); + assert(foo(1235).?); if (foo(null) != null) unreachable; - assert(!??foo(1234)); + assert(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -102,12 +102,12 @@ fn testTestNullRuntime(x: ?i32) void { assert(!(x != null)); } -test "nullable void" { - nullableVoidImpl(); - comptime nullableVoidImpl(); +test "optional void" { + optionalVoidImpl(); + comptime optionalVoidImpl(); } -fn nullableVoidImpl() void { +fn optionalVoidImpl() void { assert(bar(null) == null); assert(bar({}) != null); } @@ -120,19 +120,19 @@ fn bar(x: ?void) ?void { } } -const StructWithNullable = struct { +const StructWithOptional = struct { field: ?i32, }; -var struct_with_nullable: StructWithNullable = undefined; +var struct_with_optional: StructWithOptional = undefined; -test "unwrap nullable which is field of global var" { - struct_with_nullable.field = null; - if (struct_with_nullable.field) |payload| { +test "unwrap optional which is field of global var" { + struct_with_optional.field = null; + if (struct_with_optional.field) |payload| { unreachable; } - struct_with_nullable.field = 1234; - if (struct_with_nullable.field) |payload| { + struct_with_optional.field = 1234; + if (struct_with_optional.field) |payload| { assert(payload == 1234); } else { unreachable; diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 48fcc9ef03..3d3af3c889 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -2,7 +2,7 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; const reflection = this; -test "reflection: array, pointer, nullable, error union type child" { +test "reflection: array, pointer, optional, error union type child" { comptime { assert(([10]u8).Child == u8); assert((*u8).Child == u8); diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index b452c8e9f6..1bc58b14e1 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -88,15 +88,15 @@ fn testArray() void { assert(arr_info.Array.child == bool); } -test "type info: nullable type info" { - testNullable(); - comptime testNullable(); +test "type info: optional type info" { + testOptional(); + comptime testOptional(); } -fn testNullable() void { +fn testOptional() void { const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Nullable); - assert(null_info.Nullable.child == void); + assert(TypeId(null_info) == TypeId.Optional); + assert(null_info.Optional.child == void); } test "type info: promise info" { @@ -168,7 +168,7 @@ fn testUnion() void { assert(typeinfo_info.Union.tag_type == TypeId); assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); - assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); + assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); assert(typeinfo_info.Union.defs.len == 20); diff --git a/test/cases/while.zig b/test/cases/while.zig index a95481668d..fe53522ea6 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -81,7 +81,7 @@ test "while with else" { assert(got_else == 1); } -test "while with nullable as condition" { +test "while with optional as condition" { numbers_left = 10; var sum: i32 = 0; while (getNumberOrNull()) |value| { @@ -90,7 +90,7 @@ test "while with nullable as condition" { assert(sum == 45); } -test "while with nullable as condition with else" { +test "while with optional as condition with else" { numbers_left = 10; var sum: i32 = 0; var got_else: i32 = 0; @@ -132,7 +132,7 @@ fn getNumberOrNull() ?i32 { }; } -test "while on nullable with else result follow else prong" { +test "while on optional with else result follow else prong" { const result = while (returnNull()) |value| { break value; } else @@ -140,8 +140,8 @@ test "while on nullable with else result follow else prong" { assert(result == 2); } -test "while on nullable with else result follow break prong" { - const result = while (returnMaybe(10)) |value| { +test "while on optional with else result follow break prong" { + const result = while (returnOptional(10)) |value| { break value; } else i32(2); @@ -210,7 +210,7 @@ fn testContinueOuter() void { fn returnNull() ?i32 { return null; } -fn returnMaybe(x: i32) ?i32 { +fn returnOptional(x: i32) ?i32 { return x; } fn returnError() error!i32 { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 102c4e428d..1c737a59e7 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1341,7 +1341,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ if (true) |x| { } \\} , - ".tmp_source.zig:2:9: error: expected nullable type, found 'bool'", + ".tmp_source.zig:2:9: error: expected optional type, found 'bool'", ); cases.add( @@ -1780,7 +1780,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "assign null to non-nullable pointer", + "assign null to non-optional pointer", \\const a: *u8 = null; \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } @@ -2817,7 +2817,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "while expected bool, got nullable", + "while expected bool, got optional", \\export fn foo() void { \\ while (bar()) {} \\} @@ -2837,23 +2837,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "while expected nullable, got bool", + "while expected optional, got bool", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() bool { return true; } , - ".tmp_source.zig:2:15: error: expected nullable type, found 'bool'", + ".tmp_source.zig:2:15: error: expected optional type, found 'bool'", ); cases.add( - "while expected nullable, got error union", + "while expected optional, got error union", \\export fn foo() void { \\ while (bar()) |x| {} \\} \\fn bar() error!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'", + ".tmp_source.zig:2:15: error: expected optional type, found 'error!i32'", ); cases.add( @@ -2867,7 +2867,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "while expected error union, got nullable", + "while expected error union, got optional", \\export fn foo() void { \\ while (bar()) |x| {} else |err| {} \\} diff --git a/test/tests.zig b/test/tests.zig index cc562331fe..b66441f628 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -282,8 +282,8 @@ pub const CompareOutputContext = struct { var stdout = Buffer.initNull(b.allocator); var stderr = Buffer.initNull(b.allocator); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; @@ -601,8 +601,8 @@ pub const CompileErrorContext = struct { var stdout_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; @@ -872,8 +872,8 @@ pub const TranslateCContext = struct { var stdout_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator); - var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); - var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); + var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); + var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; -- cgit v1.2.3 From 77678b2cbc7ac9ba2d5d4725241f6a9f7ac64fa4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Jun 2018 01:13:51 -0400 Subject: breaking syntax change: orelse keyword instead of ?? (#1096) use the `zig-fmt-optional-default` branch to have zig fmt automatically do the changes. closes #1023 --- build.zig | 6 +++--- doc/docgen.zig | 6 +++--- doc/langref.html.in | 16 +++++++-------- src-self-hosted/main.zig | 14 ++++++------- src-self-hosted/module.zig | 8 ++++---- src/all_types.hpp | 7 ++++++- src/analyze.cpp | 1 + src/ast_render.cpp | 12 +++++++++-- src/ir.cpp | 31 ++++++++++++----------------- src/parser.cpp | 13 ++++++------ src/tokenizer.cpp | 27 ++++++------------------- src/tokenizer.hpp | 2 +- src/translate_c.cpp | 16 ++++++++------- std/atomic/queue.zig | 4 ++-- std/atomic/stack.zig | 4 ++-- std/buf_map.zig | 6 +++--- std/buf_set.zig | 4 ++-- std/build.zig | 24 +++++++++++----------- std/debug/index.zig | 20 +++++++++---------- std/heap.zig | 10 +++++----- std/linked_list.zig | 4 ++-- std/os/index.zig | 14 ++++++------- std/os/linux/vdso.zig | 8 ++++---- std/os/path.zig | 12 +++++------ std/os/windows/util.zig | 2 +- std/special/build_runner.zig | 10 +++++----- std/unicode.zig | 2 +- std/zig/parse.zig | 47 ++++++++++++++++++++++---------------------- std/zig/render.zig | 8 ++++---- test/cases/cast.zig | 6 +++--- test/cases/null.zig | 10 +++++----- test/compile_errors.zig | 2 +- test/translate_c.zig | 20 +++++++++---------- 33 files changed, 187 insertions(+), 189 deletions(-) (limited to 'src') diff --git a/build.zig b/build.zig index eada37816c..fd154c7504 100644 --- a/build.zig +++ b/build.zig @@ -102,11 +102,11 @@ pub fn build(b: *Builder) !void { b.default_step.dependOn(&exe.step); - const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false; + const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false; if (!skip_self_hosted) { test_step.dependOn(&exe.step); } - const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false; + const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false; exe.setVerboseLink(verbose_link_exe); b.installArtifact(exe); @@ -114,7 +114,7 @@ pub fn build(b: *Builder) !void { installCHeaders(b, c_header_files); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); - const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false; + const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false; test_step.dependOn(docs_step); diff --git a/doc/docgen.zig b/doc/docgen.zig index ed0e1be273..3283d146b0 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -25,13 +25,13 @@ pub fn main() !void { if (!args_it.skip()) @panic("expected self arg"); - const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg")); + const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg")); defer allocator.free(zig_exe); - const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg")); + const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg")); defer allocator.free(in_file_name); - const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg")); + const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg")); defer allocator.free(out_file_name); var in_file = try os.File.openRead(allocator, in_file_name); diff --git a/doc/langref.html.in b/doc/langref.html.in index 4c4a637095..0ada8a5196 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -985,7 +985,7 @@ a ^= b
    -
    a ?? b
    +
    a orelse b
    • {#link|Optionals#}
    • @@ -998,7 +998,7 @@ a ^= b
      const value: ?u32 = null;
      -const unwrapped = value ?? 1234;
      +const unwrapped = value orelse 1234;
       unwrapped == 1234
      @@ -1011,7 +1011,7 @@ unwrapped == 1234 Equivalent to: -
      a ?? unreachable
      +
      a orelse unreachable
      const value: ?u32 = 5678;
      @@ -1278,7 +1278,7 @@ x{} x.* x.?
       == != < > <= >=
       and
       or
      -?? catch
      +orelse catch
       = *= /= %= += -= <<= >>= &= ^= |=
      {#header_close#} {#header_close#} @@ -3062,7 +3062,7 @@ fn createFoo(param: i32) !Foo { // but we want to return it if the function succeeds. errdefer deallocateFoo(foo); - const tmp_buf = allocateTmpBuffer() ?? return error.OutOfMemory; + const tmp_buf = allocateTmpBuffer() orelse return error.OutOfMemory; // tmp_buf is truly a temporary resource, and we for sure want to clean it up // before this block leaves scope defer deallocateTmpBuffer(tmp_buf); @@ -3219,13 +3219,13 @@ struct Foo *do_a_thing(void) { extern fn malloc(size: size_t) ?*u8; fn doAThing() ?*Foo { - const ptr = malloc(1234) ?? return null; + const ptr = malloc(1234) orelse return null; // ... } {#code_end#}

      Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr" - is *u8 not ?*u8. The ?? operator + is *u8 not ?*u8. The orelse keyword unwrapped the optional type and therefore ptr is guaranteed to be non-null everywhere it is used in the function.

      @@ -5941,7 +5941,7 @@ AsmClobbers= ":" list(String, ",") UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression -UnwrapOptional = "??" Expression +UnwrapOptional = "orelse" Expression UnwrapError = "catch" option("|" Symbol "|") Expression diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 64734f077a..1c91ab9cbe 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -212,7 +212,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void { const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig"); defer allocator.free(build_runner_path); - const build_file = flags.single("build-file") ?? "build.zig"; + const build_file = flags.single("build-file") orelse "build.zig"; const build_file_abs = try os.path.resolve(allocator, ".", build_file); defer allocator.free(build_file_abs); @@ -516,7 +516,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const basename = os.path.basename(in_file.?); var it = mem.split(basename, "."); - const root_name = it.next() ?? { + const root_name = it.next() orelse { try stderr.write("file name cannot be empty\n"); os.exit(1); }; @@ -535,7 +535,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const zig_root_source_file = in_file; - const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch { + const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch { os.exit(1); }; defer allocator.free(full_cache_dir); @@ -555,9 +555,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo ); defer module.destroy(); - module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10); - module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10); - module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10); + module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10); + module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10); + module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10); module.is_test = false; @@ -652,7 +652,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo } try module.build(); - try module.link(flags.single("out-file") ?? null); + try module.link(flags.single("out-file") orelse null); if (flags.present("print-timing-info")) { // codegen_print_timing_info(g, stderr); diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index a7ddf3f9e9..575105f25f 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -130,13 +130,13 @@ pub const Module = struct { var name_buffer = try Buffer.init(allocator, name); errdefer name_buffer.deinit(); - const context = c.LLVMContextCreate() ?? return error.OutOfMemory; + const context = c.LLVMContextCreate() orelse return error.OutOfMemory; errdefer c.LLVMContextDispose(context); - const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory; + const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory; errdefer c.LLVMDisposeModule(module); - const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory; + const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory; errdefer c.LLVMDisposeBuilder(builder); const module_ptr = try allocator.create(Module); @@ -223,7 +223,7 @@ pub const Module = struct { c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); } - const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path"); + const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; diff --git a/src/all_types.hpp b/src/all_types.hpp index 2a5a0ad740..ab219e4e56 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -387,6 +387,7 @@ enum NodeType { NodeTypeSliceExpr, NodeTypeFieldAccessExpr, NodeTypePtrDeref, + NodeTypeUnwrapOptional, NodeTypeUse, NodeTypeBoolLiteral, NodeTypeNullLiteral, @@ -575,6 +576,10 @@ struct AstNodeCatchExpr { AstNode *op2; }; +struct AstNodeUnwrapOptional { + AstNode *expr; +}; + enum CastOp { CastOpNoCast, // signifies the function call expression is not a cast CastOpNoop, // fn call expr is a cast, but does nothing @@ -624,7 +629,6 @@ enum PrefixOp { PrefixOpNegation, PrefixOpNegationWrap, PrefixOpOptional, - PrefixOpUnwrapOptional, PrefixOpAddrOf, }; @@ -909,6 +913,7 @@ struct AstNode { AstNodeTestDecl test_decl; AstNodeBinOpExpr bin_op_expr; AstNodeCatchExpr unwrap_err_expr; + AstNodeUnwrapOptional unwrap_optional; AstNodePrefixOpExpr prefix_op_expr; AstNodePointerType pointer_type; AstNodeFnCallExpr fn_call_expr; diff --git a/src/analyze.cpp b/src/analyze.cpp index ed261148ea..0aa5ea5dcb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3308,6 +3308,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypePtrDeref: + case NodeTypeUnwrapOptional: case NodeTypeStructField: case NodeTypeContainerInitExpr: case NodeTypeStructValueField: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2c8c03b226..2ace00885d 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) { case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitOr: return "|="; case BinOpTypeAssignMergeErrorSets: return "||="; - case BinOpTypeUnwrapOptional: return "??"; + case BinOpTypeUnwrapOptional: return "orelse"; case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayMult: return "**"; case BinOpTypeErrorUnion: return "!"; @@ -67,7 +67,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) { case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; case PrefixOpOptional: return "?"; - case PrefixOpUnwrapOptional: return "??"; case PrefixOpAddrOf: return "&"; } zig_unreachable(); @@ -222,6 +221,8 @@ static const char *node_type_str(NodeType node_type) { return "FieldAccessExpr"; case NodeTypePtrDeref: return "PtrDerefExpr"; + case NodeTypeUnwrapOptional: + return "UnwrapOptional"; case NodeTypeContainerDecl: return "ContainerDecl"; case NodeTypeStructField: @@ -711,6 +712,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, ".*"); break; } + case NodeTypeUnwrapOptional: + { + AstNode *lhs = node->data.unwrap_optional.expr; + render_node_ungrouped(ar, lhs); + fprintf(ar->f, ".?"); + break; + } case NodeTypeUndefinedLiteral: fprintf(ar->f, "undefined"); break; diff --git a/src/ir.cpp b/src/ir.cpp index 02606fc4aa..96eb5f7434 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4661,21 +4661,6 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode return ir_build_load_ptr(irb, scope, source_node, payload_ptr); } -static IrInstruction *ir_gen_maybe_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); - if (maybe_ptr == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); - if (lval.is_ptr) - return unwrapped_ptr; - - return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); -} - static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypePrefixOpExpr); AstNode *expr_node = node->data.prefix_op_expr.primary_expr; @@ -4705,8 +4690,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval); case PrefixOpOptional: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); - case PrefixOpUnwrapOptional: - return ir_gen_maybe_assert_ok(irb, scope, node, lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval); @@ -6541,7 +6524,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_load_ptr(irb, scope, node, ptr_instruction); } case NodeTypePtrDeref: { - assert(node->type == NodeTypePtrDeref); AstNode *expr_node = node->data.ptr_deref_expr.target; IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval); if (value == irb->codegen->invalid_instruction) @@ -6549,6 +6531,19 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); } + case NodeTypeUnwrapOptional: { + AstNode *expr_node = node->data.unwrap_optional.expr; + + IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + if (maybe_ptr == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); + if (lval.is_ptr) + return unwrapped_ptr; + + return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + } case NodeTypeThisLiteral: return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); case NodeTypeBoolLiteral: diff --git a/src/parser.cpp b/src/parser.cpp index 2ee69f81ab..adb1633f5d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1151,9 +1151,8 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, } else if (token->id == TokenIdQuestion) { *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, first_token); - node->data.prefix_op_expr.prefix_op = PrefixOpUnwrapOptional; - node->data.prefix_op_expr.primary_expr = primary_expr; + AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token); + node->data.unwrap_optional.expr = primary_expr; primary_expr = node; } else { @@ -1173,7 +1172,6 @@ static PrefixOp tok_to_prefix_op(Token *token) { case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; case TokenIdQuestion: return PrefixOpOptional; - case TokenIdDoubleQuestion: return PrefixOpUnwrapOptional; case TokenIdAmpersand: return PrefixOpAddrOf; default: return PrefixOpInvalid; } @@ -2312,7 +2310,7 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma /* UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression -UnwrapOptional : "??" BoolOrExpression +UnwrapOptional = "orelse" Expression UnwrapError = "catch" option("|" Symbol "|") Expression */ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { @@ -2322,7 +2320,7 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdDoubleQuestion) { + if (token->id == TokenIdKeywordOrElse) { *token_index += 1; AstNode *rhs = ast_parse_expression(pc, token_index, true); @@ -3035,6 +3033,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypePtrDeref: visit_field(&node->data.ptr_deref_expr.target, visit, context); break; + case NodeTypeUnwrapOptional: + visit_field(&node->data.unwrap_optional.expr, visit, context); + break; case NodeTypeUse: visit_field(&node->data.use.expr, visit, context); break; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index cfabdf11ad..2950b4eb49 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = { {"noalias", TokenIdKeywordNoAlias}, {"null", TokenIdKeywordNull}, {"or", TokenIdKeywordOr}, + {"orelse", TokenIdKeywordOrElse}, {"packed", TokenIdKeywordPacked}, {"promise", TokenIdKeywordPromise}, {"pub", TokenIdKeywordPub}, @@ -215,7 +216,6 @@ enum TokenizeState { TokenizeStateSawGreaterThanGreaterThan, TokenizeStateSawDot, TokenizeStateSawDotDot, - TokenizeStateSawQuestionMark, TokenizeStateSawAtSign, TokenizeStateCharCode, TokenizeStateError, @@ -532,6 +532,10 @@ void tokenize(Buf *buf, Tokenization *out) { begin_token(&t, TokenIdComma); end_token(&t); break; + case '?': + begin_token(&t, TokenIdQuestion); + end_token(&t); + break; case '{': begin_token(&t, TokenIdLBrace); end_token(&t); @@ -624,28 +628,10 @@ void tokenize(Buf *buf, Tokenization *out) { begin_token(&t, TokenIdDot); t.state = TokenizeStateSawDot; break; - case '?': - begin_token(&t, TokenIdQuestion); - t.state = TokenizeStateSawQuestionMark; - break; default: invalid_char_error(&t, c); } break; - case TokenizeStateSawQuestionMark: - switch (c) { - case '?': - set_token_id(&t, t.cur_tok, TokenIdDoubleQuestion); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; case TokenizeStateSawDot: switch (c) { case '.': @@ -1480,7 +1466,6 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawGreaterThan: case TokenizeStateSawGreaterThanGreaterThan: case TokenizeStateSawDot: - case TokenizeStateSawQuestionMark: case TokenizeStateSawAtSign: case TokenizeStateSawStarPercent: case TokenizeStateSawPlusPercent: @@ -1545,7 +1530,6 @@ const char * token_name(TokenId id) { case TokenIdDash: return "-"; case TokenIdDivEq: return "/="; case TokenIdDot: return "."; - case TokenIdDoubleQuestion: return "??"; case TokenIdEllipsis2: return ".."; case TokenIdEllipsis3: return "..."; case TokenIdEof: return "EOF"; @@ -1582,6 +1566,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordNoAlias: return "noalias"; case TokenIdKeywordNull: return "null"; case TokenIdKeywordOr: return "or"; + case TokenIdKeywordOrElse: return "orelse"; case TokenIdKeywordPacked: return "packed"; case TokenIdKeywordPromise: return "promise"; case TokenIdKeywordPub: return "pub"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 7c617f85c6..75c7feb476 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -41,7 +41,6 @@ enum TokenId { TokenIdDash, TokenIdDivEq, TokenIdDot, - TokenIdDoubleQuestion, TokenIdEllipsis2, TokenIdEllipsis3, TokenIdEof, @@ -76,6 +75,7 @@ enum TokenId { TokenIdKeywordNoAlias, TokenIdKeywordNull, TokenIdKeywordOr, + TokenIdKeywordOrElse, TokenIdKeywordPacked, TokenIdKeywordPromise, TokenIdKeywordPub, diff --git a/src/translate_c.cpp b/src/translate_c.cpp index aaaf5a1edb..db46d31c5b 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -260,6 +260,12 @@ static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *ch return node; } +static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child_node) { + AstNode *node = trans_create_node(c, NodeTypeUnwrapOptional); + node->data.unwrap_optional.expr = child_node; + return node; +} + static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) { AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); node->data.bin_op_expr.op1 = lhs_node; @@ -382,7 +388,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r fn_def->data.fn_def.fn_proto = fn_proto; fn_proto->data.fn_proto.fn_def_node = fn_def; - AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, ref_node); + AstNode *unwrap_node = trans_create_node_unwrap_null(c, ref_node); AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr); fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node; @@ -409,10 +415,6 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r return fn_def; } -static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) { - return trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, child); -} - static AstNode *get_global(Context *c, Buf *name) { { auto entry = c->global_table.maybe_get(name); @@ -1963,7 +1965,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType()); if (is_fn_ptr) return value_node; - AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, value_node); + AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node); return trans_create_node_ptr_deref(c, unwrapped); } case UO_Plus: @@ -2587,7 +2589,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * } } if (callee_node == nullptr) { - callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapOptional, callee_raw_node); + callee_node = trans_create_node_unwrap_null(c, callee_raw_node); } } else { callee_node = callee_raw_node; diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 142c958173..4f856d9e01 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -33,8 +33,8 @@ pub fn Queue(comptime T: type) type { pub fn get(self: *Self) ?*Node { var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); while (true) { - const node = head.next ?? return null; - head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; + const node = head.next orelse return null; + head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node; } } }; diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 15611188d2..77fa1a9100 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -28,14 +28,14 @@ pub fn Stack(comptime T: type) type { var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); while (true) { node.next = root; - root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; + root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break; } } pub fn pop(self: *Self) ?*Node { var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); while (true) { - root = @cmpxchgWeak(?*Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; + root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root; } } diff --git a/std/buf_map.zig b/std/buf_map.zig index 0d4f3a6d5e..a82d1b731a 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -19,7 +19,7 @@ pub const BufMap = struct { pub fn deinit(self: *const BufMap) void { var it = self.hash_map.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() orelse break; self.free(entry.key); self.free(entry.value); } @@ -37,12 +37,12 @@ pub const BufMap = struct { } pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 { - const entry = self.hash_map.get(key) ?? return null; + const entry = self.hash_map.get(key) orelse return null; return entry.value; } pub fn delete(self: *BufMap, key: []const u8) void { - const entry = self.hash_map.remove(key) ?? return; + const entry = self.hash_map.remove(key) orelse return; self.free(entry.key); self.free(entry.value); } diff --git a/std/buf_set.zig b/std/buf_set.zig index 03a050ed8b..ab2d8e7c34 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -17,7 +17,7 @@ pub const BufSet = struct { pub fn deinit(self: *const BufSet) void { var it = self.hash_map.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() orelse break; self.free(entry.key); } @@ -33,7 +33,7 @@ pub const BufSet = struct { } pub fn delete(self: *BufSet, key: []const u8) void { - const entry = self.hash_map.remove(key) ?? return; + const entry = self.hash_map.remove(key) orelse return; self.free(entry.key); } diff --git a/std/build.zig b/std/build.zig index fed02e0815..5733aec17d 100644 --- a/std/build.zig +++ b/std/build.zig @@ -136,7 +136,7 @@ pub const Builder = struct { } pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { - self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default + self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable; self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable; } @@ -312,9 +312,9 @@ pub const Builder = struct { if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { var it = mem.split(nix_cflags_compile, " "); while (true) { - const word = it.next() ?? break; + const word = it.next() orelse break; if (mem.eql(u8, word, "-isystem")) { - const include_path = it.next() ?? { + const include_path = it.next() orelse { warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n"); break; }; @@ -330,9 +330,9 @@ pub const Builder = struct { if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { var it = mem.split(nix_ldflags, " "); while (true) { - const word = it.next() ?? break; + const word = it.next() orelse break; if (mem.eql(u8, word, "-rpath")) { - const rpath = it.next() ?? { + const rpath = it.next() orelse { warn("Expected argument after -rpath in NIX_LDFLAGS\n"); break; }; @@ -362,7 +362,7 @@ pub const Builder = struct { } self.available_options_list.append(available_option) catch unreachable; - const entry = self.user_input_options.get(name) ?? return null; + const entry = self.user_input_options.get(name) orelse return null; entry.value.used = true; switch (type_id) { TypeId.Bool => switch (entry.value.value) { @@ -416,9 +416,9 @@ pub const Builder = struct { pub fn standardReleaseOptions(self: *Builder) builtin.Mode { if (self.release_mode) |mode| return mode; - const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; - const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false; - const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false; + const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false; + const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false; + const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false; const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: { warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); @@ -518,7 +518,7 @@ pub const Builder = struct { // make sure all args are used var it = self.user_input_options.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() orelse break; if (!entry.value.used) { warn("Invalid option: -D{}\n\n", entry.key); self.markInvalidUserInput(); @@ -1246,7 +1246,7 @@ pub const LibExeObjStep = struct { { var it = self.link_libs.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() orelse break; zig_args.append("--library") catch unreachable; zig_args.append(entry.key) catch unreachable; } @@ -1696,7 +1696,7 @@ pub const TestStep = struct { { var it = self.link_libs.iterator(); while (true) { - const entry = it.next() ?? break; + const entry = it.next() orelse break; try zig_args.append("--library"); try zig_args.append(entry.key); } diff --git a/std/debug/index.zig b/std/debug/index.zig index be47ab76bc..25f7a58b25 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -208,7 +208,7 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us .name = "???", .address = address, }; - const symbol = debug_info.symbol_table.search(address) ?? &unknown; + const symbol = debug_info.symbol_table.search(address) orelse &unknown; try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); }, else => { @@ -268,10 +268,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace { try st.elf.openFile(allocator, &st.self_exe_file); errdefer st.elf.close(); - st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; - st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; - st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; - st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; + st.debug_info = (try st.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo; + st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo; + st.debug_str = (try st.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo; + st.debug_line = (try st.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo; st.debug_ranges = (try st.elf.findSection(".debug_ranges")); try scanAllCompileUnits(st); return st; @@ -443,7 +443,7 @@ const Die = struct { } fn getAttrAddr(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Address => |value| value, else => error.InvalidDebugInfo, @@ -451,7 +451,7 @@ const Die = struct { } fn getAttrSecOffset(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), FormValue.SecOffset => |value| value, @@ -460,7 +460,7 @@ const Die = struct { } fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.Const => |value| value.asUnsignedLe(), else => error.InvalidDebugInfo, @@ -468,7 +468,7 @@ const Die = struct { } fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 { - const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { FormValue.String => |value| value, FormValue.StrPtr => |offset| getString(st, offset), @@ -748,7 +748,7 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) ! var in_file_stream = io.FileInStream.init(in_file); const in_stream = &in_file_stream.stream; const abbrev_code = try readULeb128(in_stream); - const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; + const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; var result = Die{ .tag_id = table_entry.tag_id, diff --git a/std/heap.zig b/std/heap.zig index d1fbf9ca0a..172bc24118 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -97,12 +97,12 @@ pub const DirectAllocator = struct { }, Os.windows => { const amt = n + alignment + @sizeOf(usize); - const heap_handle = self.heap_handle ?? blk: { - const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory; + const heap_handle = self.heap_handle orelse blk: { + const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory; self.heap_handle = hh; break :blk hh; }; - const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) ?? return error.OutOfMemory; + const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); const rem = @rem(root_addr, alignment); const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); @@ -142,7 +142,7 @@ pub const DirectAllocator = struct { const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; const old_ptr = @intToPtr(*c_void, root_addr); const amt = new_size + alignment + @sizeOf(usize); - const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) ?? blk: { + const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) orelse blk: { if (new_size > old_mem.len) return error.OutOfMemory; const new_record_addr = old_record_addr - new_size + old_mem.len; @intToPtr(*align(1) usize, new_record_addr).* = root_addr; @@ -343,7 +343,7 @@ pub const ThreadSafeFixedBufferAllocator = struct { if (new_end_index > self.buffer.len) { return error.OutOfMemory; } - end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index]; + end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index]; } } diff --git a/std/linked_list.zig b/std/linked_list.zig index 536c6d24d0..9e32b7d9da 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -169,7 +169,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Returns: /// A pointer to the last node in the list. pub fn pop(list: *Self) ?*Node { - const last = list.last ?? return null; + const last = list.last orelse return null; list.remove(last); return last; } @@ -179,7 +179,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// Returns: /// A pointer to the first node in the list. pub fn popFirst(list: *Self) ?*Node { - const first = list.first ?? return null; + const first = list.first orelse return null; list.remove(first); return first; } diff --git a/std/os/index.zig b/std/os/index.zig index 807b2c398b..6a13ff94d4 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -425,7 +425,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); } - const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin"; + const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; // PATH.len because it is >= the largest search_path // +1 for the / to join the search path and exe_path // +1 for the null terminating byte @@ -490,7 +490,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { errdefer result.deinit(); if (is_windows) { - const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory; + const ptr = windows.GetEnvironmentStringsA() orelse return error.OutOfMemory; defer assert(windows.FreeEnvironmentStringsA(ptr) != 0); var i: usize = 0; @@ -573,7 +573,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 { return allocator.shrink(u8, buf, result); } } else { - const result = getEnvPosix(key) ?? return error.EnvironmentVariableNotFound; + const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; return mem.dupe(allocator, u8, result); } } @@ -1641,7 +1641,7 @@ pub const ArgIterator = struct { if (builtin.os == Os.windows) { return self.inner.next(allocator); } else { - return mem.dupe(allocator, u8, self.inner.next() ?? return null); + return mem.dupe(allocator, u8, self.inner.next() orelse return null); } } @@ -2457,9 +2457,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread } }; - const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; + const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory; const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); - const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; + const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; @@ -2468,7 +2468,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread outer_context.thread.data.alloc_start = bytes_ptr; const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); - outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { + outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { const err = windows.GetLastError(); return switch (err) { else => os.unexpectedErrorWindows(err), diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 1414b8185b..cbd0cd1df5 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -28,7 +28,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { } } } - const dynv = maybe_dynv ?? return 0; + const dynv = maybe_dynv orelse return 0; if (base == @maxValue(usize)) return 0; var maybe_strings: ?[*]u8 = null; @@ -52,9 +52,9 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { } } - const strings = maybe_strings ?? return 0; - const syms = maybe_syms ?? return 0; - const hashtab = maybe_hashtab ?? return 0; + const strings = maybe_strings orelse return 0; + const syms = maybe_syms orelse return 0; + const hashtab = maybe_hashtab orelse return 0; if (maybe_verdef == null) maybe_versym = null; const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); diff --git a/std/os/path.zig b/std/os/path.zig index 430dda2934..a3ad23b1a9 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -182,8 +182,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { } var it = mem.split(path, []u8{this_sep}); - _ = (it.next() ?? return relative_path); - _ = (it.next() ?? return relative_path); + _ = (it.next() orelse return relative_path); + _ = (it.next() orelse return relative_path); return WindowsPath{ .is_abs = isAbsoluteWindows(path), .kind = WindowsPath.Kind.NetworkShare, @@ -200,8 +200,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { } var it = mem.split(path, []u8{this_sep}); - _ = (it.next() ?? return relative_path); - _ = (it.next() ?? return relative_path); + _ = (it.next() orelse return relative_path); + _ = (it.next() orelse return relative_path); return WindowsPath{ .is_abs = isAbsoluteWindows(path), .kind = WindowsPath.Kind.NetworkShare, @@ -923,7 +923,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) var from_it = mem.split(resolved_from, "/\\"); var to_it = mem.split(resolved_to, "/\\"); while (true) { - const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); + const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest()); const to_rest = to_it.rest(); if (to_it.next()) |to_component| { // TODO ASCII is wrong, we actually need full unicode support to compare paths. @@ -974,7 +974,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![ var from_it = mem.split(resolved_from, "/"); var to_it = mem.split(resolved_to, "/"); while (true) { - const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); + const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest()); const to_rest = to_it.rest(); if (to_it.next()) |to_component| { if (mem.eql(u8, from_component, to_component)) diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 7170346108..f93a673be0 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -153,7 +153,7 @@ pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { const padded_buff = try cstr.addNullByte(allocator, dll_path); defer allocator.free(padded_buff); - return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound; + return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound; } pub fn windowsUnloadDll(hModule: windows.HMODULE) void { diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 3471d6ed21..e4f04df6d0 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -27,15 +27,15 @@ pub fn main() !void { // skip my own exe name _ = arg_it.skip(); - const zig_exe = try unwrapArg(arg_it.next(allocator) ?? { + const zig_exe = try unwrapArg(arg_it.next(allocator) orelse { warn("Expected first argument to be path to zig compiler\n"); return error.InvalidArgs; }); - const build_root = try unwrapArg(arg_it.next(allocator) ?? { + const build_root = try unwrapArg(arg_it.next(allocator) orelse { warn("Expected second argument to be build root directory path\n"); return error.InvalidArgs; }); - const cache_root = try unwrapArg(arg_it.next(allocator) ?? { + const cache_root = try unwrapArg(arg_it.next(allocator) orelse { warn("Expected third argument to be cache root directory path\n"); return error.InvalidArgs; }); @@ -84,12 +84,12 @@ pub fn main() !void { } else if (mem.eql(u8, arg, "--help")) { return usage(&builder, false, try stdout_stream); } else if (mem.eql(u8, arg, "--prefix")) { - prefix = try unwrapArg(arg_it.next(allocator) ?? { + prefix = try unwrapArg(arg_it.next(allocator) orelse { warn("Expected argument after --prefix\n\n"); return usageAndErr(&builder, false, try stderr_stream); }); } else if (mem.eql(u8, arg, "--search-prefix")) { - const search_prefix = try unwrapArg(arg_it.next(allocator) ?? { + const search_prefix = try unwrapArg(arg_it.next(allocator) orelse { warn("Expected argument after --search-prefix\n\n"); return usageAndErr(&builder, false, try stderr_stream); }); diff --git a/std/unicode.zig b/std/unicode.zig index 21ae12f59c..ec808ca4fe 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -220,7 +220,7 @@ const Utf8Iterator = struct { } pub fn nextCodepoint(it: *Utf8Iterator) ?u32 { - const slice = it.nextCodepointSlice() ?? return null; + const slice = it.nextCodepointSlice() orelse return null; switch (slice.len) { 1 => return u32(slice[0]), diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 9f8ef3c3d6..5752f69409 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -43,7 +43,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { // skip over line comments at the top of the file while (true) { - const next_tok = tok_it.peek() ?? break; + const next_tok = tok_it.peek() orelse break; if (next_tok.id != Token.Id.LineComment) break; _ = tok_it.next(); } @@ -197,7 +197,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lib_name_token = nextToken(&tok_it, &tree); const lib_name_token_index = lib_name_token.index; const lib_name_token_ptr = lib_name_token.ptr; - break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? { + break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) orelse { prevToken(&tok_it, &tree); break :blk null; }; @@ -1434,13 +1434,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { try stack.append(State{ .ExpectTokenSave = ExpectTokenSave{ .id = Token.Id.AngleBracketRight, - .ptr = &async_node.rangle_bracket.? }, + .ptr = &async_node.rangle_bracket.?, + }, }); try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } }); continue; }, State.AsyncEnd => |ctx| { - const node = ctx.ctx.get() ?? continue; + const node = ctx.ctx.get() orelse continue; switch (node.id) { ast.Node.Id.FnProto => { @@ -1813,7 +1814,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.RangeExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { const node = try arena.construct(ast.Node.InfixOp{ @@ -1835,7 +1836,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.AssignmentExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -1865,7 +1866,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.UnwrapExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -1900,7 +1901,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.BoolOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { const node = try arena.construct(ast.Node.InfixOp{ @@ -1924,7 +1925,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.BoolAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { const node = try arena.construct(ast.Node.InfixOp{ @@ -1948,7 +1949,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.ComparisonExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -1978,7 +1979,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.BinaryOrExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { const node = try arena.construct(ast.Node.InfixOp{ @@ -2002,7 +2003,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.BinaryXorExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { const node = try arena.construct(ast.Node.InfixOp{ @@ -2026,7 +2027,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.BinaryAndExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { const node = try arena.construct(ast.Node.InfixOp{ @@ -2050,7 +2051,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.BitShiftExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -2080,7 +2081,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.AdditionExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -2110,7 +2111,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.MultiplyExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -2141,7 +2142,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.CurlySuffixExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (tok_it.peek().?.id == Token.Id.Period) { const node = try arena.construct(ast.Node.SuffixOp{ @@ -2189,7 +2190,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.TypeExprEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { const node = try arena.construct(ast.Node.InfixOp{ @@ -2269,7 +2270,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.SuffixOpExpressionEnd => |opt_ctx| { - const lhs = opt_ctx.get() ?? continue; + const lhs = opt_ctx.get() orelse continue; const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -2418,7 +2419,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { - opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable); + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) orelse unreachable); continue; }, Token.Id.LParen => { @@ -2648,7 +2649,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? { + opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) orelse { prevToken(&tok_it, &tree); if (opt_ctx != OptionalCtx.Optional) { ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } }; @@ -3348,7 +3349,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok assert(result.ptr.id != Token.Id.LineComment); while (true) { - const next_tok = tok_it.peek() ?? return result; + const next_tok = tok_it.peek() orelse return result; if (next_tok.id != Token.Id.LineComment) return result; _ = tok_it.next(); } @@ -3356,7 +3357,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void { while (true) { - const prev_tok = tok_it.prev() ?? return; + const prev_tok = tok_it.prev() orelse return; if (prev_tok.id == Token.Id.LineComment) continue; return; } diff --git a/std/zig/render.zig b/std/zig/render.zig index 0b8e4d1453..bc45768fa3 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -83,7 +83,7 @@ fn renderRoot( var start_col: usize = 0; var it = tree.root_node.decls.iterator(0); while (true) { - var decl = (it.next() ?? return).*; + var decl = (it.next() orelse return).*; // look for zig fmt: off comment var start_token_index = decl.firstToken(); zig_fmt_loop: while (start_token_index != 0) { @@ -112,7 +112,7 @@ fn renderRoot( const start = tree.tokens.at(start_token_index + 1).start; try stream.print("{}\n", tree.source[start..end_token.end]); while (tree.tokens.at(decl.firstToken()).start < end_token.end) { - decl = (it.next() ?? return).*; + decl = (it.next() orelse return).*; } break :zig_fmt_loop; } @@ -1993,7 +1993,7 @@ fn renderDocComments( indent: usize, start_col: *usize, ) (@typeOf(stream).Child.Error || Error)!void { - const comment = node.doc_comments ?? return; + const comment = node.doc_comments orelse return; var it = comment.lines.iterator(0); const first_token = node.firstToken(); while (it.next()) |line_token_index| { @@ -2021,7 +2021,7 @@ fn nodeIsBlock(base: *const ast.Node) bool { } fn nodeCausesSliceOpSpace(base: *ast.Node) bool { - const infix_op = base.cast(ast.Node.InfixOp) ?? return false; + const infix_op = base.cast(ast.Node.InfixOp) orelse return false; return switch (infix_op.op) { ast.Node.InfixOp.Op.Period => false, else => true, diff --git a/test/cases/cast.zig b/test/cases/cast.zig index a56c470408..ade1cf78aa 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -73,7 +73,7 @@ fn Struct(comptime T: type) type { fn maybePointer(self: ?*const Self) Self { const none = Self{ .x = if (T == void) void{} else 0 }; - return (self ?? &none).*; + return (self orelse &none).*; } }; } @@ -87,7 +87,7 @@ const Union = union { fn maybePointer(self: ?*const Union) Union { const none = Union{ .x = 0 }; - return (self ?? &none).*; + return (self orelse &none).*; } }; @@ -100,7 +100,7 @@ const Enum = enum { } fn maybePointer(self: ?*const Enum) Enum { - return (self ?? &Enum.None).*; + return (self orelse &Enum.None).*; } }; diff --git a/test/cases/null.zig b/test/cases/null.zig index 62565784ac..cdcfd23efb 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -15,13 +15,13 @@ test "optional type" { const next_x: ?i32 = null; - const z = next_x ?? 1234; + const z = next_x orelse 1234; assert(z == 1234); const final_x: ?i32 = 13; - const num = final_x ?? unreachable; + const num = final_x orelse unreachable; assert(num == 13); } @@ -38,7 +38,7 @@ test "test maybe object and get a pointer to the inner value" { test "rhs maybe unwrap return" { const x: ?bool = true; - const y = x ?? return; + const y = x orelse return; } test "maybe return" { @@ -53,7 +53,7 @@ fn maybeReturnImpl() void { } fn foo(x: ?i32) ?bool { - const value = x ?? return null; + const value = x orelse return null; return value > 1234; } @@ -140,6 +140,6 @@ test "unwrap optional which is field of global var" { } test "null with default unwrap" { - const x: i32 = null ?? 1; + const x: i32 = null orelse 1; assert(x == 1); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1c737a59e7..5ec2759032 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2296,7 +2296,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\ defer try canFail(); \\ - \\ const a = maybeInt() ?? return; + \\ const a = maybeInt() orelse return; \\} \\ \\fn canFail() error!void { } diff --git a/test/translate_c.zig b/test/translate_c.zig index 3489f9da21..417171d2c2 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -246,13 +246,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern var fn_ptr: ?extern fn() void; , \\pub inline fn foo() void { - \\ return (??fn_ptr)(); + \\ return fn_ptr.?(); \\} , \\pub extern var fn_ptr2: ?extern fn(c_int, f32) u8; , \\pub inline fn bar(arg0: c_int, arg1: f32) u8 { - \\ return (??fn_ptr2)(arg0, arg1); + \\ return fn_ptr2.?(arg0, arg1); \\} ); @@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ field: c_int, \\}; \\pub export fn read_field(foo: ?[*]struct_Foo) c_int { - \\ return (??foo).field; + \\ return foo.?.field; \\} ); @@ -969,11 +969,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn bar() void { \\ var f: ?extern fn() void = foo; \\ var b: ?extern fn() c_int = baz; - \\ (??f)(); - \\ (??f)(); + \\ f.?(); + \\ f.?(); \\ foo(); - \\ _ = (??b)(); - \\ _ = (??b)(); + \\ _ = b.?(); + \\ _ = b.?(); \\ _ = baz(); \\} ); @@ -984,7 +984,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , \\pub export fn foo(x: ?[*]c_int) void { - \\ (??x).* = 1; + \\ x.?.* = 1; \\} ); @@ -1012,7 +1012,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn foo() c_int { \\ var x: c_int = 1234; \\ var ptr: ?[*]c_int = &x; - \\ return (??ptr).*; + \\ return ptr.?.*; \\} ); @@ -1119,7 +1119,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const glClearPFN = PFNGLCLEARPROC; , \\pub inline fn glClearUnion(arg0: GLbitfield) void { - \\ return (??glProcs.gl.Clear)(arg0); + \\ return glProcs.gl.Clear.?(arg0); \\} , \\pub const OpenGLProcs = union_OpenGLProcs; -- cgit v1.2.3 From 0a95b0f1ffeb8ae5ee317b02626890adebe5ec63 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Jun 2018 01:18:31 -0400 Subject: std.zig: update syntax for orelse keyword --- src/ir.cpp | 16 ---------------- std/zig/parse.zig | 2 +- std/zig/parser_test.zig | 2 +- std/zig/tokenizer.zig | 27 +++++++-------------------- 4 files changed, 9 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 96eb5f7434..38f4dc90e7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14842,22 +14842,6 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, TypeTableEntry *type_entry = ptr_type->data.pointer.child_type; if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; - } else if (type_entry->id == TypeTableEntryIdMetaType) { - // surprise! actually this is just ??T not an unwrap maybe instruction - ConstExprValue *ptr_val = const_ptr_pointee(ira->codegen, &value->value); - assert(ptr_val->type->id == TypeTableEntryIdMetaType); - TypeTableEntry *child_type = ptr_val->data.x_type; - - type_ensure_zero_bits_known(ira->codegen, child_type); - TypeTableEntry *layer1 = get_maybe_type(ira->codegen, child_type); - TypeTableEntry *layer2 = get_maybe_type(ira->codegen, layer1); - - IrInstruction *const_instr = ir_build_const_type(&ira->new_irb, unwrap_maybe_instruction->base.scope, - unwrap_maybe_instruction->base.source_node, layer2); - IrInstruction *result_instr = ir_get_ref(ira, &unwrap_maybe_instruction->base, const_instr, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile); - ir_link_new_instruction(result_instr, &unwrap_maybe_instruction->base); - return result_instr->value.type; } else if (type_entry->id != TypeTableEntryIdOptional) { ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 5752f69409..877b81c527 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -3248,7 +3248,7 @@ fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op { fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { return switch (id) { Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null }, - Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapOptional = void{} }, + Token.Id.Keyword_orelse => ast.Node.InfixOp.Op{ .UnwrapOptional = void{} }, else => null, }; } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index ea3a4858b0..09ea8aa1a1 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1151,7 +1151,7 @@ test "zig fmt: infix operators" { \\ _ = i!i; \\ _ = i ** i; \\ _ = i ++ i; - \\ _ = i ?? i; + \\ _ = i orelse i; \\ _ = i % i; \\ _ = i / i; \\ _ = i *% i; diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index b288a3adb7..4534529f36 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -39,6 +39,7 @@ pub const Token = struct { Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias }, Keyword{ .bytes = "null", .id = Id.Keyword_null }, Keyword{ .bytes = "or", .id = Id.Keyword_or }, + Keyword{ .bytes = "orelse", .id = Id.Keyword_orelse }, Keyword{ .bytes = "packed", .id = Id.Keyword_packed }, Keyword{ .bytes = "promise", .id = Id.Keyword_promise }, Keyword{ .bytes = "pub", .id = Id.Keyword_pub }, @@ -129,7 +130,6 @@ pub const Token = struct { Ampersand, AmpersandEqual, QuestionMark, - QuestionMarkQuestionMark, AngleBracketLeft, AngleBracketLeftEqual, AngleBracketAngleBracketLeft, @@ -171,6 +171,7 @@ pub const Token = struct { Keyword_noalias, Keyword_null, Keyword_or, + Keyword_orelse, Keyword_packed, Keyword_promise, Keyword_pub, @@ -254,7 +255,6 @@ pub const Tokenizer = struct { Ampersand, Caret, Percent, - QuestionMark, Plus, PlusPercent, AngleBracketLeft, @@ -345,6 +345,11 @@ pub const Tokenizer = struct { self.index += 1; break; }, + '?' => { + result.id = Token.Id.QuestionMark; + self.index += 1; + break; + }, ':' => { result.id = Token.Id.Colon; self.index += 1; @@ -359,9 +364,6 @@ pub const Tokenizer = struct { '+' => { state = State.Plus; }, - '?' => { - state = State.QuestionMark; - }, '<' => { state = State.AngleBracketLeft; }, @@ -496,18 +498,6 @@ pub const Tokenizer = struct { }, }, - State.QuestionMark => switch (c) { - '?' => { - result.id = Token.Id.QuestionMarkQuestionMark; - self.index += 1; - break; - }, - else => { - result.id = Token.Id.QuestionMark; - break; - }, - }, - State.Percent => switch (c) { '=' => { result.id = Token.Id.PercentEqual; @@ -1084,9 +1074,6 @@ pub const Tokenizer = struct { State.Plus => { result.id = Token.Id.Plus; }, - State.QuestionMark => { - result.id = Token.Id.QuestionMark; - }, State.Percent => { result.id = Token.Id.Percent; }, -- cgit v1.2.3 From 03c16c6c548a8f8246c3dcff540482b7612aab80 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Jun 2018 14:58:42 -0400 Subject: implement @tagName as a switch instead of table lookup closes #976 closes #1080 --- src/all_types.hpp | 6 +- src/codegen.cpp | 172 ++++++++++++++++++++++++++++++---------------------- src/ir.cpp | 5 -- test/cases/enum.zig | 9 +++ 4 files changed, 112 insertions(+), 80 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index ab219e4e56..0d364915f9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1091,8 +1091,7 @@ struct TypeTableEntryEnum { bool zero_bits_loop_flag; bool zero_bits_known; - bool generate_name_table; - LLVMValueRef name_table; + LLVMValueRef name_function; HashMap fields_by_name; }; @@ -1411,6 +1410,7 @@ enum PanicMsgId { PanicMsgIdInvalidErrorCode, PanicMsgIdIncorrectAlignment, PanicMsgIdBadUnionField, + PanicMsgIdBadEnumValue, PanicMsgIdCount, }; @@ -1730,8 +1730,6 @@ struct CodeGen { ZigList link_objects; ZigList assembly_files; - ZigList name_table_enums; - Buf *test_filter; Buf *test_name_prefix; diff --git a/src/codegen.cpp b/src/codegen.cpp index da08ecfc9e..d05bcba2ce 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -875,6 +875,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("incorrect alignment"); case PanicMsgIdBadUnionField: return buf_create_from_str("access of inactive union field"); + case PanicMsgIdBadEnumValue: + return buf_create_from_str("invalid enum value"); } zig_unreachable(); } @@ -3516,34 +3518,112 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, ""); } +static LLVMValueRef get_enum_tag_name_function(CodeGen *g, TypeTableEntry *enum_type) { + assert(enum_type->id == TypeTableEntryIdEnum); + if (enum_type->data.enumeration.name_function) + return enum_type->data.enumeration.name_function; + + TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); + TypeTableEntry *u8_slice_type = get_slice_type(g, u8_ptr_type); + TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; + + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(u8_slice_type->type_ref, 0), + &tag_int_type->type_ref, 1, false); + + Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + if (g->build_mode == BuildModeDebug) { + ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); + ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr); + } + + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + FnTableEntry *prev_cur_fn = g->cur_fn; + LLVMValueRef prev_cur_fn_val = g->cur_fn_val; + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + g->cur_fn = nullptr; + g->cur_fn_val = fn_val; + + size_t field_count = enum_type->data.enumeration.src_field_count; + LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); + LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0); + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + + + TypeTableEntry *usize = g->builtin_types.entry_usize; + LLVMValueRef array_ptr_indices[] = { + LLVMConstNull(usize->type_ref), + LLVMConstNull(usize->type_ref), + }; + + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + Buf *name = enum_type->data.enumeration.fields[field_i].name; + LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); + LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); + LLVMSetInitializer(str_global, str_init); + LLVMSetLinkage(str_global, LLVMPrivateLinkage); + LLVMSetGlobalConstant(str_global, true); + LLVMSetUnnamedAddr(str_global, true); + LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init))); + + LLVMValueRef fields[] = { + LLVMConstGEP(str_global, array_ptr_indices, 2), + LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false), + }; + LLVMValueRef slice_init_value = LLVMConstNamedStruct(u8_slice_type->type_ref, fields, 2); + + LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), ""); + LLVMSetInitializer(slice_global, slice_init_value); + LLVMSetLinkage(slice_global, LLVMPrivateLinkage); + LLVMSetGlobalConstant(slice_global, true); + LLVMSetUnnamedAddr(slice_global, true); + LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value))); + + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name"); + LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref, + &enum_type->data.enumeration.fields[field_i].value); + LLVMAddCase(switch_instr, this_tag_int_value, return_block); + + LLVMPositionBuilderAtEnd(g->builder, return_block); + LLVMBuildRet(g->builder, slice_global); + } + + LLVMPositionBuilderAtEnd(g->builder, bad_value_block); + if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) { + gen_safety_crash(g, PanicMsgIdBadEnumValue); + } else { + LLVMBuildUnreachable(g->builder); + } + + g->cur_fn = prev_cur_fn; + g->cur_fn_val = prev_cur_fn_val; + LLVMPositionBuilderAtEnd(g->builder, prev_block); + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + + enum_type->data.enumeration.name_function = fn_val; + return fn_val; +} + static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, IrInstructionTagName *instruction) { TypeTableEntry *enum_type = instruction->target->value.type; assert(enum_type->id == TypeTableEntryIdEnum); - assert(enum_type->data.enumeration.generate_name_table); - TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; - LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); - if (ir_want_runtime_safety(g, &instruction->base)) { - size_t field_count = enum_type->data.enumeration.src_field_count; - - // if the field_count can't fit in the bits of the enum_type, then it can't possibly - // be the wrong value - BigInt field_bi; - bigint_init_unsigned(&field_bi, field_count); - if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) { - LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false); - add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val); - } - } + LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->type_ref), - gen_widen_or_shorten(g, false, tag_int_type, - g->builtin_types.entry_usize, enum_tag_value), - }; - return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, ""); + LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); + return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); } static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable, @@ -5471,55 +5551,6 @@ static void generate_error_name_table(CodeGen *g) { LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init))); } -static void generate_enum_name_tables(CodeGen *g) { - TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0); - TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); - - TypeTableEntry *usize = g->builtin_types.entry_usize; - LLVMValueRef array_ptr_indices[] = { - LLVMConstNull(usize->type_ref), - LLVMConstNull(usize->type_ref), - }; - - - for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) { - TypeTableEntry *enum_type = g->name_table_enums.at(enum_i); - assert(enum_type->id == TypeTableEntryIdEnum); - - size_t field_count = enum_type->data.enumeration.src_field_count; - LLVMValueRef *values = allocate(field_count); - for (size_t field_i = 0; field_i < field_count; field_i += 1) { - Buf *name = enum_type->data.enumeration.fields[field_i].name; - - LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); - LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); - LLVMSetInitializer(str_global, str_init); - LLVMSetLinkage(str_global, LLVMPrivateLinkage); - LLVMSetGlobalConstant(str_global, true); - LLVMSetUnnamedAddr(str_global, true); - LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init))); - - LLVMValueRef fields[] = { - LLVMConstGEP(str_global, array_ptr_indices, 2), - LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false), - }; - values[field_i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2); - } - - LLVMValueRef name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)field_count); - - Buf *table_name = get_mangled_name(g, buf_sprintf("%s_name_table", buf_ptr(&enum_type->name)), false); - LLVMValueRef name_table = LLVMAddGlobal(g->module, LLVMTypeOf(name_table_init), buf_ptr(table_name)); - LLVMSetInitializer(name_table, name_table_init); - LLVMSetLinkage(name_table, LLVMPrivateLinkage); - LLVMSetGlobalConstant(name_table, true); - LLVMSetUnnamedAddr(name_table, true); - LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init))); - enum_type->data.enumeration.name_table = name_table; - } -} - static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) { IrExecutable *executable = &fn->analyzed_executable; assert(executable->basic_block_list.length > 0); @@ -5616,7 +5647,6 @@ static void do_code_gen(CodeGen *g) { } generate_error_name_table(g); - generate_enum_name_tables(g); // Generate module level variables for (size_t i = 0; i < g->global_vars.length; i += 1) { diff --git a/src/ir.cpp b/src/ir.cpp index 38f4dc90e7..4b6d5fdcf1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15837,11 +15837,6 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn return out_val->type; } - if (!target->value.type->data.enumeration.generate_name_table) { - target->value.type->data.enumeration.generate_name_table = true; - ira->codegen->name_table_enums.append(target->value.type); - } - IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope, instruction->base.source_node, target); ir_link_new_instruction(result, &instruction->base); diff --git a/test/cases/enum.zig b/test/cases/enum.zig index ae9f04869b..5c78d73092 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -883,3 +883,12 @@ test "empty extern enum with members" { }; assert(@sizeOf(E) == @sizeOf(c_int)); } + +test "aoeu" { + const LocalFoo = enum { + A = 1, + B = 0, + }; + var b = LocalFoo.B; + assert(mem.eql(u8, @tagName(b), "B")); +} -- cgit v1.2.3 From 259413251df478545948fdbc6213669f88f584bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Jun 2018 15:06:02 -0400 Subject: fix ability to call mutating methods on zero size structs closes #838 --- src/ir.cpp | 37 ++++++++++++++++++++++++++++++++++++- test/cases/struct.zig | 17 +++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 4b6d5fdcf1..4cebc488b8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8151,6 +8151,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } } + // implicit T to *T where T is zero bits + if (expected_type->id == TypeTableEntryIdPointer && expected_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, expected_type->data.pointer.child_type, + actual_type, source_node).id == ConstCastResultIdOk) + { + type_ensure_zero_bits_known(ira->codegen, actual_type); + if (!type_has_bits(actual_type)) { + return ImplicitCastMatchResultYes; + } + } + // implicit undefined literal to anything if (actual_type->id == TypeTableEntryIdUndefined) { return ImplicitCastMatchResultYes; @@ -8820,7 +8831,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca) { - if (value->value.special != ConstValSpecialRuntime && + if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice) { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, @@ -9382,9 +9393,19 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi 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; } + + type_ensure_zero_bits_known(ira->codegen, value->value.type); + if (type_is_invalid(value->value.type)) { + return ira->codegen->invalid_instruction; + } + + if (!type_has_bits(value->value.type)) { + return load_ptr_inst->ptr; + } } if (instr_is_comptime(value)) { @@ -10340,6 +10361,20 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // explicit cast from T to *T where T is zero bits + if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type, source_node).id == ConstCastResultIdOk) + { + type_ensure_zero_bits_known(ira->codegen, actual_type); + if (type_is_invalid(actual_type)) { + return ira->codegen->invalid_instruction; + } + if (!type_has_bits(actual_type)) { + return ir_get_ref(ira, source_instr, value, false, false); + } + } + // explicit cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefined) { diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 6f7d44e09b..6952611a8c 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -421,3 +421,20 @@ const Expr = union(enum) { fn alloc(comptime T: type) []T { return []T{}; } + +test "call method with mutable reference to struct with no fields" { + const S = struct { + fn doC(s: *const this) bool { + return true; + } + fn do(s: *this) bool { + return true; + } + }; + + var s = S{}; + assert(S.doC(&s)); + assert(s.doC()); + assert(S.do(&s)); + assert(s.do()); +} -- cgit v1.2.3 From 8dd24796c43b5241a5dcd5508e4be00483ebc25b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 13 Jun 2018 11:04:09 -0400 Subject: disallow implicit casts that break rules for optionals closes #1102 --- src/ir.cpp | 332 +++++++++++++++++++++++++++--------------------- test/compile_errors.zig | 13 ++ 2 files changed, 197 insertions(+), 148 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 4cebc488b8..e5e8dcbb9d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7647,23 +7647,24 @@ static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry } -static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type, - TypeTableEntry *actual_type, AstNode *source_node) +static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *wanted_type, + TypeTableEntry *actual_type, AstNode *source_node, bool wanted_is_mutable) { CodeGen *g = ira->codegen; ConstCastOnly result = {}; result.id = ConstCastResultIdOk; - if (expected_type == actual_type) + if (wanted_type == actual_type) return result; // * and [*] can do a const-cast-only to ?* and ?[*], respectively - if (expected_type->id == TypeTableEntryIdOptional && - expected_type->data.maybe.child_type->id == TypeTableEntryIdPointer && + // but not if there is a mutable parent pointer + if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional && + wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { ConstCastOnly child = types_match_const_cast_only(ira, - expected_type->data.maybe.child_type, actual_type, source_node); + wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdNullWrapPtr; result.data.null_wrap_ptr_child = allocate_nonzero(1); @@ -7673,16 +7674,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // pointer const - if (expected_type->id == TypeTableEntryIdPointer && + if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer && - (actual_type->data.pointer.ptr_len == expected_type->data.pointer.ptr_len) && - (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && - actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && - actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && - actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) + (actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && + actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && + actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && + actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment) { - ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node); + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; result.data.pointer_child = allocate_nonzero(1); @@ -7692,17 +7694,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // slice const - if (is_slice(expected_type) && is_slice(actual_type)) { + if (is_slice(wanted_type) && is_slice(actual_type)) { TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; - TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; - if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) && - (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) && - actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset && - actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && - actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) + TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; + if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && + (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && + actual_ptr_type->data.pointer.bit_offset == wanted_ptr_type->data.pointer.bit_offset && + actual_ptr_type->data.pointer.unaligned_bit_count == wanted_ptr_type->data.pointer.unaligned_bit_count && + actual_ptr_type->data.pointer.alignment >= wanted_ptr_type->data.pointer.alignment) { - ConstCastOnly child = types_match_const_cast_only(ira, expected_ptr_type->data.pointer.child_type, - actual_ptr_type->data.pointer.child_type, source_node); + ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, + actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdSliceChild; result.data.slice_child = allocate_nonzero(1); @@ -7713,8 +7715,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // maybe - if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { - ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node); + if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, + actual_type->data.maybe.child_type, source_node, wanted_is_mutable); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdOptionalChild; result.data.optional_child = allocate_nonzero(1); @@ -7724,15 +7727,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // error union - if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { - ConstCastOnly payload_child = types_match_const_cast_only(ira, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type, source_node); + if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { + ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, + actual_type->data.error_union.payload_type, source_node, wanted_is_mutable); if (payload_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionPayload; result.data.error_union_payload = allocate_nonzero(1); *result.data.error_union_payload = payload_child; return result; } - ConstCastOnly error_set_child = types_match_const_cast_only(ira, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node); + ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type, + actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable); if (error_set_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionErrorSet; result.data.error_union_error_set = allocate_nonzero(1); @@ -7743,9 +7748,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // error set - if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { + if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { TypeTableEntry *contained_set = actual_type; - TypeTableEntry *container_set = expected_type; + TypeTableEntry *container_set = wanted_type; // if the container set is inferred, then this will always work. if (container_set->data.error_set.infer_fn != nullptr) { @@ -7786,36 +7791,37 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; } - if (expected_type == ira->codegen->builtin_types.entry_promise && + if (wanted_type == ira->codegen->builtin_types.entry_promise && actual_type->id == TypeTableEntryIdPromise) { return result; } // fn - if (expected_type->id == TypeTableEntryIdFn && + if (wanted_type->id == TypeTableEntryIdFn && actual_type->id == TypeTableEntryIdFn) { - if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { + if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { result.id = ConstCastResultIdFnAlign; return result; } - if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { + if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { result.id = ConstCastResultIdFnCC; return result; } - if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { + if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { result.id = ConstCastResultIdFnVarArgs; return result; } - if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { + if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) { result.id = ConstCastResultIdFnIsGeneric; return result; } - if (!expected_type->data.fn.is_generic && + if (!wanted_type->data.fn.is_generic && actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable) { - ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node); + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type, + actual_type->data.fn.fn_type_id.return_type, source_node, false); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnReturnType; result.data.return_type = allocate_nonzero(1); @@ -7823,9 +7829,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; } } - if (!expected_type->data.fn.is_generic && expected_type->data.fn.fn_type_id.cc == CallingConventionAsync) { - ConstCastOnly child = types_match_const_cast_only(ira, actual_type->data.fn.fn_type_id.async_allocator_type, - expected_type->data.fn.fn_type_id.async_allocator_type, source_node); + if (!wanted_type->data.fn.is_generic && wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync) { + ConstCastOnly child = types_match_const_cast_only(ira, + actual_type->data.fn.fn_type_id.async_allocator_type, + wanted_type->data.fn.fn_type_id.async_allocator_type, + source_node, false); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdAsyncAllocatorType; result.data.async_allocator_type = allocate_nonzero(1); @@ -7833,22 +7841,23 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; } } - if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { + if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { result.id = ConstCastResultIdFnArgCount; return result; } - if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { + if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { result.id = ConstCastResultIdFnGenericArgCount; return result; } - assert(expected_type->data.fn.is_generic || - expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); - for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) { + assert(wanted_type->data.fn.is_generic || + wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count); + for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.next_param_index; i += 1) { // note it's reversed for parameters FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; - FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; + FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i]; - ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node); + ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, + expected_param_info->type, source_node, false); if (arg_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdFnArg; result.data.fn_arg.arg_index = i; @@ -7876,11 +7885,12 @@ enum ImplicitCastMatchResult { ImplicitCastMatchResultReportedError, }; -static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, +static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *wanted_type, TypeTableEntry *actual_type, IrInstruction *value) { AstNode *source_node = value->source_node; - ConstCastOnly const_cast_result = types_match_const_cast_only(ira, expected_type, actual_type, source_node); + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, + source_node, false); if (const_cast_result.id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -7895,21 +7905,21 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors; } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) { ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); add_error_note(ira->codegen, msg, value->source_node, buf_sprintf("unable to cast global error set into smaller set")); return ImplicitCastMatchResultReportedError; } } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) { ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); add_error_note(ira->codegen, msg, value->source_node, buf_sprintf("unable to cast global error set into smaller set")); return ImplicitCastMatchResultReportedError; } if (missing_errors != nullptr) { ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); for (size_t i = 0; i < missing_errors->length; i += 1) { ErrorTableEntry *error_entry = missing_errors->at(i); add_error_note(ira->codegen, msg, error_entry->decl_node, @@ -7920,162 +7930,168 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit conversion from ?T to ?U - if (expected_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { - ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, + if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { + ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type, actual_type->data.maybe.child_type, value); if (res != ImplicitCastMatchResultNo) return res; } // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdOptional) { - ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, + if (wanted_type->id == TypeTableEntryIdOptional) { + ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type, actual_type, value); if (res != ImplicitCastMatchResultNo) return res; } // implicit conversion from null literal to maybe type - if (expected_type->id == TypeTableEntryIdOptional && + if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ImplicitCastMatchResultYes; } // implicit T to U!T - if (expected_type->id == TypeTableEntryIdErrorUnion && - ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type, actual_type, value)) + if (wanted_type->id == TypeTableEntryIdErrorUnion && + ir_types_match_with_implicit_cast(ira, wanted_type->data.error_union.payload_type, actual_type, value)) { return ImplicitCastMatchResultYes; } // implicit conversion from error set to error union type - if (expected_type->id == TypeTableEntryIdErrorUnion && + if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorSet) { return ImplicitCastMatchResultYes; } // implicit conversion from T to U!?T - if (expected_type->id == TypeTableEntryIdErrorUnion && - expected_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && + if (wanted_type->id == TypeTableEntryIdErrorUnion && + wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && ir_types_match_with_implicit_cast(ira, - expected_type->data.error_union.payload_type->data.maybe.child_type, + wanted_type->data.error_union.payload_type->data.maybe.child_type, actual_type, value)) { return ImplicitCastMatchResultYes; } // implicit widening conversion - if (expected_type->id == TypeTableEntryIdInt && + if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && - expected_type->data.integral.is_signed == actual_type->data.integral.is_signed && - expected_type->data.integral.bit_count >= actual_type->data.integral.bit_count) + wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) { return ImplicitCastMatchResultYes; } // small enough unsigned ints can get casted to large enough signed ints - if (expected_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed && + if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && - expected_type->data.integral.bit_count > actual_type->data.integral.bit_count) + wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) { return ImplicitCastMatchResultYes; } // implicit float widening conversion - if (expected_type->id == TypeTableEntryIdFloat && + if (wanted_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdFloat && - expected_type->data.floating.bit_count >= actual_type->data.floating.bit_count) + wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) { return ImplicitCastMatchResultYes; } // implicit [N]T to []const T - if (is_slice(expected_type) && actual_type->id == TypeTableEntryIdArray) { - TypeTableEntry *ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + 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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } // implicit &const [N]T to []const T - if (is_slice(expected_type) && + if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.is_const && actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) { - TypeTableEntry *ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } // implicit [N]T to &const []const T - if (expected_type->id == TypeTableEntryIdPointer && - expected_type->data.pointer.is_const && - expected_type->data.pointer.ptr_len == PtrLenSingle && - is_slice(expected_type->data.pointer.child_type) && + if (wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.is_const && + wanted_type->data.pointer.ptr_len == PtrLenSingle && + is_slice(wanted_type->data.pointer.child_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = - expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; + wanted_type->data.pointer.child_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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, + actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } // implicit *[N]T to [*]T - if (expected_type->id == TypeTableEntryIdPointer && - expected_type->data.pointer.ptr_len == PtrLenUnknown && + if (wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenUnknown && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && - types_match_const_cast_only(ira, expected_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } // implicit *[N]T to []T - if (is_slice(expected_type) && + if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) { - TypeTableEntry *slice_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(slice_ptr_type->id == TypeTableEntryIdPointer); if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } // implicit [N]T to ?[]const T - if (expected_type->id == TypeTableEntryIdOptional && - is_slice(expected_type->data.maybe.child_type) && + if (wanted_type->id == TypeTableEntryIdOptional && + is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = - expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; + wanted_type->data.maybe.child_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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, + actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -8087,16 +8103,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, if (actual_type->id == TypeTableEntryIdComptimeFloat || actual_type->id == TypeTableEntryIdComptimeInt) { - if (expected_type->id == TypeTableEntryIdPointer && - expected_type->data.pointer.ptr_len == PtrLenSingle && - expected_type->data.pointer.is_const) + if (wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenSingle && + wanted_type->data.pointer.is_const) { - if (ir_num_lit_fits_in_other_type(ira, value, expected_type->data.pointer.child_type, false)) { + if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.pointer.child_type, false)) { return ImplicitCastMatchResultYes; } else { return ImplicitCastMatchResultReportedError; } - } else if (ir_num_lit_fits_in_other_type(ira, value, expected_type, false)) { + } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, false)) { return ImplicitCastMatchResultYes; } else { return ImplicitCastMatchResultReportedError; @@ -8106,41 +8122,41 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicit typed number to integer or float literal. // works when the number is known if (value->value.special == ConstValSpecialStatic) { - if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdComptimeInt) { + if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) { return ImplicitCastMatchResultYes; - } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdComptimeFloat) { + } else if (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat) { return ImplicitCastMatchResultYes; } } // implicit union to its enum tag type - if (expected_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion && + if (wanted_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion && (actual_type->data.unionation.decl_node->data.container_decl.auto_enum || actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { type_ensure_zero_bits_known(ira->codegen, actual_type); - if (actual_type->data.unionation.tag_type == expected_type) { + if (actual_type->data.unionation.tag_type == wanted_type) { return ImplicitCastMatchResultYes; } } // implicit enum to union which has the enum as the tag type - if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && - (expected_type->data.unionation.decl_node->data.container_decl.auto_enum || - expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && + (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || + wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) { - type_ensure_zero_bits_known(ira->codegen, expected_type); - if (expected_type->data.unionation.tag_type == actual_type) { + type_ensure_zero_bits_known(ira->codegen, wanted_type); + if (wanted_type->data.unionation.tag_type == actual_type) { return ImplicitCastMatchResultYes; } } // implicit enum to &const union which has the enum as the tag type if (actual_type->id == TypeTableEntryIdEnum && - expected_type->id == TypeTableEntryIdPointer && - expected_type->data.pointer.ptr_len == PtrLenSingle) + wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenSingle) { - TypeTableEntry *union_type = expected_type->data.pointer.child_type; + TypeTableEntry *union_type = wanted_type->data.pointer.child_type; if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) { @@ -8152,9 +8168,9 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // implicit T to *T where T is zero bits - if (expected_type->id == TypeTableEntryIdPointer && expected_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, expected_type->data.pointer.child_type, - actual_type, source_node).id == ConstCastResultIdOk) + if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type, source_node, false).id == ConstCastResultIdOk) { type_ensure_zero_bits_known(ira->codegen, actual_type); if (!type_has_bits(actual_type)) { @@ -8170,10 +8186,10 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (expected_type->id == TypeTableEntryIdPointer && - expected_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, expected_type, const_ptr_actual, - source_node).id == ConstCastResultIdOk) + if (wanted_type->id == TypeTableEntryIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, wanted_type, const_ptr_actual, + source_node, false).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -8415,9 +8431,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod TypeTableEntry *cur_payload_type = cur_type->data.error_union.payload_type; bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type, - source_node).id == ConstCastResultIdOk; + source_node, false).id == ConstCastResultIdOk; bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type, - source_node).id == ConstCastResultIdOk; + source_node, false).id == ConstCastResultIdOk; if (const_cast_prev || const_cast_cur) { if (const_cast_cur) { @@ -8504,11 +8520,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) { continue; } - if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } @@ -8531,13 +8547,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (prev_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, + source_node, false).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, + source_node, false).id == ConstCastResultIdOk) { if (err_set_type != nullptr) { TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; @@ -8559,13 +8577,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (prev_type->id == TypeTableEntryIdOptional && - types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, + source_node, false).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdOptional && - types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, + source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; @@ -8602,8 +8622,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && - cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + cur_type->data.array.len != prev_type->data.array.len && + types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { convert_to_const_slice = true; prev_inst = cur_inst; @@ -8611,8 +8632,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && - cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + cur_type->data.array.len != prev_type->data.array.len && + types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { convert_to_const_slice = true; continue; @@ -8621,8 +8643,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) && (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && - types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, + prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { convert_to_const_slice = false; continue; @@ -8631,8 +8654,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) && (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && - types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, + cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) { prev_inst = cur_inst; convert_to_const_slice = false; @@ -9913,7 +9937,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // explicit match or non-const to const - if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -9959,7 +9983,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst 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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -9977,7 +10002,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -9993,7 +10019,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.pointer.child_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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -10016,7 +10043,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.maybe.child_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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -10084,7 +10112,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); } @@ -10098,7 +10127,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(slice_ptr_type->id == TypeTableEntryIdPointer); if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) { return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type); } @@ -10109,7 +10139,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism if (wanted_type->id == TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; - if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, + false).id == ConstCastResultIdOk) + { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdComptimeInt || actual_type->id == TypeTableEntryIdComptimeFloat) @@ -10144,7 +10176,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { - if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, + source_node, false).id == ConstCastResultIdOk) + { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdComptimeInt || actual_type->id == TypeTableEntryIdComptimeFloat) @@ -10166,7 +10200,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.error_union.payload_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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, + source_node, false).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -10193,7 +10228,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id != TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; - if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || + if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk || actual_type->id == TypeTableEntryIdNull || actual_type->id == TypeTableEntryIdComptimeInt || actual_type->id == TypeTableEntryIdComptimeFloat) @@ -10345,7 +10380,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *array_type = wanted_type->data.pointer.child_type; if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 && types_match_const_cast_only(ira, array_type->data.array.child_type, - actual_type->data.pointer.child_type, source_node).id == ConstCastResultIdOk) + actual_type->data.pointer.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); @@ -10364,7 +10400,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from T to *T where T is zero bits if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type, source_node).id == ConstCastResultIdOk) + actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) { type_ensure_zero_bits_known(ira->codegen, actual_type); if (type_is_invalid(actual_type)) { @@ -10384,7 +10420,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5ec2759032..06f17a37ee 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "use implicit casts to assign null to non-nullable pointer", + \\export fn entry() void { + \\ var x: i32 = 1234; + \\ var p: *i32 = &x; + \\ var pp: *?*i32 = &p; + \\ pp.* = null; + \\ var y = p.*; + \\} + , + ".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'", + ); + cases.add( "attempted implicit cast from T to [*]const T", \\export fn entry() void { -- cgit v1.2.3 From 4ec09ac243afa0b784669e618ec09e9e444a0275 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 14 Jun 2018 17:57:28 +0300 Subject: Enabled optional types of zero bit types with no LLVM DI type. (#1110) * Zero bit optional types do not need a LLVM DI type --- src/analyze.cpp | 3 ++- test/cases/null.zig | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 0aa5ea5dcb..cbeac7bc21 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -522,7 +522,6 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional); assert(child_type->type_ref || child_type->zero_bits); - assert(child_type->di_type); entry->is_copyable = type_is_copyable(g, child_type); buf_resize(&entry->name, 0); @@ -532,12 +531,14 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; } else if (type_is_codegen_pointer(child_type)) { + assert(child_type->di_type); // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers // function types are technically pointers entry->type_ref = child_type->type_ref; entry->di_type = child_type->di_type; } else { + assert(child_type->di_type); // create a struct with a boolean whether this is the null value LLVMTypeRef elem_types[] = { child_type->type_ref, diff --git a/test/cases/null.zig b/test/cases/null.zig index cdcfd23efb..d2a9aaed55 100644 --- a/test/cases/null.zig +++ b/test/cases/null.zig @@ -143,3 +143,14 @@ test "null with default unwrap" { const x: i32 = null orelse 1; assert(x == 1); } + +test "optional types" { + comptime { + const opt_type_struct = StructWithOptionalType { .t=u8, }; + assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); + } +} + +const StructWithOptionalType = struct { + t: ?type, +}; -- cgit v1.2.3 From 48de57d8248d9203b44d28d7749b5d7c1a00deba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 17:01:23 -0400 Subject: add basic std lib code for loading dynamic libraries this is going to only work for very basic libraries; I plan to slowly add more features over time to support more complicated libraries --- CMakeLists.txt | 1 + src/codegen.cpp | 12 +- src/link.cpp | 4 +- std/dynamic_library.zig | 161 +++++++++++++++++++++++++ std/elf.zig | 15 +++ std/index.zig | 1 + std/io.zig | 7 +- std/math/index.zig | 11 ++ std/os/file.zig | 11 +- std/os/index.zig | 14 +++ test/build_examples.zig | 5 + test/standalone/load_dynamic_library/add.zig | 3 + test/standalone/load_dynamic_library/build.zig | 22 ++++ test/standalone/load_dynamic_library/main.zig | 17 +++ 14 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 std/dynamic_library.zig create mode 100644 test/standalone/load_dynamic_library/add.zig create mode 100644 test/standalone/load_dynamic_library/build.zig create mode 100644 test/standalone/load_dynamic_library/main.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index dd4770ad72..e502901bd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -438,6 +438,7 @@ set(ZIG_STD_FILES "debug/failing_allocator.zig" "debug/index.zig" "dwarf.zig" + "dynamic_library.zig" "elf.zig" "empty.zig" "event.zig" diff --git a/src/codegen.cpp b/src/codegen.cpp index d05bcba2ce..fedfcfa744 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6768,7 +6768,7 @@ static void define_builtin_compile_vars(CodeGen *g) { int err; Buf *abs_full_path = buf_alloc(); if ((err = os_path_real(builtin_zig_path, abs_full_path))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); exit(1); } @@ -6936,11 +6936,11 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(&path_to_code_src, abs_full_path))) { - zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); + zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } Buf *import_code = buf_alloc(); if ((err = os_fetch_file_path(abs_full_path, import_code, false))) { - zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); + zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); } return add_source_file(g, package, abs_full_path, import_code); @@ -7024,13 +7024,13 @@ static void gen_root_source(CodeGen *g) { Buf *abs_full_path = buf_alloc(); int err; if ((err = os_path_real(rel_full_path, abs_full_path))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); exit(1); } Buf *source_code = buf_alloc(); if ((err = os_fetch_file_path(rel_full_path, source_code, true))) { - fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err)); + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(rel_full_path), err_str(err)); exit(1); } @@ -7374,7 +7374,7 @@ static void gen_h_file(CodeGen *g) { FILE *out_h = fopen(buf_ptr(g->out_h_path), "wb"); if (!out_h) - zig_panic("unable to open %s: %s", buf_ptr(g->out_h_path), strerror(errno)); + zig_panic("unable to open %s: %s\n", buf_ptr(g->out_h_path), strerror(errno)); Buf *export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); buf_upcase(export_macro); diff --git a/src/link.cpp b/src/link.cpp index d2925cb5a8..a4631b1daf 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) { static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; - if (lj->link_in_crt) { + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } @@ -432,7 +432,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si static void construct_linker_job_coff(LinkJob *lj) { CodeGen *g = lj->codegen; - if (lj->link_in_crt) { + if (g->libc_link_lib != nullptr) { find_libc_lib_path(g); } diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig new file mode 100644 index 0000000000..87b58ec207 --- /dev/null +++ b/std/dynamic_library.zig @@ -0,0 +1,161 @@ +const std = @import("index.zig"); +const mem = std.mem; +const elf = std.elf; +const cstr = std.cstr; +const linux = std.os.linux; + +pub const DynLib = struct { + allocator: *mem.Allocator, + elf_lib: ElfLib, + fd: i32, + map_addr: usize, + map_size: usize, + + /// Trusts the file + pub fn findAndOpen(allocator: *mem.Allocator, name: []const u8) !DynLib { + return open(allocator, name); + } + + /// Trusts the file + pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib { + const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY); + errdefer std.os.close(fd); + + const size = usize((try std.os.posixFStat(fd)).size); + + const addr = linux.mmap( + null, + size, + linux.PROT_READ | linux.PROT_EXEC, + linux.MAP_PRIVATE | linux.MAP_LOCKED, + fd, + 0, + ); + errdefer _ = linux.munmap(addr, size); + + const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size]; + + return DynLib{ + .allocator = allocator, + .elf_lib = try ElfLib.init(bytes), + .fd = fd, + .map_addr = addr, + .map_size = size, + }; + } + + pub fn close(self: *DynLib) void { + _ = linux.munmap(self.map_addr, self.map_size); + std.os.close(self.fd); + self.* = undefined; + } + + pub fn lookup(self: *DynLib, name: []const u8) ?usize { + return self.elf_lib.lookup("", name); + } +}; + +pub const ElfLib = struct { + strings: [*]u8, + syms: [*]elf.Sym, + hashtab: [*]linux.Elf_Symndx, + versym: ?[*]u16, + verdef: ?*elf.Verdef, + base: usize, + + // Trusts the memory + pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib { + const eh = @ptrCast(*elf.Ehdr, bytes.ptr); + if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; + if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary; + + const elf_addr = @ptrToInt(bytes.ptr); + var ph_addr: usize = elf_addr + eh.e_phoff; + + var base: usize = @maxValue(usize); + var maybe_dynv: ?[*]usize = null; + { + var i: usize = 0; + while (i < eh.e_phnum) : ({ + i += 1; + ph_addr += eh.e_phentsize; + }) { + const ph = @intToPtr(*elf.Phdr, ph_addr); + switch (ph.p_type) { + elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr, + elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset), + else => {}, + } + } + } + const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; + if (base == @maxValue(usize)) return error.BaseNotFound; + + var maybe_strings: ?[*]u8 = null; + var maybe_syms: ?[*]elf.Sym = null; + var maybe_hashtab: ?[*]linux.Elf_Symndx = null; + var maybe_versym: ?[*]u16 = null; + var maybe_verdef: ?*elf.Verdef = null; + + { + var i: usize = 0; + while (dynv[i] != 0) : (i += 2) { + const p = base + dynv[i + 1]; + switch (dynv[i]) { + elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p), + elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p), + elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p), + elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p), + elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p), + else => {}, + } + } + } + + return ElfLib{ + .base = base, + .strings = maybe_strings orelse return error.ElfStringSectionNotFound, + .syms = maybe_syms orelse return error.ElfSymSectionNotFound, + .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, + .versym = maybe_versym, + .verdef = maybe_verdef, + }; + } + + /// Returns the address of the symbol + pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize { + const maybe_versym = if (self.verdef == null) null else self.versym; + + const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); + const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE); + + var i: usize = 0; + while (i < self.hashtab[1]) : (i += 1) { + if (0 == (u32(1) << u5(self.syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << u5(self.syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == self.syms[i].st_shndx) continue; + if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue; + if (maybe_versym) |versym| { + if (!checkver(self.verdef.?, versym[i], vername, self.strings)) + continue; + } + return self.base + self.syms[i].st_value; + } + + return null; + } +}; + +fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool { + var def = def_arg; + const vsym = @bitCast(u32, vsym_arg) & 0x7fff; + while (true) { + if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym) + break; + if (def.vd_next == 0) + return false; + def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next); + } + const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux); + return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name)); +} diff --git a/std/elf.zig b/std/elf.zig index 50e97ab271..8e6445c631 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -305,6 +305,21 @@ pub const STT_ARM_16BIT = STT_HIPROC; pub const VER_FLG_BASE = 0x1; pub const VER_FLG_WEAK = 0x2; +/// An unknown type. +pub const ET_NONE = 0; + +/// A relocatable file. +pub const ET_REL = 1; + +/// An executable file. +pub const ET_EXEC = 2; + +/// A shared object. +pub const ET_DYN = 3; + +/// A core file. +pub const ET_CORE = 4; + pub const FileType = enum { Relocatable, Executable, diff --git a/std/index.zig b/std/index.zig index 8abfa3db88..3b523f519f 100644 --- a/std/index.zig +++ b/std/index.zig @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; +pub const DynLib = @import("dynamic_library.zig").DynLib; pub const atomic = @import("atomic/index.zig"); pub const base64 = @import("base64.zig"); diff --git a/std/io.zig b/std/io.zig index a603d0cf5e..cfe1a7f585 100644 --- a/std/io.zig +++ b/std/io.zig @@ -242,11 +242,16 @@ pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) /// On success, caller owns returned buffer. pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { + return readFileAllocAligned(allocator, path, @alignOf(u8)); +} + +/// On success, caller owns returned buffer. +pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 { var file = try File.openRead(allocator, path); defer file.close(); const size = try file.getEndPos(); - const buf = try allocator.alloc(u8, size); + const buf = try allocator.alignedAlloc(u8, A, size); errdefer allocator.free(buf); var adapter = FileInStream.init(&file); diff --git a/std/math/index.zig b/std/math/index.zig index cc1b833a37..8c1dcc32c4 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -536,6 +536,17 @@ test "math.cast" { assert(@typeOf(try cast(u8, u32(255))) == u8); } +pub const AlignCastError = error{UnalignedMemory}; + +/// Align cast a pointer but return an error if it's the wrong field +pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { + const addr = @ptrToInt(ptr); + if (addr % alignment != 0) { + return error.UnalignedMemory; + } + return @alignCast(alignment, ptr); +} + pub fn floorPowerOfTwo(comptime T: type, value: T) T { var x = value; diff --git a/std/os/file.zig b/std/os/file.zig index 56da4f73a6..41d3dfbf95 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -265,16 +265,7 @@ pub const File = struct { pub fn getEndPos(self: *File) !usize { if (is_posix) { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(self.handle, &stat)); - if (err > 0) { - return switch (err) { - posix.EBADF => error.BadFd, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; - } - + const stat = try os.posixFStat(self.handle); return usize(stat.size); } else if (is_windows) { var file_size: windows.LARGE_INTEGER = undefined; diff --git a/std/os/index.zig b/std/os/index.zig index 62eeb7e43e..fb4605fce0 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2697,3 +2697,17 @@ pub fn posixWait(pid: i32) i32 { } } } + +pub fn posixFStat(fd: i32) !posix.Stat { + var stat: posix.Stat = undefined; + const err = posix.getErrno(posix.fstat(fd, &stat)); + if (err > 0) { + return switch (err) { + posix.EBADF => error.BadFd, + posix.ENOMEM => error.SystemResources, + else => os.unexpectedErrorPosix(err), + }; + } + + return stat; +} diff --git a/test/build_examples.zig b/test/build_examples.zig index 1ba0ca46cf..7cae734677 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -18,4 +18,9 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addBuildFile("test/standalone/pkg_import/build.zig"); cases.addBuildFile("test/standalone/use_alias/build.zig"); cases.addBuildFile("test/standalone/brace_expansion/build.zig"); + if (builtin.os == builtin.Os.linux) { + // TODO hook up the DynLib API for windows using LoadLibraryA + // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it + cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); + } } diff --git a/test/standalone/load_dynamic_library/add.zig b/test/standalone/load_dynamic_library/add.zig new file mode 100644 index 0000000000..a04ec1544d --- /dev/null +++ b/test/standalone/load_dynamic_library/add.zig @@ -0,0 +1,3 @@ +export fn add(a: i32, b: i32) i32 { + return a + b; +} diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig new file mode 100644 index 0000000000..1f981a5c7d --- /dev/null +++ b/test/standalone/load_dynamic_library/build.zig @@ -0,0 +1,22 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const opts = b.standardReleaseOptions(); + + const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0)); + lib.setBuildMode(opts); + lib.linkSystemLibrary("c"); + + const main = b.addExecutable("main", "main.zig"); + main.setBuildMode(opts); + + const run = b.addCommand(".", b.env_map, [][]const u8{ + main.getOutputPath(), + lib.getOutputPath(), + }); + run.step.dependOn(&lib.step); + run.step.dependOn(&main.step); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/load_dynamic_library/main.zig b/test/standalone/load_dynamic_library/main.zig new file mode 100644 index 0000000000..4c45ad6fde --- /dev/null +++ b/test/standalone/load_dynamic_library/main.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn main() !void { + const args = try std.os.argsAlloc(std.debug.global_allocator); + defer std.os.argsFree(std.debug.global_allocator, args); + + const dynlib_name = args[1]; + + var lib = try std.DynLib.open(std.debug.global_allocator, dynlib_name); + defer lib.close(); + + const addr = lib.lookup("add") orelse return error.SymbolNotFound; + const addFn = @intToPtr(extern fn (i32, i32) i32, addr); + + const result = addFn(12, 34); + std.debug.assert(result == 46); +} -- cgit v1.2.3 From 59b3dc8907f76b93caa689732e878a5bfa2f65c2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 13 Jun 2018 22:40:38 -0400 Subject: allow passing by non-copying value closes #733 --- doc/langref.html.in | 37 ++++++++++++++----------------------- src/analyze.cpp | 11 ++++------- test/cases/fn.zig | 13 +++++++++++++ test/compile_errors.zig | 23 ----------------------- 4 files changed, 31 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 814de721a6..b32c8165e2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2797,39 +2797,30 @@ fn foo() void { } {#code_end#} {#header_open|Pass-by-value Parameters#}

      - In Zig, structs, unions, and enums with payloads cannot be passed by value - to a function. + In Zig, structs, unions, and enums with payloads can be passed directly to a function:

      - {#code_begin|test_err|not copyable; cannot pass by value#} -const Foo = struct { + {#code_begin|test#} +const Point = struct { x: i32, + y: i32, }; -fn bar(foo: Foo) void {} - -test "pass aggregate type by value to function" { - bar(Foo {.x = 12,}); +fn foo(point: Point) i32 { + return point.x + point.y; } - {#code_end#} -

      - Instead, one must use *const. Zig allows implicitly casting something - to a const pointer to it: -

      - {#code_begin|test#} -const Foo = struct { - x: i32, -}; -fn bar(foo: *const Foo) void {} +const assert = @import("std").debug.assert; -test "implicitly cast to const pointer" { - bar(Foo {.x = 12,}); +test "pass aggregate type by non-copy value to function" { + assert(foo(Point{ .x = 1, .y = 2 }) == 3); } {#code_end#}

      - However, - the C ABI does allow passing structs and unions by value. So functions which - use the C calling convention may pass structs and unions by value. + In this case, the value may be passed by reference, or by value, whichever way + Zig decides will be faster. +

      +

      + For extern functions, Zig follows the C ABI for passing structs and unions by value.

      {#header_close#} {#header_open|Function Reflection#} diff --git a/src/analyze.cpp b/src/analyze.cpp index cbeac7bc21..758bc1a045 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1135,7 +1135,10 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { gen_param_info->src_index = i; gen_param_info->gen_index = SIZE_MAX; - type_ensure_zero_bits_known(g, type_entry); + ensure_complete_type(g, type_entry); + if (type_is_invalid(type_entry)) + return g->builtin_types.entry_invalid; + if (type_has_bits(type_entry)) { TypeTableEntry *gen_type; if (handle_is_ptr(type_entry)) { @@ -1546,12 +1549,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: - ensure_complete_type(g, type_entry); - if (calling_convention_allows_zig_types(fn_type_id.cc) && !type_is_copyable(g, type_entry)) { - add_node_error(g, param_node->data.param_decl.type, - buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name))); - return g->builtin_types.entry_invalid; - } break; } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; diff --git a/test/cases/fn.zig b/test/cases/fn.zig index dfb254c6aa..2426a411df 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -119,3 +119,16 @@ test "assign inline fn to const variable" { } inline fn inlineFn() void {} + +test "pass by non-copying value" { + assert(bar(Point{ .x = 1, .y = 2 }) == 3); +} + +const Point = struct { + x: i32, + y: i32, +}; + +fn bar(pt: Point) i32 { + return pt.x + pt.y; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 06f17a37ee..60ba255172 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2573,15 +2573,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { break :x tc; }); - cases.add( - "pass non-copyable type by value to function", - \\const Point = struct { x: i32, y: i32, }; - \\fn foo(p: Point) void { } - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , - ".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value", - ); - cases.add( "implicit cast from array to mutable slice", \\var global_array: [10]i32 = undefined; @@ -4066,20 +4057,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:5: note: field 'A' has type 'i32'", ); - cases.add( - "self-referencing function pointer field", - \\const S = struct { - \\ f: fn(_: S) void, - \\}; - \\fn f(_: S) void { - \\} - \\export fn entry() void { - \\ var _ = S { .f = f }; - \\} - , - ".tmp_source.zig:4:9: error: type 'S' is not copyable; cannot pass by value", - ); - cases.add( "taking offset of void field in struct", \\const Empty = struct { -- cgit v1.2.3 From e311cd562b47529bdcd2423658915539ddb6bc36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 15 Jun 2018 13:49:39 -0400 Subject: don't automatically take pointer when passing by non-copying value this commit does not have all tests passing --- src/ir.cpp | 57 ++++---- std/array_list.zig | 26 ++-- std/build.zig | 2 +- std/fmt/index.zig | 8 +- std/json.zig | 2 +- std/math/big/int.zig | 346 ++++++++++++++++++++---------------------------- std/mem.zig | 10 +- test/cases/cast.zig | 8 -- test/cases/fn.zig | 48 ++++++- test/cases/var_args.zig | 12 -- test/compile_errors.zig | 2 +- 11 files changed, 249 insertions(+), 272 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index e5e8dcbb9d..d008ead113 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10463,13 +10463,6 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ zig_unreachable(); } -static IrInstruction *ir_implicit_byval_const_ref_cast(IrAnalyze *ira, IrInstruction *inst) { - if (type_is_copyable(ira->codegen, inst->value.type)) - return inst; - TypeTableEntry *const_ref_type = get_pointer_to_type(ira->codegen, inst->value.type, true); - return ir_implicit_cast(ira, inst, const_ref_type); -} - static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { TypeTableEntry *type_entry = ptr->value.type; if (type_is_invalid(type_entry)) { @@ -12283,7 +12276,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod IrInstruction *casted_arg; if (is_var_args) { arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); + casted_arg = arg; } else { if (param_decl_node->data.param_decl.var_token == nullptr) { AstNode *param_type_node = param_decl_node->data.param_decl.type; @@ -12296,7 +12289,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod return false; } else { arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); + casted_arg = arg; } } @@ -12515,9 +12508,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal size_t next_proto_i = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + first_arg_known_bare = param_type->id != TypeTableEntryIdPointer; + } + + IrInstruction *first_arg; + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12667,9 +12669,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal size_t next_proto_i = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + TypeTableEntry *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + first_arg_known_bare = param_type->id != TypeTableEntryIdPointer; + } + + IrInstruction *first_arg; + if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12802,10 +12813,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } if (inst_fn_type_id.async_allocator_type == nullptr) { - IrInstruction *casted_inst = ir_implicit_byval_const_ref_cast(ira, uncasted_async_allocator_inst); - if (type_is_invalid(casted_inst->value.type)) - return ira->codegen->builtin_types.entry_invalid; - inst_fn_type_id.async_allocator_type = casted_inst->value.type; + inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type; } async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type); if (type_is_invalid(async_allocator_inst->value.type)) @@ -12866,9 +12874,16 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal IrInstruction **casted_args = allocate(call_param_count); size_t next_arg_index = 0; if (first_arg_ptr) { - IrInstruction *first_arg; assert(first_arg_ptr->value.type->id == TypeTableEntryIdPointer); - if (handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) { + + TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; + if (type_is_invalid(param_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *first_arg; + if (param_type->id == TypeTableEntryIdPointer && + handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) + { first_arg = first_arg_ptr; } else { first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr); @@ -12876,10 +12891,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } - TypeTableEntry *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type); if (type_is_invalid(casted_arg->value.type)) return ira->codegen->builtin_types.entry_invalid; diff --git a/std/array_list.zig b/std/array_list.zig index 1a235d28a3..fd1d5cbe26 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -29,36 +29,36 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } - pub fn deinit(self: *const Self) void { + pub fn deinit(self: Self) void { self.allocator.free(self.items); } - pub fn toSlice(self: *const Self) []align(A) T { + pub fn toSlice(self: Self) []align(A) T { return self.items[0..self.len]; } - pub fn toSliceConst(self: *const Self) []align(A) const T { + pub fn toSliceConst(self: Self) []align(A) const T { return self.items[0..self.len]; } - pub fn at(self: *const Self, n: usize) T { + pub fn at(self: Self, n: usize) T { return self.toSliceConst()[n]; } /// Sets the value at index `i`, or returns `error.OutOfBounds` if /// the index is not in range. - pub fn setOrError(self: *const Self, i: usize, item: *const T) !void { + pub fn setOrError(self: Self, i: usize, item: T) !void { if (i >= self.len) return error.OutOfBounds; - self.items[i] = item.*; + self.items[i] = item; } /// Sets the value at index `i`, asserting that the value is in range. - pub fn set(self: *const Self, i: usize, item: *const T) void { + pub fn set(self: *Self, i: usize, item: T) void { assert(i < self.len); - self.items[i] = item.*; + self.items[i] = item; } - pub fn count(self: *const Self) usize { + pub fn count(self: Self) usize { return self.len; } @@ -81,12 +81,12 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return result; } - pub fn insert(self: *Self, n: usize, item: *const T) !void { + pub fn insert(self: *Self, n: usize, item: T) !void { try self.ensureCapacity(self.len + 1); self.len += 1; mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]); - self.items[n] = item.*; + self.items[n] = item; } pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void { @@ -97,9 +97,9 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { mem.copy(T, self.items[n .. n + items.len], items); } - pub fn append(self: *Self, item: *const T) !void { + pub fn append(self: *Self, item: T) !void { const new_item_ptr = try self.addOne(); - new_item_ptr.* = item.*; + new_item_ptr.* = item; } pub fn appendSlice(self: *Self, items: []align(A) const T) !void { diff --git a/std/build.zig b/std/build.zig index 16ce426bcb..92454a183a 100644 --- a/std/build.zig +++ b/std/build.zig @@ -234,7 +234,7 @@ pub const Builder = struct { defer wanted_steps.deinit(); if (step_names.len == 0) { - try wanted_steps.append(&self.default_step); + try wanted_steps.append(self.default_step); } else { for (step_names) |step_name| { const s = try self.getTopLevelStepByName(step_name); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index cfc0948d2c..90d3a559c4 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -162,8 +162,6 @@ pub fn formatType( }, builtin.TypeInfo.Pointer.Size.Many => { if (ptr_info.child == u8) { - //This is a bit of a hack, but it made more sense to - // do this check here than have formatText do it if (fmt[0] == 's') { const len = std.cstr.len(value); return formatText(value[0..len], fmt, context, Errors, output); @@ -176,6 +174,12 @@ pub fn formatType( return output(context, casted_value); }, }, + builtin.TypeId.Array => |info| { + if (info.child == u8) { + return formatText(value, fmt, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); + }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), } } diff --git a/std/json.zig b/std/json.zig index 75ea2eee1c..8bbee981e3 100644 --- a/std/json.zig +++ b/std/json.zig @@ -1326,7 +1326,7 @@ pub const Parser = struct { }, // Array Parent -> [ ..., , value ] Value.Array => |*array| { - try array.append(value); + try array.append(value.*); p.state = State.ArrayValue; }, else => { diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 19af10e695..5e15cfb895 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -18,39 +18,6 @@ comptime { debug.assert(Limb.is_signed == false); } -const wrapped_buffer_size = 512; - -// Converts primitive integer values onto a stack-based big integer, or passes through existing -// Int types with no modifications. This can fail at runtime if using a very large dynamic -// integer but it is very unlikely and is considered a user error. -fn wrapInt(allocator: *Allocator, bn: var) *const Int { - const T = @typeOf(bn); - switch (@typeInfo(T)) { - TypeId.Pointer => |info| { - if (info.child == Int) { - return bn; - } else { - @compileError("cannot set Int using type " ++ @typeName(T)); - } - }, - else => { - var s = allocator.create(Int) catch unreachable; - s.* = Int{ - .allocator = allocator, - .positive = false, - .limbs = block: { - var limbs = allocator.alloc(Limb, Int.default_capacity) catch unreachable; - limbs[0] = 0; - break :block limbs; - }, - .len = 1, - }; - s.set(bn) catch unreachable; - return s; - }, - } -} - pub const Int = struct { allocator: *Allocator, positive: bool, @@ -93,11 +60,11 @@ pub const Int = struct { self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity); } - pub fn deinit(self: *const Int) void { + pub fn deinit(self: Int) void { self.allocator.free(self.limbs); } - pub fn clone(other: *const Int) !Int { + pub fn clone(other: Int) !Int { return Int{ .allocator = other.allocator, .positive = other.positive, @@ -110,8 +77,8 @@ pub const Int = struct { }; } - pub fn copy(self: *Int, other: *const Int) !void { - if (self == other) { + pub fn copy(self: *Int, other: Int) !void { + if (self == &other) { return; } @@ -125,7 +92,7 @@ pub const Int = struct { mem.swap(Int, self, other); } - pub fn dump(self: *const Int) void { + pub fn dump(self: Int) void { for (self.limbs) |limb| { debug.warn("{x} ", limb); } @@ -140,20 +107,20 @@ pub const Int = struct { r.positive = true; } - pub fn isOdd(r: *const Int) bool { + pub fn isOdd(r: Int) bool { return r.limbs[0] & 1 != 0; } - pub fn isEven(r: *const Int) bool { + pub fn isEven(r: Int) bool { return !r.isOdd(); } - fn bitcount(self: *const Int) usize { + fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); return usize(!self.positive) + u_bit_count; } - pub fn sizeInBase(self: *const Int, base: usize) usize { + pub fn sizeInBase(self: Int, base: usize) usize { return (self.bitcount() / math.log2(base)) + 1; } @@ -219,7 +186,7 @@ pub const Int = struct { TargetTooSmall, }; - pub fn to(self: *const Int, comptime T: type) ConvertError!T { + pub fn to(self: Int, comptime T: type) ConvertError!T { switch (@typeId(T)) { TypeId.Int => { const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T; @@ -286,16 +253,28 @@ pub const Int = struct { i += 1; } + // TODO values less than limb size should guarantee non allocating + var base_buffer: [512]u8 = undefined; + const base_al = &std.heap.FixedBufferAllocator.init(base_buffer[0..]).allocator; + const base_ap = try Int.initSet(base_al, base); + + var d_buffer: [512]u8 = undefined; + var d_fba = std.heap.FixedBufferAllocator.init(d_buffer[0..]); + const d_al = &d_fba.allocator; + try self.set(0); for (value[i..]) |ch| { const d = try charToDigit(ch, base); - try self.mul(self, base); - try self.add(self, d); + d_fba.end_index = 0; + const d_ap = try Int.initSet(d_al, d); + + try self.mul(self.*, base_ap); + try self.add(self.*, d_ap); } self.positive = positive; } - pub fn toString(self: *const Int, allocator: *Allocator, base: u8) ![]const u8 { + pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 { if (base < 2 or base > 16) { return error.InvalidBase; } @@ -345,7 +324,7 @@ pub const Int = struct { var b = try Int.initSet(allocator, limb_base); while (q.len >= 2) { - try Int.divTrunc(&q, &r, &q, &b); + try Int.divTrunc(&q, &r, q, b); var r_word = r.limbs[0]; var i: usize = 0; @@ -378,12 +357,7 @@ pub const Int = struct { } // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. - pub fn cmpAbs(a: *const Int, bv: var) i8 { - // TODO: Thread-local buffer. - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var b = wrapInt(&stack.allocator, bv); - + pub fn cmpAbs(a: Int, b: Int) i8 { if (a.len < b.len) { return -1; } @@ -408,11 +382,7 @@ pub const Int = struct { } // returns -1, 0, 1 if a < b, a == b or a > b respectively. - pub fn cmp(a: *const Int, bv: var) i8 { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var b = wrapInt(&stack.allocator, bv); - + pub fn cmp(a: Int, b: Int) i8 { if (a.positive != b.positive) { return if (a.positive) i8(1) else -1; } else { @@ -422,17 +392,17 @@ pub const Int = struct { } // if a == 0 - pub fn eqZero(a: *const Int) bool { + pub fn eqZero(a: Int) bool { return a.len == 1 and a.limbs[0] == 0; } // if |a| == |b| - pub fn eqAbs(a: *const Int, b: var) bool { + pub fn eqAbs(a: Int, b: Int) bool { return cmpAbs(a, b) == 0; } // if a == b - pub fn eq(a: *const Int, b: var) bool { + pub fn eq(a: Int, b: Int) bool { return cmp(a, b) == 0; } @@ -473,12 +443,7 @@ pub const Int = struct { } // r = a + b - pub fn add(r: *Int, av: var, bv: var) Allocator.Error!void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void { if (a.eqZero()) { try r.copy(b); return; @@ -547,12 +512,7 @@ pub const Int = struct { } // r = a - b - pub fn sub(r: *Int, av: var, bv: var) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn sub(r: *Int, a: Int, b: Int) !void { if (a.positive != b.positive) { if (a.positive) { // (a) - (-b) => a + b @@ -632,14 +592,9 @@ pub const Int = struct { // rma = a * b // // For greatest efficiency, ensure rma does not alias a or b. - pub fn mul(rma: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn mul(rma: *Int, a: Int, b: Int) !void { var r = rma; - var aliased = rma == a or rma == b; + var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr; var sr: Int = undefined; if (aliased) { @@ -714,29 +669,29 @@ pub const Int = struct { } } - pub fn divFloor(q: *Int, r: *Int, a: var, b: var) !void { + pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); // Trunc -> Floor. if (!q.positive) { - try q.sub(q, 1); - try r.add(q, 1); + // TODO values less than limb size should guarantee non allocating + var one_buffer: [512]u8 = undefined; + const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator; + const one_ap = try Int.initSet(one_al, 1); + + try q.sub(q.*, one_ap); + try r.add(q.*, one_ap); } r.positive = b.positive; } - pub fn divTrunc(q: *Int, r: *Int, a: var, b: var) !void { + pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void { try div(q, r, a, b); r.positive = a.positive; } // Truncates by default. - fn div(quo: *Int, rem: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void { if (b.eqZero()) { @panic("division by zero"); } @@ -821,8 +776,8 @@ pub const Int = struct { // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) const norm_shift = @clz(y.limbs[y.len - 1]); - try x.shiftLeft(x, norm_shift); - try y.shiftLeft(y, norm_shift); + try x.shiftLeft(x.*, norm_shift); + try y.shiftLeft(y.*, norm_shift); const n = x.len - 1; const t = y.len - 1; @@ -832,10 +787,10 @@ pub const Int = struct { mem.set(Limb, q.limbs[0..q.len], 0); // 2. - try tmp.shiftLeft(y, Limb.bit_count * (n - t)); - while (x.cmp(&tmp) >= 0) { + try tmp.shiftLeft(y.*, Limb.bit_count * (n - t)); + while (x.cmp(tmp) >= 0) { q.limbs[n - t] += 1; - try x.sub(x, tmp); + try x.sub(x.*, tmp); } // 3. @@ -864,7 +819,7 @@ pub const Int = struct { r.limbs[2] = carry; r.normN(3); - if (r.cmpAbs(&tmp) <= 0) { + if (r.cmpAbs(tmp) <= 0) { break; } @@ -873,13 +828,13 @@ pub const Int = struct { // 3.3 try tmp.set(q.limbs[i - t - 1]); - try tmp.mul(&tmp, y); - try tmp.shiftLeft(&tmp, Limb.bit_count * (i - t - 1)); - try x.sub(x, &tmp); + try tmp.mul(tmp, y.*); + try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1)); + try x.sub(x.*, tmp); if (!x.positive) { - try tmp.shiftLeft(y, Limb.bit_count * (i - t - 1)); - try x.add(x, &tmp); + try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1)); + try x.add(x.*, tmp); q.limbs[i - t - 1] -= 1; } } @@ -887,16 +842,12 @@ pub const Int = struct { // Denormalize q.normN(q.len); - try r.shiftRight(x, norm_shift); + try r.shiftRight(x.*, norm_shift); r.normN(r.len); } // r = a << shift, in other words, r = a * 2^shift - pub fn shiftLeft(r: *Int, av: var, shift: usize) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - + pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void { try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1); llshl(r.limbs[0..], a.limbs[0..a.len], shift); r.norm1(a.len + (shift / Limb.bit_count) + 1); @@ -927,11 +878,7 @@ pub const Int = struct { } // r = a >> shift - pub fn shiftRight(r: *Int, av: var, shift: usize) !void { - var buffer: [wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - + pub fn shiftRight(r: *Int, a: Int, shift: usize) !void { if (a.len <= shift / Limb.bit_count) { r.len = 1; r.limbs[0] = 0; @@ -966,12 +913,7 @@ pub const Int = struct { } // r = a | b - pub fn bitOr(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitOr(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(a.len); llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -998,12 +940,7 @@ pub const Int = struct { } // r = a & b - pub fn bitAnd(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitAnd(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(b.len); lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -1027,12 +964,7 @@ pub const Int = struct { } // r = a ^ b - pub fn bitXor(r: *Int, av: var, bv: var) !void { - var buffer: [2 * wrapped_buffer_size]u8 = undefined; - var stack = std.heap.FixedBufferAllocator.init(buffer[0..]); - var a = wrapInt(&stack.allocator, av); - var b = wrapInt(&stack.allocator, bv); - + pub fn bitXor(r: *Int, a: Int, b: Int) !void { if (a.len > b.len) { try r.ensureCapacity(a.len); llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); @@ -1065,7 +997,7 @@ pub const Int = struct { // may be untested in some cases. const u256 = @IntType(false, 256); -var al = debug.global_allocator; +const al = debug.global_allocator; test "big.int comptime_int set" { comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab; @@ -1198,7 +1130,7 @@ test "big.int bitcount + sizeInBase" { debug.assert(a.sizeInBase(2) >= 32); debug.assert(a.sizeInBase(10) >= 10); - try a.shiftLeft(&a, 5000); + try a.shiftLeft(a, 5000); debug.assert(a.bitcount() == 5032); debug.assert(a.sizeInBase(2) >= 5032); a.positive = false; @@ -1320,40 +1252,40 @@ test "big.int compare" { var a = try Int.initSet(al, -11); var b = try Int.initSet(al, 10); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(a.cmp(&b) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(a.cmp(b) == -1); } test "big.int compare similar" { var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee); var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef); - debug.assert(a.cmpAbs(&b) == -1); - debug.assert(b.cmpAbs(&a) == 1); + debug.assert(a.cmpAbs(b) == -1); + debug.assert(b.cmpAbs(a) == 1); } test "big.int compare different limb size" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, 1); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(b.cmpAbs(&a) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(b.cmpAbs(a) == -1); } test "big.int compare multi-limb" { var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef); var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee); - debug.assert(a.cmpAbs(&b) == 1); - debug.assert(a.cmp(&b) == -1); + debug.assert(a.cmpAbs(b) == 1); + debug.assert(a.cmp(b) == -1); } test "big.int equality" { var a = try Int.initSet(al, 0xffffffff1); var b = try Int.initSet(al, -0xffffffff1); - debug.assert(a.eqAbs(&b)); - debug.assert(!a.eq(&b)); + debug.assert(a.eqAbs(b)); + debug.assert(!a.eq(b)); } test "big.int abs" { @@ -1381,7 +1313,7 @@ test "big.int add single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u32)) == 55); } @@ -1392,10 +1324,10 @@ test "big.int add multi-single" { var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2); - try c.add(&b, &a); + try c.add(b, a); debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2); } @@ -1406,7 +1338,7 @@ test "big.int add multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u128)) == op1 + op2); } @@ -1416,7 +1348,7 @@ test "big.int add zero-zero" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.add(&a, &b); + try c.add(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1426,7 +1358,7 @@ test "big.int add alias multi-limb nonzero-zero" { var a = try Int.initSet(al, op1); var b = try Int.initSet(al, 0); - try a.add(&a, &b); + try a.add(a, b); debug.assert((try a.to(u128)) == op1); } @@ -1434,16 +1366,21 @@ test "big.int add alias multi-limb nonzero-zero" { test "big.int add sign" { var a = try Int.init(al); - try a.add(1, 2); + const one = try Int.initSet(al, 1); + const two = try Int.initSet(al, 2); + const neg_one = try Int.initSet(al, -1); + const neg_two = try Int.initSet(al, -2); + + try a.add(one, two); debug.assert((try a.to(i32)) == 3); - try a.add(-1, 2); + try a.add(neg_one, two); debug.assert((try a.to(i32)) == 1); - try a.add(1, -2); + try a.add(one, neg_two); debug.assert((try a.to(i32)) == -1); - try a.add(-1, -2); + try a.add(neg_one, neg_two); debug.assert((try a.to(i32)) == -3); } @@ -1452,7 +1389,7 @@ test "big.int sub single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u32)) == 45); } @@ -1462,7 +1399,7 @@ test "big.int sub multi-single" { var b = try Int.initSet(al, 1); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(Limb)) == @maxValue(Limb)); } @@ -1475,7 +1412,7 @@ test "big.int sub multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u128)) == op1 - op2); } @@ -1485,7 +1422,7 @@ test "big.int sub equal" { var b = try Int.initSet(al, 0x11efefefefefefefefefefefef); var c = try Int.init(al); - try c.sub(&a, &b); + try c.sub(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1493,19 +1430,24 @@ test "big.int sub equal" { test "big.int sub sign" { var a = try Int.init(al); - try a.sub(1, 2); + const one = try Int.initSet(al, 1); + const two = try Int.initSet(al, 2); + const neg_one = try Int.initSet(al, -1); + const neg_two = try Int.initSet(al, -2); + + try a.sub(one, two); debug.assert((try a.to(i32)) == -1); - try a.sub(-1, 2); + try a.sub(neg_one, two); debug.assert((try a.to(i32)) == -3); - try a.sub(1, -2); + try a.sub(one, neg_two); debug.assert((try a.to(i32)) == 3); - try a.sub(-1, -2); + try a.sub(neg_one, neg_two); debug.assert((try a.to(i32)) == 1); - try a.sub(-2, -1); + try a.sub(neg_two, neg_one); debug.assert((try a.to(i32)) == -1); } @@ -1514,7 +1456,7 @@ test "big.int mul single-single" { var b = try Int.initSet(al, 5); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u64)) == 250); } @@ -1524,7 +1466,7 @@ test "big.int mul multi-single" { var b = try Int.initSet(al, 2); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1536,7 +1478,7 @@ test "big.int mul multi-multi" { var b = try Int.initSet(al, op2); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u256)) == op1 * op2); } @@ -1545,7 +1487,7 @@ test "big.int mul alias r with a" { var a = try Int.initSet(al, @maxValue(Limb)); var b = try Int.initSet(al, 2); - try a.mul(&a, &b); + try a.mul(a, b); debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1554,7 +1496,7 @@ test "big.int mul alias r with b" { var a = try Int.initSet(al, @maxValue(Limb)); var b = try Int.initSet(al, 2); - try a.mul(&b, &a); + try a.mul(b, a); debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb)); } @@ -1562,7 +1504,7 @@ test "big.int mul alias r with b" { test "big.int mul alias r with a and b" { var a = try Int.initSet(al, @maxValue(Limb)); - try a.mul(&a, &a); + try a.mul(a, a); debug.assert((try a.to(DoubleLimb)) == @maxValue(Limb) * @maxValue(Limb)); } @@ -1572,7 +1514,7 @@ test "big.int mul a*0" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1582,7 +1524,7 @@ test "big.int mul 0*0" { var b = try Int.initSet(al, 0); var c = try Int.init(al); - try c.mul(&a, &b); + try c.mul(a, b); debug.assert((try c.to(u32)) == 0); } @@ -1593,7 +1535,7 @@ test "big.int div single-single no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u32)) == 10); debug.assert((try r.to(u32)) == 0); @@ -1605,7 +1547,7 @@ test "big.int div single-single with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u32)) == 9); debug.assert((try r.to(u32)) == 4); @@ -1620,7 +1562,7 @@ test "big.int div multi-single no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == op1 / op2); debug.assert((try r.to(u64)) == 0); @@ -1635,7 +1577,7 @@ test "big.int div multi-single with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == op1 / op2); debug.assert((try r.to(u64)) == 3); @@ -1650,7 +1592,7 @@ test "big.int div multi>2-single" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == op1 / op2); debug.assert((try r.to(u32)) == 0x3e4e); @@ -1662,7 +1604,7 @@ test "big.int div single-single q < r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == 0); debug.assert((try r.to(u64)) == 0x0078f432); @@ -1674,7 +1616,7 @@ test "big.int div single-single q == r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u64)) == 1); debug.assert((try r.to(u64)) == 0); @@ -1684,7 +1626,7 @@ test "big.int div q=0 alias" { var a = try Int.initSet(al, 3); var b = try Int.initSet(al, 10); - try Int.divTrunc(&a, &b, &a, &b); + try Int.divTrunc(&a, &b, a, b); debug.assert((try a.to(u64)) == 0); debug.assert((try b.to(u64)) == 3); @@ -1698,7 +1640,7 @@ test "big.int div multi-multi q < r" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0); debug.assert((try r.to(u128)) == op1); @@ -1713,7 +1655,7 @@ test "big.int div trunc single-single +/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1733,7 +1675,7 @@ test "big.int div trunc single-single -/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1753,7 +1695,7 @@ test "big.int div trunc single-single +/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // 5 = -1 * -3 + 2 @@ -1773,7 +1715,7 @@ test "big.int div trunc single-single -/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); // n = q * d + r // -5 = 1 * -3 - 2 @@ -1793,7 +1735,7 @@ test "big.int div floor single-single +/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // 5 = 1 * 3 + 2 @@ -1813,7 +1755,7 @@ test "big.int div floor single-single -/+" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // -5 = -2 * 3 + 1 @@ -1833,7 +1775,7 @@ test "big.int div floor single-single +/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // 5 = -2 * -3 - 1 @@ -1853,7 +1795,7 @@ test "big.int div floor single-single -/-" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divFloor(&q, &r, &a, &b); + try Int.divFloor(&q, &r, a, b); // n = q * d + r // -5 = 2 * -3 + 1 @@ -1870,7 +1812,7 @@ test "big.int div multi-multi with rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); debug.assert((try r.to(u128)) == 0x28de0acacd806823638); @@ -1882,7 +1824,7 @@ test "big.int div multi-multi no rem" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); debug.assert((try r.to(u128)) == 0); @@ -1894,7 +1836,7 @@ test "big.int div multi-multi (2 branch)" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0x10000000000000000); debug.assert((try r.to(u128)) == 0x44444443444444431111111111111111); @@ -1906,7 +1848,7 @@ test "big.int div multi-multi (3.1/3.3 branch)" { var q = try Int.init(al); var r = try Int.init(al); - try Int.divTrunc(&q, &r, &a, &b); + try Int.divTrunc(&q, &r, a, b); debug.assert((try q.to(u128)) == 0xfffffffffffffffffff); debug.assert((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); @@ -1943,17 +1885,17 @@ test "big.int shift-left multi" { test "big.int shift-right negative" { var a = try Int.init(al); - try a.shiftRight(-20, 2); + try a.shiftRight(try Int.initSet(al, -20), 2); debug.assert((try a.to(i32)) == -20 >> 2); - try a.shiftRight(-5, 10); + try a.shiftRight(try Int.initSet(al, -5), 10); debug.assert((try a.to(i32)) == -5 >> 10); } test "big.int shift-left negative" { var a = try Int.init(al); - try a.shiftRight(-10, 1232); + try a.shiftRight(try Int.initSet(al, -10), 1232); debug.assert((try a.to(i32)) == -10 >> 1232); } @@ -1961,7 +1903,7 @@ test "big.int bitwise and simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitAnd(&a, &b); + try a.bitAnd(a, b); debug.assert((try a.to(u64)) == 0xeeeeeeee00000000); } @@ -1970,7 +1912,7 @@ test "big.int bitwise and multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitAnd(&a, &b); + try a.bitAnd(a, b); debug.assert((try a.to(u128)) == 0); } @@ -1979,7 +1921,7 @@ test "big.int bitwise xor simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitXor(&a, &b); + try a.bitXor(a, b); debug.assert((try a.to(u64)) == 0x1111111133333333); } @@ -1988,7 +1930,7 @@ test "big.int bitwise xor multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitXor(&a, &b); + try a.bitXor(a, b); debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) ^ @maxValue(Limb)); } @@ -1997,7 +1939,7 @@ test "big.int bitwise or simple" { var a = try Int.initSet(al, 0xffffffff11111111); var b = try Int.initSet(al, 0xeeeeeeee22222222); - try a.bitOr(&a, &b); + try a.bitOr(a, b); debug.assert((try a.to(u64)) == 0xffffffff33333333); } @@ -2006,7 +1948,7 @@ test "big.int bitwise or multi-limb" { var a = try Int.initSet(al, @maxValue(Limb) + 1); var b = try Int.initSet(al, @maxValue(Limb)); - try a.bitOr(&a, &b); + try a.bitOr(a, b); // TODO: big.int.cpp or is wrong on multi-limb. debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) + @maxValue(Limb)); @@ -2015,9 +1957,9 @@ test "big.int bitwise or multi-limb" { test "big.int var args" { var a = try Int.initSet(al, 5); - try a.add(&a, 6); + try a.add(a, try Int.initSet(al, 6)); debug.assert((try a.to(u64)) == 11); - debug.assert(a.cmp(11) == 0); - debug.assert(a.cmp(14) <= 0); + debug.assert(a.cmp(try Int.initSet(al, 11)) == 0); + debug.assert(a.cmp(try Int.initSet(al, 14)) <= 0); } diff --git a/std/mem.zig b/std/mem.zig index f961c7862b..10b3eb8fef 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -40,16 +40,12 @@ pub const Allocator = struct { /// Call destroy with the result /// TODO once #733 is solved, this will replace create - pub fn construct(self: *Allocator, init: var) t: { - // TODO this is a workaround for type getting parsed as Error!&const T - const T = @typeOf(init).Child; - break :t Error!*T; - } { - const T = @typeOf(init).Child; + pub fn construct(self: *Allocator, init: var) Error!*@typeOf(init) { + const T = @typeOf(init); if (@sizeOf(T) == 0) return &{}; const slice = try self.alloc(T, 1); const ptr = &slice[0]; - ptr.* = init.*; + ptr.* = init; return ptr; } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index ade1cf78aa..4c216010eb 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -318,14 +318,6 @@ fn testCastConstArrayRefToConstSlice() void { assert(mem.eql(u8, slice, "aoeu")); } -test "var args implicitly casts by value arg to const ref" { - foo("hello"); -} - -fn foo(args: ...) void { - assert(@typeOf(args[0]) == *const [5]u8); -} - test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 2426a411df..12f22bfc35 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(bar(Point{ .x = 1, .y = 2 }) == 3); + assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -129,6 +129,50 @@ const Point = struct { y: i32, }; -fn bar(pt: Point) i32 { +fn addPointCoords(pt: Point) i32 { return pt.x + pt.y; } + +test "pass by non-copying value through var arg" { + assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); +} + +fn addPointCoordsVar(pt: var) i32 { + comptime assert(@typeOf(pt) == Point); + return pt.x + pt.y; +} + +test "pass by non-copying value as method" { + var pt = Point2{ .x = 1, .y = 2 }; + assert(pt.addPointCoords() == 3); +} + +const Point2 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point2) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, which is generic" { + var pt = Point3{ .x = 1, .y = 2 }; + assert(pt.addPointCoords(i32) == 3); +} + +const Point3 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point3, comptime T: type) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, at comptime" { + comptime { + var pt = Point2{ .x = 1, .y = 2 }; + assert(pt.addPointCoords() == 3); + } +} diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index 5ef41f52ba..3eb6e30448 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -75,18 +75,6 @@ test "array of var args functions" { assert(!foos[1]()); } -test "pass array and slice of same array to var args should have same pointers" { - const array = "hi"; - const slice: []const u8 = array; - return assertSlicePtrsEql(array, slice); -} - -fn assertSlicePtrsEql(args: ...) void { - const s1 = ([]const u8)(args[0]); - const s2 = args[1]; - assert(s1.ptr == s2.ptr); -} - test "pass zero length array to var args param" { doNothingWithFirstArg(""); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 60ba255172..ed46e43066 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2215,7 +2215,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected type 'i32', found '*const Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", ); cases.add( -- cgit v1.2.3 From 472b7ef7e6db4bdd4717ff5b44b63e233853bb06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Jun 2018 19:14:14 -0400 Subject: disable byval --- src/codegen.cpp | 21 --------------------- test/behavior.zig | 1 + test/cases/byval_arg_var.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 test/cases/byval_arg_var.zig (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index fedfcfa744..425cdac024 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -326,13 +326,6 @@ static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const cha return addLLVMAttr(arg_val, param_index + 1, attr_name); } -static void addLLVMCallsiteAttr(LLVMValueRef call_instr, unsigned param_index, const char *attr_name) { - unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); - assert(kind_id != 0); - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0); - LLVMAddCallSiteAttribute(call_instr, param_index + 1, llvm_attr); -} - static bool is_symbol_available(CodeGen *g, Buf *name) { return g->exported_symbol_names.maybe_get(name) == nullptr && g->external_prototypes.maybe_get(name) == nullptr; } @@ -581,11 +574,6 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (param_type->id == TypeTableEntryIdPointer) { addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); } - // Note: byval is disabled on windows due to an LLVM bug: - // https://github.com/ziglang/zig/issues/536 - if (is_byval && g->zig_target.os != OsWindows) { - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "byval"); - } } uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); @@ -3114,15 +3102,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; - // Note: byval is disabled on windows due to an LLVM bug: - // https://github.com/ziglang/zig/issues/536 - if (gen_info->is_byval && g->zig_target.os != OsWindows) { - addLLVMCallsiteAttr(result, (unsigned)gen_info->gen_index, "byval"); - } - } - if (instruction->is_async) { LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); LLVMBuildStore(g->builder, result, payload_ptr); diff --git a/test/behavior.zig b/test/behavior.zig index eb8b643bb7..096c07b2e0 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -13,6 +13,7 @@ comptime { _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); + _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); diff --git a/test/cases/byval_arg_var.zig b/test/cases/byval_arg_var.zig new file mode 100644 index 0000000000..826b9cc9e5 --- /dev/null +++ b/test/cases/byval_arg_var.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +var result: []const u8 = "wrong"; + +test "aoeu" { + start(); + blowUpStack(10); + + std.debug.assert(std.mem.eql(u8, result, "string literal")); +} + +fn start() void { + foo("string literal"); +} + +fn foo(x: var) void { + bar(x); +} + +fn bar(x: var) void { + result = x; +} + +fn blowUpStack(x: u32) void { + if (x == 0) return; + blowUpStack(x - 1); +} -- cgit v1.2.3 From 79120612267f55901029dd57290ee90c0a3ec987 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Jun 2018 02:57:07 -0400 Subject: remove integer and float casting syntax * add `@intCast` * add `@floatCast` * add `@floatToInt` * add `@intToFloat` See #1061 --- doc/langref.html.in | 12 +- src/all_types.hpp | 36 +++++ src/codegen.cpp | 8 + src/ir.cpp | 290 +++++++++++++++++++++++++++++++++--- src/ir_print.cpp | 44 ++++++ src/main.cpp | 2 +- std/array_list.zig | 8 +- std/base64.zig | 4 +- std/crypto/blake2.zig | 10 +- std/crypto/md5.zig | 6 +- std/crypto/sha1.zig | 6 +- std/crypto/sha2.zig | 12 +- std/debug/index.zig | 8 +- std/fmt/errol/index.zig | 78 +++++----- std/fmt/index.zig | 19 +-- std/hash/crc.zig | 4 +- std/hash/siphash.zig | 6 +- std/heap.zig | 2 +- std/json.zig | 2 +- std/math/acos.zig | 4 +- std/math/asin.zig | 4 +- std/math/atan.zig | 4 +- std/math/atan2.zig | 8 +- std/math/atanh.zig | 2 +- std/math/big/int.zig | 22 +-- std/math/cbrt.zig | 6 +- std/math/ceil.zig | 4 +- std/math/complex/atan.zig | 10 +- std/math/complex/cosh.zig | 4 +- std/math/complex/exp.zig | 6 +- std/math/complex/ldexp.zig | 13 +- std/math/complex/sinh.zig | 10 +- std/math/complex/sqrt.zig | 10 +- std/math/complex/tanh.zig | 10 +- std/math/cos.zig | 4 +- std/math/cosh.zig | 2 +- std/math/exp.zig | 12 +- std/math/exp2.zig | 14 +- std/math/expm1.zig | 20 +-- std/math/floor.zig | 4 +- std/math/fma.zig | 6 +- std/math/frexp.zig | 4 +- std/math/hypot.zig | 2 +- std/math/ilogb.zig | 4 +- std/math/index.zig | 25 +++- std/math/ln.zig | 12 +- std/math/log.zig | 11 +- std/math/log10.zig | 14 +- std/math/log1p.zig | 12 +- std/math/log2.zig | 12 +- std/math/modf.zig | 8 +- std/math/pow.zig | 4 +- std/math/scalbn.zig | 4 +- std/math/sin.zig | 4 +- std/math/sinh.zig | 2 +- std/math/sqrt.zig | 2 +- std/math/tan.zig | 4 +- std/math/tanh.zig | 4 +- std/math/trunc.zig | 8 +- std/mem.zig | 2 +- std/os/child_process.zig | 2 +- std/os/darwin.zig | 11 +- std/os/file.zig | 6 +- std/os/index.zig | 16 +- std/os/linux/index.zig | 80 +++++----- std/os/linux/test.zig | 6 +- std/os/linux/vdso.zig | 4 +- std/os/time.zig | 24 +-- std/os/windows/util.zig | 9 +- std/rand/index.zig | 16 +- std/segmented_list.zig | 12 +- std/special/bootstrap.zig | 2 +- std/special/builtin.zig | 30 ++-- std/special/compiler_rt/divti3.zig | 2 +- std/special/compiler_rt/fixuint.zig | 8 +- std/special/compiler_rt/index.zig | 12 +- std/special/compiler_rt/udivmod.zig | 34 ++--- std/unicode.zig | 20 +-- std/zig/tokenizer.zig | 2 +- test/cases/cast.zig | 18 ++- test/cases/enum.zig | 2 +- test/cases/eval.zig | 8 +- test/cases/fn.zig | 2 +- test/cases/for.zig | 4 +- test/cases/struct.zig | 8 +- 85 files changed, 799 insertions(+), 413 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 651629d8fb..35ca9a13b4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1355,7 +1355,7 @@ var some_integers: [100]i32 = undefined; test "modify an array" { for (some_integers) |*item, i| { - item.* = i32(i); + item.* = @intCast(i32, i); } assert(some_integers[10] == 10); assert(some_integers[99] == 99); @@ -1397,8 +1397,8 @@ var fancy_array = init: { var initial_value: [10]Point = undefined; for (initial_value) |*pt, i| { pt.* = Point{ - .x = i32(i), - .y = i32(i) * 2, + .x = @intCast(i32, i), + .y = @intCast(i32, i) * 2, }; } break :init initial_value; @@ -2410,7 +2410,7 @@ test "for basics" { var sum2: i32 = 0; for (items) |value, i| { assert(@typeOf(i) == usize); - sum2 += i32(i); + sum2 += @intCast(i32, i); } assert(sum2 == 10); } @@ -5730,7 +5730,7 @@ comptime { {#code_begin|test_err|attempt to cast negative value to unsigned integer#} comptime { const value: i32 = -1; - const unsigned = u32(value); + const unsigned = @intCast(u32, value); } {#code_end#}

      At runtime crashes with the message attempt to cast negative value to unsigned integer and a stack trace.

      @@ -5744,7 +5744,7 @@ comptime { {#code_begin|test_err|cast from 'u16' to 'u8' truncates bits#} comptime { const spartan_count: u16 = 300; - const byte = u8(spartan_count); + const byte = @intCast(u8, spartan_count); } {#code_end#}

      At runtime crashes with the message integer cast truncated bits and a stack trace.

      diff --git a/src/all_types.hpp b/src/all_types.hpp index 0d364915f9..bc2fe07c18 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1357,6 +1357,10 @@ enum BuiltinFnId { BuiltinFnIdMod, BuiltinFnIdSqrt, BuiltinFnIdTruncate, + BuiltinFnIdIntCast, + BuiltinFnIdFloatCast, + BuiltinFnIdIntToFloat, + BuiltinFnIdFloatToInt, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2040,6 +2044,10 @@ enum IrInstructionId { IrInstructionIdCmpxchg, IrInstructionIdFence, IrInstructionIdTruncate, + IrInstructionIdIntCast, + IrInstructionIdFloatCast, + IrInstructionIdIntToFloat, + IrInstructionIdFloatToInt, IrInstructionIdIntType, IrInstructionIdBoolNot, IrInstructionIdMemset, @@ -2632,6 +2640,34 @@ struct IrInstructionTruncate { IrInstruction *target; }; +struct IrInstructionIntCast { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionFloatCast { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionIntToFloat { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionFloatToInt { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + struct IrInstructionIntType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 425cdac024..4108cbbd68 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4722,6 +4722,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPromiseResultType: case IrInstructionIdAwaitBookkeeping: case IrInstructionIdAddImplicitReturnType: + case IrInstructionIdIntCast: + case IrInstructionIdFloatCast: + case IrInstructionIdIntToFloat: + case IrInstructionIdFloatToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6310,6 +6314,10 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); + create_builtin_fn(g, BuiltinFnIdIntCast, "intCast", 2); + create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); + create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); + create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index d008ead113..0b847fc4e4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -460,6 +460,22 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) { return IrInstructionIdTruncate; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionIntCast *) { + return IrInstructionIdIntCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) { + return IrInstructionIdFloatCast; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { + return IrInstructionIdIntToFloat; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) { + return IrInstructionIdFloatToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } @@ -1899,10 +1915,48 @@ static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } -static IrInstruction *ir_build_truncate_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_type, IrInstruction *target) { - IrInstruction *new_instruction = ir_build_truncate(irb, old_instruction->scope, old_instruction->source_node, dest_type, target); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; +static IrInstruction *ir_build_int_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionIntCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_float_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionFloatCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_float_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionFloatToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; } static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) { @@ -3957,6 +4011,66 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, truncate, lval); } + case BuiltinFnIdIntCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdFloatCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdIntToFloat: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdFloatToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -9948,34 +10062,37 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false); } - // explicit widening or shortening cast - if ((wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdInt) || - (wanted_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdFloat)) + // explicit widening conversion + if (wanted_type->id == TypeTableEntryIdInt && + actual_type->id == TypeTableEntryIdInt && + wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) { return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit error set cast - if (wanted_type->id == TypeTableEntryIdErrorSet && - actual_type->id == TypeTableEntryIdErrorSet) + // small enough unsigned ints can get casted to large enough signed ints + if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && + actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) { - return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit cast from int to float + // explicit float widening conversion if (wanted_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdInt) + actual_type->id == TypeTableEntryIdFloat && + wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpIntToFloat, false); + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit cast from float to int - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdFloat) + + // explicit error set cast + if (wanted_type->id == TypeTableEntryIdErrorSet && + actual_type->id == TypeTableEntryIdErrorSet) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false); + return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); } // explicit cast from [N]T to []const T @@ -17365,7 +17482,126 @@ static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruc return dest_type; } - ir_build_truncate_from(&ira->new_irb, &instruction->base, dest_type_value, target); + IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, dest_type_value, target); + ir_link_new_instruction(new_instruction, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdInt) { + ir_add_error(ira, instruction->dest_type, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id == TypeTableEntryIdComptimeInt) { + if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, + CastOpNumLitToConcrete, false); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } + + if (target->value.type->id != TypeTableEntryIdInt) { + ir_add_error(ira, instruction->target, buf_sprintf("expected integer type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdFloat) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id == TypeTableEntryIdComptimeInt || + target->value.type->id == TypeTableEntryIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { + CastOp op; + if (target->value.type->id == TypeTableEntryIdComptimeInt) { + op = CastOpIntToFloat; + } else { + op = CastOpNumLitToConcrete; + } + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, op, false); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } + + if (target->value.type->id != TypeTableEntryIdFloat) { + ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false); + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + +static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt, false); + ir_link_new_instruction(result, &instruction->base); return dest_type; } @@ -19899,6 +20135,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction); + case IrInstructionIdIntCast: + return ir_analyze_instruction_int_cast(ira, (IrInstructionIntCast *)instruction); + case IrInstructionIdFloatCast: + return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); + case IrInstructionIdIntToFloat: + return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); + case IrInstructionIdFloatToInt: + return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); case IrInstructionIdBoolNot: @@ -20242,6 +20486,10 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPromiseResultType: case IrInstructionIdSqrt: case IrInstructionIdAtomicLoad: + case IrInstructionIdIntCast: + case IrInstructionIdFloatCast: + case IrInstructionIdIntToFloat: + case IrInstructionIdFloatToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 43907fa9d4..b5722c52fa 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -648,6 +648,38 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) fprintf(irp->f, ")"); } +static void ir_print_int_cast(IrPrint *irp, IrInstructionIntCast *instruction) { + fprintf(irp->f, "@intCast("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_float_cast(IrPrint *irp, IrInstructionFloatCast *instruction) { + fprintf(irp->f, "@floatCast("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { + fprintf(irp->f, "@intToFloat("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_float_to_int(IrPrint *irp, IrInstructionFloatToInt *instruction) { + fprintf(irp->f, "@floatToInt("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, "@IntType("); ir_print_other_instruction(irp, instruction->is_signed); @@ -1417,6 +1449,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTruncate: ir_print_truncate(irp, (IrInstructionTruncate *)instruction); break; + case IrInstructionIdIntCast: + ir_print_int_cast(irp, (IrInstructionIntCast *)instruction); + break; + case IrInstructionIdFloatCast: + ir_print_float_cast(irp, (IrInstructionFloatCast *)instruction); + break; + case IrInstructionIdIntToFloat: + ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); + break; + case IrInstructionIdFloatToInt: + ir_print_float_to_int(irp, (IrInstructionFloatToInt *)instruction); + break; case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; diff --git a/src/main.cpp b/src/main.cpp index c63a143bff..0fe12bb0cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,7 +34,7 @@ static int usage(const char *arg0) { " --assembly [source] add assembly file to build\n" " --cache-dir [path] override the cache directory\n" " --color [auto|off|on] enable or disable colored error messages\n" - " --emit [filetype] emit a specific file format as compilation output\n" + " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " --enable-timing-info print timing diagnostics\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" " --name [name] override output name\n" diff --git a/std/array_list.zig b/std/array_list.zig index fd1d5cbe26..b71f5be6ab 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -185,23 +185,23 @@ test "basic ArrayList test" { { var i: usize = 0; while (i < 10) : (i += 1) { - list.append(i32(i + 1)) catch unreachable; + list.append(@intCast(i32, i + 1)) catch unreachable; } } { var i: usize = 0; while (i < 10) : (i += 1) { - assert(list.items[i] == i32(i + 1)); + assert(list.items[i] == @intCast(i32, i + 1)); } } for (list.toSlice()) |v, i| { - assert(v == i32(i + 1)); + assert(v == @intCast(i32, i + 1)); } for (list.toSliceConst()) |v, i| { - assert(v == i32(i + 1)); + assert(v == @intCast(i32, i + 1)); } assert(list.pop() == 10); diff --git a/std/base64.zig b/std/base64.zig index d27bcbd201..45c8e22c7e 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -99,7 +99,7 @@ pub const Base64Decoder = struct { assert(!result.char_in_alphabet[c]); assert(c != pad_char); - result.char_to_index[c] = u8(i); + result.char_to_index[c] = @intCast(u8, i); result.char_in_alphabet[c] = true; } @@ -284,7 +284,7 @@ pub const Base64DecoderUnsafe = struct { }; for (alphabet_chars) |c, i| { assert(c != pad_char); - result.char_to_index[c] = u8(i); + result.char_to_index[c] = @intCast(u8, i); } return result; } diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index f0a9766c00..947133e4cf 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -79,7 +79,7 @@ fn Blake2s(comptime out_len: usize) type { mem.copy(u32, d.h[0..], iv[0..]); // No key plus default parameters - d.h[0] ^= 0x01010000 ^ u32(out_len >> 3); + d.h[0] ^= 0x01010000 ^ @intCast(u32, out_len >> 3); d.t = 0; d.buf_len = 0; } @@ -110,7 +110,7 @@ fn Blake2s(comptime out_len: usize) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); } pub fn final(d: *Self, out: []u8) void { @@ -144,7 +144,7 @@ fn Blake2s(comptime out_len: usize) type { } v[12] ^= @truncate(u32, d.t); - v[13] ^= u32(d.t >> 32); + v[13] ^= @intCast(u32, d.t >> 32); if (last) v[14] = ~v[14]; const rounds = comptime []RoundParam{ @@ -345,7 +345,7 @@ fn Blake2b(comptime out_len: usize) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); } pub fn final(d: *Self, out: []u8) void { @@ -377,7 +377,7 @@ fn Blake2b(comptime out_len: usize) type { } v[12] ^= @truncate(u64, d.t); - v[13] ^= u64(d.t >> 64); + v[13] ^= @intCast(u64, d.t >> 64); if (last) v[14] = ~v[14]; const rounds = comptime []RoundParam{ diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index c0d1732d37..23fe2313a0 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -78,7 +78,7 @@ pub const Md5 = struct { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); // Md5 uses the bottom 64-bits for length padding d.total_len +%= b.len; @@ -103,9 +103,9 @@ pub const Md5 = struct { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[56] = u8(d.total_len & 0x1f) << 3; + d.buf[56] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 8) : (i += 1) { - d.buf[56 + i] = u8(len & 0xff); + d.buf[56 + i] = @intCast(u8, len & 0xff); len >>= 8; } diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 9e46fc9239..5c91590c88 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -78,7 +78,7 @@ pub const Sha1 = struct { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.total_len += b.len; } @@ -102,9 +102,9 @@ pub const Sha1 = struct { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[63] = u8(d.total_len & 0x1f) << 3; + d.buf[63] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 8) : (i += 1) { - d.buf[63 - i] = u8(len & 0xff); + d.buf[63 - i] = @intCast(u8, len & 0xff); len >>= 8; } diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index d1375d73e8..d1b915835c 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -131,7 +131,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.total_len += b.len; } @@ -155,9 +155,9 @@ fn Sha2_32(comptime params: Sha2Params32) type { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[63] = u8(d.total_len & 0x1f) << 3; + d.buf[63] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 8) : (i += 1) { - d.buf[63 - i] = u8(len & 0xff); + d.buf[63 - i] = @intCast(u8, len & 0xff); len >>= 8; } @@ -472,7 +472,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { // Copy any remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.total_len += b.len; } @@ -496,9 +496,9 @@ fn Sha2_64(comptime params: Sha2Params64) type { // Append message length. var i: usize = 1; var len = d.total_len >> 5; - d.buf[127] = u8(d.total_len & 0x1f) << 3; + d.buf[127] = @intCast(u8, d.total_len & 0x1f) << 3; while (i < 16) : (i += 1) { - d.buf[127 - i] = u8(len & 0xff); + d.buf[127 - i] = @intCast(u8, len & 0xff); len >>= 8; } diff --git a/std/debug/index.zig b/std/debug/index.zig index fb1dac537c..198e0f90f6 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -554,7 +554,7 @@ const LineNumberProgram = struct { const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name); errdefer self.file_entries.allocator.free(file_name); return LineInfo{ - .line = if (self.prev_line >= 0) usize(self.prev_line) else 0, + .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, .column = self.prev_column, .file_name = file_name, .allocator = self.file_entries.allocator, @@ -1070,7 +1070,7 @@ fn readULeb128(in_stream: var) !u64 { var operand: u64 = undefined; - if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; + if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; result |= operand; @@ -1089,13 +1089,13 @@ fn readILeb128(in_stream: var) !i64 { var operand: i64 = undefined; - if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand)) return error.InvalidDebugInfo; + if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; result |= operand; shift += 7; if ((byte & 0b10000000) == 0) { - if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << u6(shift)); + if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); return result; } } diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index a906b714ab..a5fb692857 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -29,11 +29,11 @@ pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: Ro switch (mode) { RoundMode.Decimal => { if (float_decimal.exp >= 0) { - round_digit = precision + usize(float_decimal.exp); + round_digit = precision + @intCast(usize, float_decimal.exp); } else { // if a small negative exp, then adjust we need to offset by the number // of leading zeros that will occur. - const min_exp_required = usize(-float_decimal.exp); + const min_exp_required = @intCast(usize, -float_decimal.exp); if (precision > min_exp_required) { round_digit = precision - min_exp_required; } @@ -107,16 +107,16 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { // normalize the midpoint const e = math.frexp(val).exponent; - var exp = i16(math.floor(307 + f64(e) * 0.30103)); + var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); if (exp < 20) { exp = 20; - } else if (usize(exp) >= lookup_table.len) { - exp = i16(lookup_table.len - 1); + } else if (@intCast(usize, exp) >= lookup_table.len) { + exp = @intCast(i16, lookup_table.len - 1); } - var mid = lookup_table[usize(exp)]; + var mid = lookup_table[@intCast(usize, exp)]; mid = hpProd(mid, val); - const lten = lookup_table[usize(exp)].val; + const lten = lookup_table[@intCast(usize, exp)].val; exp -= 307; @@ -168,25 +168,25 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { // the 0-index for this extra digit. var buf_index: usize = 1; while (true) { - var hdig = u8(math.floor(high.val)); - if ((high.val == f64(hdig)) and (high.off < 0)) hdig -= 1; + var hdig = @floatToInt(u8, math.floor(high.val)); + if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; - var ldig = u8(math.floor(low.val)); - if ((low.val == f64(ldig)) and (low.off < 0)) ldig -= 1; + var ldig = @floatToInt(u8, math.floor(low.val)); + if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; if (ldig != hdig) break; buffer[buf_index] = hdig + '0'; buf_index += 1; - high.val -= f64(hdig); - low.val -= f64(ldig); + high.val -= @intToFloat(f64, hdig); + low.val -= @intToFloat(f64, ldig); hpMul10(&high); hpMul10(&low); } const tmp = (high.val + low.val) / 2.0; - var mdig = u8(math.floor(tmp + 0.5)); - if ((f64(mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; + var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); + if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; buffer[buf_index] = mdig + '0'; buf_index += 1; @@ -304,7 +304,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); - var mid = u128(val); + var mid = @floatToInt(u128, val); var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); @@ -314,11 +314,11 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { low -= 1; } - var l64 = u64(low % pow19); - const lf = u64((low / pow19) % pow19); + var l64 = @intCast(u64, low % pow19); + const lf = @intCast(u64, (low / pow19) % pow19); - var h64 = u64(high % pow19); - const hf = u64((high / pow19) % pow19); + var h64 = @intCast(u64, high % pow19); + const hf = @intCast(u64, (high / pow19) % pow19); if (lf != hf) { l64 = lf; @@ -348,7 +348,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { return FloatDecimal{ .digits = buffer[0..buf_index], - .exp = i32(buf_index) + mi, + .exp = @intCast(i32, buf_index) + mi, }; } @@ -359,33 +359,33 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { fn errolFixed(val: f64, buffer: []u8) FloatDecimal { assert((val >= 16.0) and (val < 9.007199254740992e15)); - const u = u64(val); - const n = f64(u); + const u = @floatToInt(u64, val); + const n = @intToFloat(f64, u); var mid = val - n; var lo = ((fpprev(val) - n) + mid) / 2.0; var hi = ((fpnext(val) - n) + mid) / 2.0; var buf_index = u64toa(u, buffer); - var exp = i32(buf_index); + var exp = @intCast(i32, buf_index); var j = buf_index; buffer[j] = 0; if (mid != 0.0) { while (mid != 0.0) { lo *= 10.0; - const ldig = i32(lo); - lo -= f64(ldig); + const ldig = @floatToInt(i32, lo); + lo -= @intToFloat(f64, ldig); mid *= 10.0; - const mdig = i32(mid); - mid -= f64(mdig); + const mdig = @floatToInt(i32, mid); + mid -= @intToFloat(f64, mdig); hi *= 10.0; - const hdig = i32(hi); - hi -= f64(hdig); + const hdig = @floatToInt(i32, hi); + hi -= @intToFloat(f64, hdig); - buffer[j] = u8(mdig + '0'); + buffer[j] = @intCast(u8, mdig + '0'); j += 1; if (hdig != ldig or j > 50) break; @@ -452,7 +452,7 @@ fn u64toa(value_param: u64, buffer: []u8) usize { var buf_index: usize = 0; if (value < kTen8) { - const v = u32(value); + const v = @intCast(u32, value); if (v < 10000) { const d1: u32 = (v / 100) << 1; const d2: u32 = (v % 100) << 1; @@ -507,8 +507,8 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buf_index += 1; } } else if (value < kTen16) { - const v0: u32 = u32(value / kTen8); - const v1: u32 = u32(value % kTen8); + const v0: u32 = @intCast(u32, value / kTen8); + const v1: u32 = @intCast(u32, value % kTen8); const b0: u32 = v0 / 10000; const c0: u32 = v0 % 10000; @@ -578,11 +578,11 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buffer[buf_index] = c_digits_lut[d8 + 1]; buf_index += 1; } else { - const a = u32(value / kTen16); // 1 to 1844 + const a = @intCast(u32, value / kTen16); // 1 to 1844 value %= kTen16; if (a < 10) { - buffer[buf_index] = '0' + u8(a); + buffer[buf_index] = '0' + @intCast(u8, a); buf_index += 1; } else if (a < 100) { const i: u32 = a << 1; @@ -591,7 +591,7 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buffer[buf_index] = c_digits_lut[i + 1]; buf_index += 1; } else if (a < 1000) { - buffer[buf_index] = '0' + u8(a / 100); + buffer[buf_index] = '0' + @intCast(u8, a / 100); buf_index += 1; const i: u32 = (a % 100) << 1; @@ -612,8 +612,8 @@ fn u64toa(value_param: u64, buffer: []u8) usize { buf_index += 1; } - const v0 = u32(value / kTen8); - const v1 = u32(value % kTen8); + const v0 = @intCast(u32, value / kTen8); + const v1 = @intCast(u32, value % kTen8); const b0: u32 = v0 / 10000; const c0: u32 = v0 % 10000; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 90d3a559c4..f4dfa0e324 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -5,6 +5,7 @@ const assert = debug.assert; const mem = std.mem; const builtin = @import("builtin"); const errol = @import("errol/index.zig"); +const lossyCast = std.math.lossyCast; const max_int_digits = 65; @@ -463,7 +464,7 @@ pub fn formatFloatDecimal( errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); @@ -492,7 +493,7 @@ pub fn formatFloatDecimal( // Zero-fill until we reach significant digits or run out of precision. if (float_decimal.exp <= 0) { - const zero_digit_count = usize(-float_decimal.exp); + const zero_digit_count = @intCast(usize, -float_decimal.exp); const zeros_to_print = math.min(zero_digit_count, precision); var i: usize = 0; @@ -521,7 +522,7 @@ pub fn formatFloatDecimal( } } else { // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); @@ -547,7 +548,7 @@ pub fn formatFloatDecimal( // Zero-fill until we reach significant digits or run out of precision. if (float_decimal.exp < 0) { - const zero_digit_count = usize(-float_decimal.exp); + const zero_digit_count = @intCast(usize, -float_decimal.exp); var i: usize = 0; while (i < zero_digit_count) : (i += 1) { @@ -578,7 +579,7 @@ pub fn formatBytes( 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), else => unreachable, }; - const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude)); + const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); const suffix = switch (radix) { 1000 => mags_si[magnitude], 1024 => mags_iec[magnitude], @@ -628,15 +629,15 @@ fn formatIntSigned( if (value < 0) { const minus_sign: u8 = '-'; try output(context, (*[1]u8)(&minus_sign)[0..]); - const new_value = uint(-(value + 1)) + 1; + const new_value = @intCast(uint, -(value + 1)) + 1; const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } else if (width == 0) { - return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output); + return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); } else { const plus_sign: u8 = '+'; try output(context, (*[1]u8)(&plus_sign)[0..]); - const new_value = uint(value); + const new_value = @intCast(uint, value); const new_width = if (width == 0) 0 else (width - 1); return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } @@ -660,7 +661,7 @@ fn formatIntUnsigned( while (true) { const digit = a % base; index -= 1; - buf[index] = digitToChar(u8(digit), uppercase); + buf[index] = digitToChar(@intCast(u8, digit), uppercase); a /= base; if (a == 0) break; } diff --git a/std/hash/crc.zig b/std/hash/crc.zig index ec831cdc2e..c455140785 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -26,7 +26,7 @@ pub fn Crc32WithPoly(comptime poly: u32) type { var tables: [8][256]u32 = undefined; for (tables[0]) |*e, i| { - var crc = u32(i); + var crc = @intCast(u32, i); var j: usize = 0; while (j < 8) : (j += 1) { if (crc & 1 == 1) { @@ -122,7 +122,7 @@ pub fn Crc32SmallWithPoly(comptime poly: u32) type { var table: [16]u32 = undefined; for (table) |*e, i| { - var crc = u32(i * 16); + var crc = @intCast(u32, i * 16); var j: usize = 0; while (j < 8) : (j += 1) { if (crc & 1 == 1) { diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index 8a90308a46..cdad77e59e 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -81,7 +81,7 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) // Remainder for next pass. mem.copy(u8, d.buf[d.buf_len..], b[off..]); - d.buf_len += u8(b[off..].len); + d.buf_len += @intCast(u8, b[off..].len); d.msg_len +%= @truncate(u8, b.len); } @@ -233,7 +233,7 @@ test "siphash64-2-4 sanity" { var buffer: [64]u8 = undefined; for (vectors) |vector, i| { - buffer[i] = u8(i); + buffer[i] = @intCast(u8, i); const expected = mem.readInt(vector, u64, Endian.Little); debug.assert(siphash.hash(test_key, buffer[0..i]) == expected); @@ -312,7 +312,7 @@ test "siphash128-2-4 sanity" { var buffer: [64]u8 = undefined; for (vectors) |vector, i| { - buffer[i] = u8(i); + buffer[i] = @intCast(u8, i); const expected = mem.readInt(vector, u128, Endian.Little); debug.assert(siphash.hash(test_key, buffer[0..i]) == expected); diff --git a/std/heap.zig b/std/heap.zig index 172bc24118..2a2c8c0b59 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -408,7 +408,7 @@ fn testAllocator(allocator: *mem.Allocator) !void { for (slice) |*item, i| { item.* = try allocator.create(i32); - item.*.* = i32(i); + item.*.* = @intCast(i32, i); } for (slice) |item, i| { diff --git a/std/json.zig b/std/json.zig index 8bbee981e3..2930cd21bb 100644 --- a/std/json.zig +++ b/std/json.zig @@ -180,7 +180,7 @@ pub const StreamingParser = struct { pub fn fromInt(x: var) State { debug.assert(x == 0 or x == 1); const T = @TagType(State); - return State(T(x)); + return State(@intCast(T, x)); } }; diff --git a/std/math/acos.zig b/std/math/acos.zig index 284f73fc91..54844e8f6e 100644 --- a/std/math/acos.zig +++ b/std/math/acos.zig @@ -95,12 +95,12 @@ fn acos64(x: f64) f64 { const pio2_lo: f64 = 6.12323399573676603587e-17; const ux = @bitCast(u64, x); - const hx = u32(ux >> 32); + const hx = @intCast(u32, ux >> 32); const ix = hx & 0x7FFFFFFF; // |x| >= 1 or nan if (ix >= 0x3FF00000) { - const lx = u32(ux & 0xFFFFFFFF); + const lx = @intCast(u32, ux & 0xFFFFFFFF); // acos(1) = 0, acos(-1) = pi if ((ix - 0x3FF00000) | lx == 0) { diff --git a/std/math/asin.zig b/std/math/asin.zig index 2ee3aa6048..30b3a57e32 100644 --- a/std/math/asin.zig +++ b/std/math/asin.zig @@ -87,12 +87,12 @@ fn asin64(x: f64) f64 { const pio2_lo: f64 = 6.12323399573676603587e-17; const ux = @bitCast(u64, x); - const hx = u32(ux >> 32); + const hx = @intCast(u32, ux >> 32); const ix = hx & 0x7FFFFFFF; // |x| >= 1 or nan if (ix >= 0x3FF00000) { - const lx = u32(ux & 0xFFFFFFFF); + const lx = @intCast(u32, ux & 0xFFFFFFFF); // asin(1) = +-pi/2 with inexact if ((ix - 0x3FF00000) | lx == 0) { diff --git a/std/math/atan.zig b/std/math/atan.zig index 7eceef98e3..6ca94dd84a 100644 --- a/std/math/atan.zig +++ b/std/math/atan.zig @@ -138,7 +138,7 @@ fn atan64(x_: f64) f64 { var x = x_; var ux = @bitCast(u64, x); - var ix = u32(ux >> 32); + var ix = @intCast(u32, ux >> 32); const sign = ix >> 31; ix &= 0x7FFFFFFF; @@ -159,7 +159,7 @@ fn atan64(x_: f64) f64 { // |x| < 2^(-27) if (ix < 0x3E400000) { if (ix < 0x00100000) { - math.forceEval(f32(x)); + math.forceEval(@floatCast(f32, x)); } return x; } diff --git a/std/math/atan2.zig b/std/math/atan2.zig index f0a8e67c46..b3e45ba045 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -124,12 +124,12 @@ fn atan2_64(y: f64, x: f64) f64 { } var ux = @bitCast(u64, x); - var ix = u32(ux >> 32); - var lx = u32(ux & 0xFFFFFFFF); + var ix = @intCast(u32, ux >> 32); + var lx = @intCast(u32, ux & 0xFFFFFFFF); var uy = @bitCast(u64, y); - var iy = u32(uy >> 32); - var ly = u32(uy & 0xFFFFFFFF); + var iy = @intCast(u32, uy >> 32); + var ly = @intCast(u32, uy & 0xFFFFFFFF); // x = 1.0 if ((ix -% 0x3FF00000) | lx == 0) { diff --git a/std/math/atanh.zig b/std/math/atanh.zig index 8ca0cc85bc..4ae8a66bc0 100644 --- a/std/math/atanh.zig +++ b/std/math/atanh.zig @@ -62,7 +62,7 @@ fn atanh_64(x: f64) f64 { if (e < 0x3FF - 32) { // underflow if (e == 0) { - math.forceEval(f32(y)); + math.forceEval(@floatCast(f32, y)); } } // |x| < 0.5 diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 5e15cfb895..21d9347c01 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -135,7 +135,7 @@ pub const Int = struct { self.positive = value >= 0; self.len = 0; - var w_value: UT = if (value < 0) UT(-value) else UT(value); + var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value); if (info.bits <= Limb.bit_count) { self.limbs[0] = Limb(w_value); @@ -198,7 +198,7 @@ pub const Int = struct { var r: UT = 0; if (@sizeOf(UT) <= @sizeOf(Limb)) { - r = UT(self.limbs[0]); + r = @intCast(UT, self.limbs[0]); } else { for (self.limbs[0..self.len]) |_, ri| { const limb = self.limbs[self.len - ri - 1]; @@ -210,7 +210,7 @@ pub const Int = struct { if (!T.is_signed) { return if (self.positive) r else error.NegativeIntoUnsigned; } else { - return if (self.positive) T(r) else -T(r); + return if (self.positive) @intCast(T, r) else -@intCast(T, r); } }, else => { @@ -295,7 +295,7 @@ pub const Int = struct { for (self.limbs[0..self.len]) |limb| { var shift: usize = 0; while (shift < Limb.bit_count) : (shift += base_shift) { - const r = u8((limb >> Log2Limb(shift)) & Limb(base - 1)); + const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & Limb(base - 1)); const ch = try digitToChar(r, base); try digits.append(ch); } @@ -329,7 +329,7 @@ pub const Int = struct { var r_word = r.limbs[0]; var i: usize = 0; while (i < digits_per_limb) : (i += 1) { - const ch = try digitToChar(u8(r_word % base), base); + const ch = try digitToChar(@intCast(u8, r_word % base), base); r_word /= base; try digits.append(ch); } @@ -340,7 +340,7 @@ pub const Int = struct { var r_word = q.limbs[0]; while (r_word != 0) { - const ch = try digitToChar(u8(r_word % base), base); + const ch = try digitToChar(@intCast(u8, r_word % base), base); r_word /= base; try digits.append(ch); } @@ -801,7 +801,7 @@ pub const Int = struct { q.limbs[i - t - 1] = @maxValue(Limb); } else { const num = (DoubleLimb(x.limbs[i]) << Limb.bit_count) | DoubleLimb(x.limbs[i - 1]); - const z = Limb(num / DoubleLimb(y.limbs[t])); + const z = @intCast(Limb, num / DoubleLimb(y.limbs[t])); q.limbs[i - t - 1] = if (z > @maxValue(Limb)) @maxValue(Limb) else Limb(z); } @@ -860,7 +860,7 @@ pub const Int = struct { debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1); const limb_shift = shift / Limb.bit_count + 1; - const interior_limb_shift = Log2Limb(shift % Limb.bit_count); + const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); var carry: Limb = 0; var i: usize = 0; @@ -869,7 +869,7 @@ pub const Int = struct { const dst_i = src_i + limb_shift; const src_digit = a[src_i]; - r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift)); + r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift)); carry = (src_digit << interior_limb_shift); } @@ -898,7 +898,7 @@ pub const Int = struct { debug.assert(r.len >= a.len - (shift / Limb.bit_count)); const limb_shift = shift / Limb.bit_count; - const interior_limb_shift = Log2Limb(shift % Limb.bit_count); + const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); var carry: Limb = 0; var i: usize = 0; @@ -908,7 +908,7 @@ pub const Int = struct { const src_digit = a[src_i]; r[dst_i] = carry | (src_digit >> interior_limb_shift); - carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift)); + carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift)); } } diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig index cd3b71ca8a..c067c5155a 100644 --- a/std/math/cbrt.zig +++ b/std/math/cbrt.zig @@ -54,7 +54,7 @@ fn cbrt32(x: f32) f32 { r = t * t * t; t = t * (f64(x) + x + r) / (x + r + r); - return f32(t); + return @floatCast(f32, t); } fn cbrt64(x: f64) f64 { @@ -69,7 +69,7 @@ fn cbrt64(x: f64) f64 { const P4: f64 = 0.145996192886612446982; var u = @bitCast(u64, x); - var hx = u32(u >> 32) & 0x7FFFFFFF; + var hx = @intCast(u32, u >> 32) & 0x7FFFFFFF; // cbrt(nan, inf) = itself if (hx >= 0x7FF00000) { @@ -79,7 +79,7 @@ fn cbrt64(x: f64) f64 { // cbrt to ~5bits if (hx < 0x00100000) { u = @bitCast(u64, x * 0x1.0p54); - hx = u32(u >> 32) & 0x7FFFFFFF; + hx = @intCast(u32, u >> 32) & 0x7FFFFFFF; // cbrt(0) is itself if (hx == 0) { diff --git a/std/math/ceil.zig b/std/math/ceil.zig index a189bb66d2..1c429504e8 100644 --- a/std/math/ceil.zig +++ b/std/math/ceil.zig @@ -20,7 +20,7 @@ pub fn ceil(x: var) @typeOf(x) { fn ceil32(x: f32) f32 { var u = @bitCast(u32, x); - var e = i32((u >> 23) & 0xFF) - 0x7F; + var e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; var m: u32 = undefined; // TODO: Shouldn't need this explicit check. @@ -31,7 +31,7 @@ fn ceil32(x: f32) f32 { if (e >= 23) { return x; } else if (e >= 0) { - m = u32(0x007FFFFF) >> u5(e); + m = u32(0x007FFFFF) >> @intCast(u5, e); if (u & m == 0) { return x; } diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index 9bfe5fe724..de60f2546d 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn atan(z: var) Complex(@typeOf(z.re)) { +pub fn atan(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => atan32(z), @@ -25,11 +25,11 @@ fn redupif32(x: f32) f32 { t -= 0.5; } - const u = f32(i32(t)); + const u = @intToFloat(f32, @floatToInt(i32, t)); return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan32(z: *const Complex(f32)) Complex(f32) { +fn atan32(z: Complex(f32)) Complex(f32) { const maxnum = 1.0e38; const x = z.re; @@ -74,11 +74,11 @@ fn redupif64(x: f64) f64 { t -= 0.5; } - const u = f64(i64(t)); + const u = @intToFloat(f64, @floatToInt(i64, t)); return ((x - u * DP1) - u * DP2) - t * DP3; } -fn atan64(z: *const Complex(f64)) Complex(f64) { +fn atan64(z: Complex(f64)) Complex(f64) { const maxnum = 1.0e308; const x = z.re; diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index c2f9a47b8d..a2e31631ea 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -83,12 +83,12 @@ fn cosh64(z: *const Complex(f64)) Complex(f64) { const y = z.im; const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); const ix = hx & 0x7fffffff; const fy = @bitCast(u64, y); - const hy = u32(fy >> 32); + const hy = @intCast(u32, fy >> 32); const ly = @truncate(u32, fy); const iy = hy & 0x7fffffff; diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 44c354f246..48fb132d97 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -6,7 +6,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; -pub fn exp(z: var) Complex(@typeOf(z.re)) { +pub fn exp(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { @@ -16,7 +16,7 @@ pub fn exp(z: var) Complex(@typeOf(z.re)) { }; } -fn exp32(z: *const Complex(f32)) Complex(f32) { +fn exp32(z: Complex(f32)) Complex(f32) { @setFloatMode(this, @import("builtin").FloatMode.Strict); const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955 @@ -63,7 +63,7 @@ fn exp32(z: *const Complex(f32)) Complex(f32) { } } -fn exp64(z: *const Complex(f64)) Complex(f64) { +fn exp64(z: Complex(f64)) Complex(f64) { const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710 const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2 diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index a56c2ef2eb..e919ef6bec 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) { +pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { @@ -20,11 +20,12 @@ fn frexp_exp32(x: f32, expt: *i32) f32 { const exp_x = math.exp(x - kln2); const hx = @bitCast(u32, exp_x); - expt.* = i32(hx >> 23) - (0x7f + 127) + k; + // TODO zig should allow this cast implicitly because it should know the value is in range + expt.* = @intCast(i32, hx >> 23) - (0x7f + 127) + k; return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23)); } -fn ldexp_cexp32(z: *const Complex(f32), expt: i32) Complex(f32) { +fn ldexp_cexp32(z: Complex(f32), expt: i32) Complex(f32) { var ex_expt: i32 = undefined; const exp_x = frexp_exp32(z.re, &ex_expt); const exptf = expt + ex_expt; @@ -45,16 +46,16 @@ fn frexp_exp64(x: f64, expt: *i32) f64 { const exp_x = math.exp(x - kln2); const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); - expt.* = i32(hx >> 20) - (0x3ff + 1023) + k; + expt.* = @intCast(i32, hx >> 20) - (0x3ff + 1023) + k; const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20); return @bitCast(f64, (u64(high_word) << 32) | lx); } -fn ldexp_cexp64(z: *const Complex(f64), expt: i32) Complex(f64) { +fn ldexp_cexp64(z: Complex(f64), expt: i32) Complex(f64) { var ex_expt: i32 = undefined; const exp_x = frexp_exp64(z.re, &ex_expt); const exptf = i64(expt + ex_expt); diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 3d196bfd50..ab23c5c74d 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -6,7 +6,7 @@ const Complex = cmath.Complex; const ldexp_cexp = @import("ldexp.zig").ldexp_cexp; -pub fn sinh(z: var) Complex(@typeOf(z.re)) { +pub fn sinh(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => sinh32(z), @@ -15,7 +15,7 @@ pub fn sinh(z: var) Complex(@typeOf(z.re)) { }; } -fn sinh32(z: *const Complex(f32)) Complex(f32) { +fn sinh32(z: Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -78,17 +78,17 @@ fn sinh32(z: *const Complex(f32)) Complex(f32) { return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y)); } -fn sinh64(z: *const Complex(f64)) Complex(f64) { +fn sinh64(z: Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); const ix = hx & 0x7fffffff; const fy = @bitCast(u64, y); - const hy = u32(fy >> 32); + const hy = @intCast(u32, fy >> 32); const ly = @truncate(u32, fy); const iy = hy & 0x7fffffff; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index caa3bd2868..47367816f7 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -49,10 +49,16 @@ fn sqrt32(z: Complex(f32)) Complex(f32) { if (dx >= 0) { const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5); - return Complex(f32).new(f32(t), f32(dy / (2.0 * t))); + return Complex(f32).new( + @floatCast(f32, t), + @floatCast(f32, dy / (2.0 * t)), + ); } else { const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5); - return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y))); + return Complex(f32).new( + @floatCast(f32, math.fabs(y) / (2.0 * t)), + @floatCast(f32, math.copysign(f64, t, y)), + ); } } diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index 1d754838a3..e48d438783 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -4,7 +4,7 @@ const math = std.math; const cmath = math.complex; const Complex = cmath.Complex; -pub fn tanh(z: var) Complex(@typeOf(z.re)) { +pub fn tanh(z: var) @typeOf(z) { const T = @typeOf(z.re); return switch (T) { f32 => tanh32(z), @@ -13,7 +13,7 @@ pub fn tanh(z: var) Complex(@typeOf(z.re)) { }; } -fn tanh32(z: *const Complex(f32)) Complex(f32) { +fn tanh32(z: Complex(f32)) Complex(f32) { const x = z.re; const y = z.im; @@ -51,12 +51,14 @@ fn tanh32(z: *const Complex(f32)) Complex(f32) { return Complex(f32).new((beta * rho * s) / den, t / den); } -fn tanh64(z: *const Complex(f64)) Complex(f64) { +fn tanh64(z: Complex(f64)) Complex(f64) { const x = z.re; const y = z.im; const fx = @bitCast(u64, x); - const hx = u32(fx >> 32); + // TODO: zig should allow this conversion implicitly because it can notice that the value necessarily + // fits in range. + const hx = @intCast(u32, fx >> 32); const lx = @truncate(u32, fx); const ix = hx & 0x7fffffff; diff --git a/std/math/cos.zig b/std/math/cos.zig index 5e5ec4f1cb..71d5e4a8f6 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -55,7 +55,7 @@ fn cos32(x_: f32) f32 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; @@ -106,7 +106,7 @@ fn cos64(x_: f64) f64 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; diff --git a/std/math/cosh.zig b/std/math/cosh.zig index fa46219986..52beafb642 100644 --- a/std/math/cosh.zig +++ b/std/math/cosh.zig @@ -49,7 +49,7 @@ fn cosh32(x: f32) f32 { fn cosh64(x: f64) f64 { const u = @bitCast(u64, x); - const w = u32(u >> 32); + const w = @intCast(u32, u >> 32); const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); // TODO: Shouldn't need this explicit check. diff --git a/std/math/exp.zig b/std/math/exp.zig index 76dc47d04b..d6185d4f0b 100644 --- a/std/math/exp.zig +++ b/std/math/exp.zig @@ -29,7 +29,7 @@ fn exp32(x_: f32) f32 { var x = x_; var hx = @bitCast(u32, x); - const sign = i32(hx >> 31); + const sign = @intCast(i32, hx >> 31); hx &= 0x7FFFFFFF; if (math.isNan(x)) { @@ -63,12 +63,12 @@ fn exp32(x_: f32) f32 { if (hx > 0x3EB17218) { // |x| > 1.5 * ln2 if (hx > 0x3F851592) { - k = i32(invln2 * x + half[usize(sign)]); + k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]); } else { k = 1 - sign - sign; } - const fk = f32(k); + const fk = @intToFloat(f32, k); hi = x - fk * ln2hi; lo = fk * ln2lo; x = hi - lo; @@ -110,7 +110,7 @@ fn exp64(x_: f64) f64 { var x = x_; var ux = @bitCast(u64, x); var hx = ux >> 32; - const sign = i32(hx >> 31); + const sign = @intCast(i32, hx >> 31); hx &= 0x7FFFFFFF; if (math.isNan(x)) { @@ -148,12 +148,12 @@ fn exp64(x_: f64) f64 { if (hx > 0x3EB17218) { // |x| >= 1.5 * ln2 if (hx > 0x3FF0A2B2) { - k = i32(invln2 * x + half[usize(sign)]); + k = @floatToInt(i32, invln2 * x + half[@intCast(usize, sign)]); } else { k = 1 - sign - sign; } - const dk = f64(k); + const dk = @intToFloat(f64, k); hi = x - dk * ln2hi; lo = dk * ln2lo; x = hi - lo; diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 62a3eb85b6..90ea088181 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -38,8 +38,8 @@ const exp2ft = []const f64{ fn exp2_32(x: f32) f32 { @setFloatMode(this, @import("builtin").FloatMode.Strict); - const tblsiz = u32(exp2ft.len); - const redux: f32 = 0x1.8p23 / f32(tblsiz); + const tblsiz = @intCast(u32, exp2ft.len); + const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz); const P1: f32 = 0x1.62e430p-1; const P2: f32 = 0x1.ebfbe0p-3; const P3: f32 = 0x1.c6b348p-5; @@ -89,7 +89,7 @@ fn exp2_32(x: f32) f32 { var r: f64 = exp2ft[i0]; const t: f64 = r * z; r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4); - return f32(r * uk); + return @floatCast(f32, r * uk); } const exp2dt = []f64{ @@ -355,8 +355,8 @@ const exp2dt = []f64{ fn exp2_64(x: f64) f64 { @setFloatMode(this, @import("builtin").FloatMode.Strict); - const tblsiz = u32(exp2dt.len / 2); - const redux: f64 = 0x1.8p52 / f64(tblsiz); + const tblsiz = @intCast(u32, exp2dt.len / 2); + const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz); const P1: f64 = 0x1.62e42fefa39efp-1; const P2: f64 = 0x1.ebfbdff82c575p-3; const P3: f64 = 0x1.c6b08d704a0a6p-5; @@ -364,7 +364,7 @@ fn exp2_64(x: f64) f64 { const P5: f64 = 0x1.5d88003875c74p-10; const ux = @bitCast(u64, x); - const ix = u32(ux >> 32) & 0x7FFFFFFF; + const ix = @intCast(u32, ux >> 32) & 0x7FFFFFFF; // TODO: This should be handled beneath. if (math.isNan(x)) { @@ -386,7 +386,7 @@ fn exp2_64(x: f64) f64 { if (ux >> 63 != 0) { // underflow if (x <= -1075 or x - 0x1.0p52 + 0x1.0p52 != x) { - math.forceEval(f32(-0x1.0p-149 / x)); + math.forceEval(@floatCast(f32, -0x1.0p-149 / x)); } if (x <= -1075) { return 0; diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 11af1b215f..438e44ccce 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -78,8 +78,8 @@ fn expm1_32(x_: f32) f32 { kf += 0.5; } - k = i32(kf); - const t = f32(k); + k = @floatToInt(i32, kf); + const t = @intToFloat(f32, k); hi = x - t * ln2_hi; lo = t * ln2_lo; } @@ -123,7 +123,7 @@ fn expm1_32(x_: f32) f32 { } } - const twopk = @bitCast(f32, u32((0x7F +% k) << 23)); + const twopk = @bitCast(f32, @intCast(u32, (0x7F +% k) << 23)); if (k < 0 or k > 56) { var y = x - e + 1.0; @@ -136,7 +136,7 @@ fn expm1_32(x_: f32) f32 { return y - 1.0; } - const uf = @bitCast(f32, u32(0x7F -% k) << 23); + const uf = @bitCast(f32, @intCast(u32, 0x7F -% k) << 23); if (k < 23) { return (x - e + (1 - uf)) * twopk; } else { @@ -158,7 +158,7 @@ fn expm1_64(x_: f64) f64 { var x = x_; const ux = @bitCast(u64, x); - const hx = u32(ux >> 32) & 0x7FFFFFFF; + const hx = @intCast(u32, ux >> 32) & 0x7FFFFFFF; const sign = ux >> 63; if (math.isNegativeInf(x)) { @@ -207,8 +207,8 @@ fn expm1_64(x_: f64) f64 { kf += 0.5; } - k = i32(kf); - const t = f64(k); + k = @floatToInt(i32, kf); + const t = @intToFloat(f64, k); hi = x - t * ln2_hi; lo = t * ln2_lo; } @@ -219,7 +219,7 @@ fn expm1_64(x_: f64) f64 { // |x| < 2^(-54) else if (hx < 0x3C900000) { if (hx < 0x00100000) { - math.forceEval(f32(x)); + math.forceEval(@floatCast(f32, x)); } return x; } else { @@ -252,7 +252,7 @@ fn expm1_64(x_: f64) f64 { } } - const twopk = @bitCast(f64, u64(0x3FF +% k) << 52); + const twopk = @bitCast(f64, @intCast(u64, 0x3FF +% k) << 52); if (k < 0 or k > 56) { var y = x - e + 1.0; @@ -265,7 +265,7 @@ fn expm1_64(x_: f64) f64 { return y - 1.0; } - const uf = @bitCast(f64, u64(0x3FF -% k) << 52); + const uf = @bitCast(f64, @intCast(u64, 0x3FF -% k) << 52); if (k < 20) { return (x - e + (1 - uf)) * twopk; } else { diff --git a/std/math/floor.zig b/std/math/floor.zig index 7d5364787f..79d1097d08 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -20,7 +20,7 @@ pub fn floor(x: var) @typeOf(x) { fn floor32(x: f32) f32 { var u = @bitCast(u32, x); - const e = i32((u >> 23) & 0xFF) - 0x7F; + const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; var m: u32 = undefined; // TODO: Shouldn't need this explicit check. @@ -33,7 +33,7 @@ fn floor32(x: f32) f32 { } if (e >= 0) { - m = u32(0x007FFFFF) >> u5(e); + m = u32(0x007FFFFF) >> @intCast(u5, e); if (u & m == 0) { return x; } diff --git a/std/math/fma.zig b/std/math/fma.zig index 3e9214f35b..21faf4118d 100644 --- a/std/math/fma.zig +++ b/std/math/fma.zig @@ -17,10 +17,10 @@ fn fma32(x: f32, y: f32, z: f32) f32 { const e = (u >> 52) & 0x7FF; if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) { - return f32(xy_z); + return @floatCast(f32, xy_z); } else { // TODO: Handle inexact case with double-rounding - return f32(xy_z); + return @floatCast(f32, xy_z); } } @@ -124,7 +124,7 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) f64 { var sum = dd_add(a, b); if (sum.lo != 0) { var uhii = @bitCast(u64, sum.hi); - const bits_lost = -i32((uhii >> 52) & 0x7FF) - scale + 1; + const bits_lost = -@intCast(i32, (uhii >> 52) & 0x7FF) - scale + 1; if ((bits_lost != 1) == (uhii & 1 != 0)) { const uloi = @bitCast(u64, sum.lo); uhii += 1 - (((uhii ^ uloi) >> 62) & 2); diff --git a/std/math/frexp.zig b/std/math/frexp.zig index b58af0a9bc..dfc790fdd9 100644 --- a/std/math/frexp.zig +++ b/std/math/frexp.zig @@ -30,7 +30,7 @@ fn frexp32(x: f32) frexp32_result { var result: frexp32_result = undefined; var y = @bitCast(u32, x); - const e = i32(y >> 23) & 0xFF; + const e = @intCast(i32, y >> 23) & 0xFF; if (e == 0) { if (x != 0) { @@ -67,7 +67,7 @@ fn frexp64(x: f64) frexp64_result { var result: frexp64_result = undefined; var y = @bitCast(u64, x); - const e = i32(y >> 52) & 0x7FF; + const e = @intCast(i32, y >> 52) & 0x7FF; if (e == 0) { if (x != 0) { diff --git a/std/math/hypot.zig b/std/math/hypot.zig index 494df22ba6..f834f422e6 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -49,7 +49,7 @@ fn hypot32(x: f32, y: f32) f32 { yy *= 0x1.0p-90; } - return z * math.sqrt(f32(f64(x) * x + f64(y) * y)); + return z * math.sqrt(@floatCast(f32, f64(x) * x + f64(y) * y)); } fn sq(hi: *f64, lo: *f64, x: f64) void { diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig index f1f33aff55..a24f580a32 100644 --- a/std/math/ilogb.zig +++ b/std/math/ilogb.zig @@ -23,7 +23,7 @@ const fp_ilogb0 = fp_ilogbnan; fn ilogb32(x: f32) i32 { var u = @bitCast(u32, x); - var e = i32((u >> 23) & 0xFF); + var e = @intCast(i32, (u >> 23) & 0xFF); // TODO: We should be able to merge this with the lower check. if (math.isNan(x)) { @@ -59,7 +59,7 @@ fn ilogb32(x: f32) i32 { fn ilogb64(x: f64) i32 { var u = @bitCast(u64, x); - var e = i32((u >> 52) & 0x7FF); + var e = @intCast(i32, (u >> 52) & 0x7FF); if (math.isNan(x)) { return @maxValue(i32); diff --git a/std/math/index.zig b/std/math/index.zig index 99e5b4ecf1..176293be74 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -227,7 +227,7 @@ pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { /// A negative shift amount results in a right shift. pub fn shl(comptime T: type, a: T, shift_amt: var) T { const abs_shift_amt = absCast(shift_amt); - const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else Log2Int(T)(abs_shift_amt); + const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); if (@typeOf(shift_amt).is_signed) { if (shift_amt >= 0) { @@ -251,7 +251,7 @@ test "math.shl" { /// A negative shift amount results in a lefft shift. pub fn shr(comptime T: type, a: T, shift_amt: var) T { const abs_shift_amt = absCast(shift_amt); - const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else Log2Int(T)(abs_shift_amt); + const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); if (@typeOf(shift_amt).is_signed) { if (shift_amt >= 0) { @@ -473,9 +473,9 @@ fn testRem() void { /// Result is an unsigned integer. pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { const uint = @IntType(false, @typeOf(x).bit_count); - if (x >= 0) return uint(x); + if (x >= 0) return @intCast(uint, x); - return uint(-(x + 1)) + 1; + return @intCast(uint, -(x + 1)) + 1; } test "math.absCast" { @@ -499,7 +499,7 @@ pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { if (x == -@minValue(int)) return @minValue(int); - return -int(x); + return -@intCast(int, x); } test "math.negateCast" { @@ -522,7 +522,7 @@ pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { } else if (@minValue(@typeOf(x)) < @minValue(T) and x < @minValue(T)) { return error.Overflow; } else { - return T(x); + return @intCast(T, x); } } @@ -565,7 +565,7 @@ test "math.floorPowerOfTwo" { pub fn log2_int(comptime T: type, x: T) Log2Int(T) { assert(x != 0); - return Log2Int(T)(T.bit_count - 1 - @clz(x)); + return @intCast(Log2Int(T), T.bit_count - 1 - @clz(x)); } pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { @@ -597,3 +597,14 @@ fn testFloorPowerOfTwo() void { assert(floorPowerOfTwo(u4, 8) == 8); assert(floorPowerOfTwo(u4, 9) == 8); } + +pub fn lossyCast(comptime T: type, value: var) T { + switch (@typeInfo(@typeOf(value))) { + builtin.TypeId.Int => return @intToFloat(T, value), + builtin.TypeId.Float => return @floatCast(T, value), + builtin.TypeId.ComptimeInt => return T(value), + builtin.TypeId.ComptimeFloat => return T(value), + else => @compileError("bad type"), + } +} + diff --git a/std/math/ln.zig b/std/math/ln.zig index 3fd75977b9..e78cc379e0 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -71,7 +71,7 @@ pub fn ln_32(x_: f32) f32 { // x into [sqrt(2) / 2, sqrt(2)] ix += 0x3F800000 - 0x3F3504F3; - k += i32(ix >> 23) - 0x7F; + k += @intCast(i32, ix >> 23) - 0x7F; ix = (ix & 0x007FFFFF) + 0x3F3504F3; x = @bitCast(f32, ix); @@ -83,7 +83,7 @@ pub fn ln_32(x_: f32) f32 { const t2 = z * (Lg1 + w * Lg3); const R = t2 + t1; const hfsq = 0.5 * f * f; - const dk = f32(k); + const dk = @intToFloat(f32, k); return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; } @@ -103,7 +103,7 @@ pub fn ln_64(x_: f64) f64 { var x = x_; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 0; if (hx < 0x00100000 or hx >> 31 != 0) { @@ -119,7 +119,7 @@ pub fn ln_64(x_: f64) f64 { // subnormal, scale x k -= 54; x *= 0x1.0p54; - hx = u32(@bitCast(u64, ix) >> 32); + hx = @intCast(u32, @bitCast(u64, ix) >> 32); } else if (hx >= 0x7FF00000) { return x; } else if (hx == 0x3FF00000 and ix << 32 == 0) { @@ -128,7 +128,7 @@ pub fn ln_64(x_: f64) f64 { // x into [sqrt(2) / 2, sqrt(2)] hx += 0x3FF00000 - 0x3FE6A09E; - k += i32(hx >> 20) - 0x3FF; + k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); @@ -141,7 +141,7 @@ pub fn ln_64(x_: f64) f64 { const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); const R = t2 + t1; - const dk = f64(k); + const dk = @intToFloat(f64, k); return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi; } diff --git a/std/math/log.zig b/std/math/log.zig index 2c876081d8..20b6d055e8 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -13,22 +13,23 @@ pub fn log(comptime T: type, base: T, x: T) T { return math.ln(x); } + const float_base = math.lossyCast(f64, base); switch (@typeId(T)) { TypeId.ComptimeFloat => { - return @typeOf(1.0)(math.ln(f64(x)) / math.ln(f64(base))); + return @typeOf(1.0)(math.ln(f64(x)) / math.ln(float_base)); }, TypeId.ComptimeInt => { - return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(f64(base)))); + return @typeOf(1)(math.floor(math.ln(f64(x)) / math.ln(float_base))); }, builtin.TypeId.Int => { // TODO implement integer log without using float math - return T(math.floor(math.ln(f64(x)) / math.ln(f64(base)))); + return @floatToInt(T, math.floor(math.ln(@intToFloat(f64, x)) / math.ln(float_base))); }, builtin.TypeId.Float => { switch (T) { - f32 => return f32(math.ln(f64(x)) / math.ln(f64(base))), - f64 => return math.ln(x) / math.ln(f64(base)), + f32 => return @floatCast(f32, math.ln(f64(x)) / math.ln(float_base)), + f64 => return math.ln(x) / math.ln(float_base), else => @compileError("log not implemented for " ++ @typeName(T)), } }, diff --git a/std/math/log10.zig b/std/math/log10.zig index c444add9ac..a93ce48fb7 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -28,7 +28,7 @@ pub fn log10(x: var) @typeOf(x) { return @typeOf(1)(math.floor(log10_64(f64(x)))); }, TypeId.Int => { - return T(math.floor(log10_64(f64(x)))); + return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))); }, else => @compileError("log10 not implemented for " ++ @typeName(T)), } @@ -71,7 +71,7 @@ pub fn log10_32(x_: f32) f32 { // x into [sqrt(2) / 2, sqrt(2)] ix += 0x3F800000 - 0x3F3504F3; - k += i32(ix >> 23) - 0x7F; + k += @intCast(i32, ix >> 23) - 0x7F; ix = (ix & 0x007FFFFF) + 0x3F3504F3; x = @bitCast(f32, ix); @@ -89,7 +89,7 @@ pub fn log10_32(x_: f32) f32 { u &= 0xFFFFF000; hi = @bitCast(f32, u); const lo = f - hi - hfsq + s * (hfsq + R); - const dk = f32(k); + const dk = @intToFloat(f32, k); return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi; } @@ -109,7 +109,7 @@ pub fn log10_64(x_: f64) f64 { var x = x_; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 0; if (hx < 0x00100000 or hx >> 31 != 0) { @@ -125,7 +125,7 @@ pub fn log10_64(x_: f64) f64 { // subnormal, scale x k -= 54; x *= 0x1.0p54; - hx = u32(@bitCast(u64, x) >> 32); + hx = @intCast(u32, @bitCast(u64, x) >> 32); } else if (hx >= 0x7FF00000) { return x; } else if (hx == 0x3FF00000 and ix << 32 == 0) { @@ -134,7 +134,7 @@ pub fn log10_64(x_: f64) f64 { // x into [sqrt(2) / 2, sqrt(2)] hx += 0x3FF00000 - 0x3FE6A09E; - k += i32(hx >> 20) - 0x3FF; + k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); @@ -157,7 +157,7 @@ pub fn log10_64(x_: f64) f64 { // val_hi + val_lo ~ log10(1 + f) + k * log10(2) var val_hi = hi * ivln10hi; - const dk = f64(k); + const dk = @intToFloat(f64, k); const y = dk * log10_2hi; var val_lo = dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi; diff --git a/std/math/log1p.zig b/std/math/log1p.zig index 57efdaf51c..7d3be6bb49 100644 --- a/std/math/log1p.zig +++ b/std/math/log1p.zig @@ -68,7 +68,7 @@ fn log1p_32(x: f32) f32 { const uf = 1 + x; var iu = @bitCast(u32, uf); iu += 0x3F800000 - 0x3F3504F3; - k = i32(iu >> 23) - 0x7F; + k = @intCast(i32, iu >> 23) - 0x7F; // correction to avoid underflow in c / u if (k < 25) { @@ -90,7 +90,7 @@ fn log1p_32(x: f32) f32 { const t2 = z * (Lg1 + w * Lg3); const R = t2 + t1; const hfsq = 0.5 * f * f; - const dk = f32(k); + const dk = @intToFloat(f32, k); return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi; } @@ -107,7 +107,7 @@ fn log1p_64(x: f64) f64 { const Lg7: f64 = 1.479819860511658591e-01; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 1; var c: f64 = undefined; var f: f64 = undefined; @@ -145,9 +145,9 @@ fn log1p_64(x: f64) f64 { if (k != 0) { const uf = 1 + x; const hu = @bitCast(u64, uf); - var iu = u32(hu >> 32); + var iu = @intCast(u32, hu >> 32); iu += 0x3FF00000 - 0x3FE6A09E; - k = i32(iu >> 20) - 0x3FF; + k = @intCast(i32, iu >> 20) - 0x3FF; // correction to avoid underflow in c / u if (k < 54) { @@ -170,7 +170,7 @@ fn log1p_64(x: f64) f64 { const t1 = w * (Lg2 + w * (Lg4 + w * Lg6)); const t2 = z * (Lg1 + w * (Lg3 + w * (Lg5 + w * Lg7))); const R = t2 + t1; - const dk = f64(k); + const dk = @intToFloat(f64, k); return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi; } diff --git a/std/math/log2.zig b/std/math/log2.zig index 2530519941..858f6ffa02 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -75,7 +75,7 @@ pub fn log2_32(x_: f32) f32 { // x into [sqrt(2) / 2, sqrt(2)] ix += 0x3F800000 - 0x3F3504F3; - k += i32(ix >> 23) - 0x7F; + k += @intCast(i32, ix >> 23) - 0x7F; ix = (ix & 0x007FFFFF) + 0x3F3504F3; x = @bitCast(f32, ix); @@ -93,7 +93,7 @@ pub fn log2_32(x_: f32) f32 { u &= 0xFFFFF000; hi = @bitCast(f32, u); const lo = f - hi - hfsq + s * (hfsq + R); - return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k); + return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + @intToFloat(f32, k); } pub fn log2_64(x_: f64) f64 { @@ -109,7 +109,7 @@ pub fn log2_64(x_: f64) f64 { var x = x_; var ix = @bitCast(u64, x); - var hx = u32(ix >> 32); + var hx = @intCast(u32, ix >> 32); var k: i32 = 0; if (hx < 0x00100000 or hx >> 31 != 0) { @@ -125,7 +125,7 @@ pub fn log2_64(x_: f64) f64 { // subnormal, scale x k -= 54; x *= 0x1.0p54; - hx = u32(@bitCast(u64, x) >> 32); + hx = @intCast(u32, @bitCast(u64, x) >> 32); } else if (hx >= 0x7FF00000) { return x; } else if (hx == 0x3FF00000 and ix << 32 == 0) { @@ -134,7 +134,7 @@ pub fn log2_64(x_: f64) f64 { // x into [sqrt(2) / 2, sqrt(2)] hx += 0x3FF00000 - 0x3FE6A09E; - k += i32(hx >> 20) - 0x3FF; + k += @intCast(i32, hx >> 20) - 0x3FF; hx = (hx & 0x000FFFFF) + 0x3FE6A09E; ix = (u64(hx) << 32) | (ix & 0xFFFFFFFF); x = @bitCast(f64, ix); @@ -159,7 +159,7 @@ pub fn log2_64(x_: f64) f64 { var val_lo = (lo + hi) * ivln2lo + lo * ivln2hi; // spadd(val_hi, val_lo, y) - const y = f64(k); + const y = @intToFloat(f64, k); const ww = y + val_hi; val_lo += (y - ww) + val_hi; val_hi = ww; diff --git a/std/math/modf.zig b/std/math/modf.zig index a6606ce34c..b6dd78f022 100644 --- a/std/math/modf.zig +++ b/std/math/modf.zig @@ -29,7 +29,7 @@ fn modf32(x: f32) modf32_result { var result: modf32_result = undefined; const u = @bitCast(u32, x); - const e = i32((u >> 23) & 0xFF) - 0x7F; + const e = @intCast(i32, (u >> 23) & 0xFF) - 0x7F; const us = u & 0x80000000; // TODO: Shouldn't need this. @@ -57,7 +57,7 @@ fn modf32(x: f32) modf32_result { return result; } - const mask = u32(0x007FFFFF) >> u5(e); + const mask = u32(0x007FFFFF) >> @intCast(u5, e); if (u & mask == 0) { result.ipart = x; result.fpart = @bitCast(f32, us); @@ -74,7 +74,7 @@ fn modf64(x: f64) modf64_result { var result: modf64_result = undefined; const u = @bitCast(u64, x); - const e = i32((u >> 52) & 0x7FF) - 0x3FF; + const e = @intCast(i32, (u >> 52) & 0x7FF) - 0x3FF; const us = u & (1 << 63); if (math.isInf(x)) { @@ -101,7 +101,7 @@ fn modf64(x: f64) modf64_result { return result; } - const mask = u64(@maxValue(u64) >> 12) >> u6(e); + const mask = u64(@maxValue(u64) >> 12) >> @intCast(u6, e); if (u & mask == 0) { result.ipart = x; result.fpart = @bitCast(f64, us); diff --git a/std/math/pow.zig b/std/math/pow.zig index dfe4fc09d6..7fc334c06b 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -146,7 +146,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { var xe = r2.exponent; var x1 = r2.significand; - var i = i32(yi); + var i = @floatToInt(i32, yi); while (i != 0) : (i >>= 1) { if (i & 1 == 1) { a1 *= x1; @@ -171,7 +171,7 @@ pub fn pow(comptime T: type, x: T, y: T) T { fn isOddInteger(x: f64) bool { const r = math.modf(x); - return r.fpart == 0.0 and i64(r.ipart) & 1 == 1; + return r.fpart == 0.0 and @floatToInt(i64, r.ipart) & 1 == 1; } test "math.pow" { diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig index deb5d989d2..f72c7e866f 100644 --- a/std/math/scalbn.zig +++ b/std/math/scalbn.zig @@ -37,7 +37,7 @@ fn scalbn32(x: f32, n_: i32) f32 { } } - const u = u32(n +% 0x7F) << 23; + const u = @intCast(u32, n +% 0x7F) << 23; return y * @bitCast(f32, u); } @@ -67,7 +67,7 @@ fn scalbn64(x: f64, n_: i32) f64 { } } - const u = u64(n +% 0x3FF) << 52; + const u = @intCast(u64, n +% 0x3FF) << 52; return y * @bitCast(f64, u); } diff --git a/std/math/sin.zig b/std/math/sin.zig index 21c324e444..3796d74812 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -60,7 +60,7 @@ fn sin32(x_: f32) f32 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; @@ -112,7 +112,7 @@ fn sin64(x_: f64) f64 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; diff --git a/std/math/sinh.zig b/std/math/sinh.zig index 85c9ae979b..bb3af280ab 100644 --- a/std/math/sinh.zig +++ b/std/math/sinh.zig @@ -57,7 +57,7 @@ fn sinh64(x: f64) f64 { @setFloatMode(this, @import("builtin").FloatMode.Strict); const u = @bitCast(u64, x); - const w = u32(u >> 32); + const w = @intCast(u32, u >> 32); const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); if (x == 0.0 or math.isNan(x)) { diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 7a3ddb3b96..599008acff 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -99,7 +99,7 @@ fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) { } const ResultType = @IntType(false, T.bit_count / 2); - return ResultType(res); + return @intCast(ResultType, res); } test "math.sqrt_int" { diff --git a/std/math/tan.zig b/std/math/tan.zig index f578cf8156..ff3ed06186 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -53,7 +53,7 @@ fn tan32(x_: f32) f32 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; @@ -102,7 +102,7 @@ fn tan64(x_: f64) f64 { } var y = math.floor(x * m4pi); - var j = i64(y); + var j = @floatToInt(i64, y); if (j & 1 == 1) { j += 1; diff --git a/std/math/tanh.zig b/std/math/tanh.zig index c1f5a0ca46..6204b2a374 100644 --- a/std/math/tanh.zig +++ b/std/math/tanh.zig @@ -68,7 +68,7 @@ fn tanh32(x: f32) f32 { fn tanh64(x: f64) f64 { const u = @bitCast(u64, x); - const w = u32(u >> 32); + const w = @intCast(u32, u >> 32); const ax = @bitCast(f64, u & (@maxValue(u64) >> 1)); var t: f64 = undefined; @@ -100,7 +100,7 @@ fn tanh64(x: f64) f64 { } // |x| is subnormal else { - math.forceEval(f32(x)); + math.forceEval(@floatCast(f32, x)); t = x; } diff --git a/std/math/trunc.zig b/std/math/trunc.zig index 54aa6943f7..92d5bfebc5 100644 --- a/std/math/trunc.zig +++ b/std/math/trunc.zig @@ -19,7 +19,7 @@ pub fn trunc(x: var) @typeOf(x) { fn trunc32(x: f32) f32 { const u = @bitCast(u32, x); - var e = i32(((u >> 23) & 0xFF)) - 0x7F + 9; + var e = @intCast(i32, ((u >> 23) & 0xFF)) - 0x7F + 9; var m: u32 = undefined; if (e >= 23 + 9) { @@ -29,7 +29,7 @@ fn trunc32(x: f32) f32 { e = 1; } - m = u32(@maxValue(u32)) >> u5(e); + m = u32(@maxValue(u32)) >> @intCast(u5, e); if (u & m == 0) { return x; } else { @@ -40,7 +40,7 @@ fn trunc32(x: f32) f32 { fn trunc64(x: f64) f64 { const u = @bitCast(u64, x); - var e = i32(((u >> 52) & 0x7FF)) - 0x3FF + 12; + var e = @intCast(i32, ((u >> 52) & 0x7FF)) - 0x3FF + 12; var m: u64 = undefined; if (e >= 52 + 12) { @@ -50,7 +50,7 @@ fn trunc64(x: f64) f64 { e = 1; } - m = u64(@maxValue(u64)) >> u6(e); + m = u64(@maxValue(u64)) >> @intCast(u6, e); if (u & m == 0) { return x; } else { diff --git a/std/mem.zig b/std/mem.zig index 10b3eb8fef..b02589b0dd 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -334,7 +334,7 @@ pub fn readInt(bytes: []const u8, comptime T: type, endian: builtin.Endian) T { builtin.Endian.Little => { const ShiftType = math.Log2Int(T); for (bytes) |b, index| { - result = result | (T(b) << ShiftType(index * 8)); + result = result | (T(b) << @intCast(ShiftType, index * 8)); } }, } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 1e3a732498..3a0fa7f461 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -413,7 +413,7 @@ pub const ChildProcess = struct { } // we are the parent - const pid = i32(pid_result); + const pid = @intCast(i32, pid_result); if (self.stdin_behavior == StdIo.Pipe) { self.stdin = os.File.openHandle(stdin_pipe[1]); } else { diff --git a/std/os/darwin.zig b/std/os/darwin.zig index a835959103..15e5608343 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -290,7 +290,7 @@ pub fn WIFSIGNALED(x: i32) bool { /// Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) usize { const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0; + return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; } pub fn close(fd: i32) usize { @@ -339,7 +339,14 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { } pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); + const ptr_result = c.mmap( + @ptrCast(*c_void, address), + length, + @bitCast(c_int, @intCast(c_uint, prot)), + @bitCast(c_int, c_uint(flags)), + fd, + offset, + ); const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); return errnoWrap(isize_result); } diff --git a/std/os/file.zig b/std/os/file.zig index 7e05501831..055f185121 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -266,7 +266,7 @@ pub const File = struct { pub fn getEndPos(self: *File) !usize { if (is_posix) { const stat = try os.posixFStat(self.handle); - return usize(stat.size); + return @intCast(usize, stat.size); } else if (is_windows) { var file_size: windows.LARGE_INTEGER = undefined; if (windows.GetFileSizeEx(self.handle, &file_size) == 0) { @@ -277,7 +277,7 @@ pub const File = struct { } if (file_size < 0) return error.Overflow; - return math.cast(usize, u64(file_size)); + return math.cast(usize, @intCast(u64, file_size)); } else { @compileError("TODO support getEndPos on this OS"); } @@ -343,7 +343,7 @@ pub const File = struct { } else if (is_windows) { var index: usize = 0; while (index < buffer.len) { - const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); + const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); var amt_read: windows.DWORD = undefined; if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { const err = windows.GetLastError(); diff --git a/std/os/index.zig b/std/os/index.zig index fb4605fce0..f1c3ab2128 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -126,7 +126,7 @@ pub fn getRandomBytes(buf: []u8) !void { } defer _ = windows.CryptReleaseContext(hCryptProv, 0); - if (windows.CryptGenRandom(hCryptProv, windows.DWORD(buf.len), buf.ptr) == 0) { + if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) { const err = windows.GetLastError(); return switch (err) { else => unexpectedErrorWindows(err), @@ -343,7 +343,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { else => return unexpectedErrorPosix(err), } } - return i32(result); + return @intCast(i32, result); } } @@ -586,7 +586,7 @@ pub fn getCwd(allocator: *Allocator) ![]u8 { errdefer allocator.free(buf); while (true) { - const result = windows.GetCurrentDirectoryA(windows.WORD(buf.len), buf.ptr); + const result = windows.GetCurrentDirectoryA(@intCast(windows.WORD, buf.len), buf.ptr); if (result == 0) { const err = windows.GetLastError(); @@ -2019,7 +2019,7 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { const rc = posix.socket(domain, socket_type, protocol); const err = posix.getErrno(rc); switch (err) { - 0 => return i32(rc), + 0 => return @intCast(i32, rc), posix.EACCES => return PosixSocketError.PermissionDenied, posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, @@ -2183,7 +2183,7 @@ pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError! const rc = posix.accept4(fd, addr, &sockaddr_size, flags); const err = posix.getErrno(rc); switch (err) { - 0 => return i32(rc), + 0 => return @intCast(i32, rc), posix.EINTR => continue, else => return unexpectedErrorPosix(err), @@ -2226,7 +2226,7 @@ pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { const rc = posix.epoll_create1(flags); const err = posix.getErrno(rc); switch (err) { - 0 => return i32(rc), + 0 => return @intCast(i32, rc), else => return unexpectedErrorPosix(err), posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall, @@ -2296,7 +2296,7 @@ pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) Lin pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { while (true) { - const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout); + const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); const err = posix.getErrno(rc); switch (err) { 0 => return rc, @@ -2661,7 +2661,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread posix.EAGAIN => return SpawnThreadError.SystemResources, posix.EPERM => unreachable, posix.EINVAL => unreachable, - else => return unexpectedErrorPosix(usize(err)), + else => return unexpectedErrorPosix(@intCast(usize, err)), } } else if (builtin.os == builtin.Os.linux) { // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 0e77371ec2..65aa659c82 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -642,7 +642,7 @@ pub fn WIFEXITED(s: i32) bool { return WTERMSIG(s) == 0; } pub fn WIFSTOPPED(s: i32) bool { - return (u16)(((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; + return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; } pub fn WIFSIGNALED(s: i32) bool { return (unsigned(s) & 0xffff) -% 1 < 0xff; @@ -658,11 +658,11 @@ pub const winsize = extern struct { /// Get the errno from a syscall return value, or 0 for no error. pub fn getErrno(r: usize) usize { const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0; + return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; } pub fn dup2(old: i32, new: i32) usize { - return syscall2(SYS_dup2, usize(old), usize(new)); + return syscall2(SYS_dup2, @intCast(usize, old), @intCast(usize, new)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -693,12 +693,12 @@ pub fn getcwd(buf: [*]u8, size: usize) usize { } pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count); + return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count); } pub fn isatty(fd: i32) bool { var wsz: winsize = undefined; - return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; + return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; } // TODO https://github.com/ziglang/zig/issues/265 @@ -727,7 +727,7 @@ pub fn umount2(special: [*]const u8, flags: u32) usize { } pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), @bitCast(usize, offset)); + return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @intCast(usize, fd), @bitCast(usize, offset)); } pub fn munmap(address: usize, length: usize) usize { @@ -735,7 +735,7 @@ pub fn munmap(address: usize, length: usize) usize { } pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count); + return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count); } // TODO https://github.com/ziglang/zig/issues/265 @@ -749,7 +749,7 @@ pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { } pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { - return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset); + return syscall4(SYS_pread, @intCast(usize, fd), @ptrToInt(buf), count, offset); } // TODO https://github.com/ziglang/zig/issues/265 @@ -766,11 +766,11 @@ pub fn pipe2(fd: *[2]i32, flags: usize) usize { } pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count); + return syscall3(SYS_write, @intCast(usize, fd), @ptrToInt(buf), count); } pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { - return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset); + return syscall4(SYS_pwrite, @intCast(usize, fd), @ptrToInt(buf), count, offset); } // TODO https://github.com/ziglang/zig/issues/265 @@ -790,7 +790,7 @@ pub fn create(path: [*]const u8, perm: usize) usize { // TODO https://github.com/ziglang/zig/issues/265 pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode); + return syscall4(SYS_openat, @intCast(usize, dirfd), @ptrToInt(path), flags, mode); } /// See also `clone` (from the arch-specific include) @@ -804,11 +804,11 @@ pub fn clone2(flags: usize, child_stack_ptr: usize) usize { } pub fn close(fd: i32) usize { - return syscall1(SYS_close, usize(fd)); + return syscall1(SYS_close, @intCast(usize, fd)); } pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { - return syscall3(SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos); + return syscall3(SYS_lseek, @intCast(usize, fd), @bitCast(usize, offset), ref_pos); } pub fn exit(status: i32) noreturn { @@ -817,11 +817,11 @@ pub fn exit(status: i32) noreturn { } pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { - return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags)); + return syscall3(SYS_getrandom, @ptrToInt(buf), count, @intCast(usize, flags)); } pub fn kill(pid: i32, sig: i32) usize { - return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig)); + return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @intCast(usize, sig)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -999,8 +999,8 @@ pub const empty_sigset = []usize{0} ** sigset_t.len; pub fn raise(sig: i32) usize { var set: sigset_t = undefined; blockAppSignals(&set); - const tid = i32(syscall0(SYS_gettid)); - const ret = syscall2(SYS_tkill, usize(tid), usize(sig)); + const tid = @intCast(i32, syscall0(SYS_gettid)); + const ret = syscall2(SYS_tkill, @intCast(usize, tid), @intCast(usize, sig)); restoreSignals(&set); return ret; } @@ -1019,12 +1019,12 @@ fn restoreSignals(set: *sigset_t) void { pub fn sigaddset(set: *sigset_t, sig: u6) void { const s = sig - 1; - (set.*)[usize(s) / usize.bit_count] |= usize(1) << (s & (usize.bit_count - 1)); + (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); } pub fn sigismember(set: *const sigset_t, sig: u6) bool { const s = sig - 1; - return ((set.*)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0; + return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; } pub const in_port_t = u16; @@ -1057,11 +1057,11 @@ pub const iovec = extern struct { }; pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len)); + return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len)); } pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len)); + return syscall3(SYS_getpeername, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len)); } pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { @@ -1069,47 +1069,47 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { } pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { - return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen)); + return syscall5(SYS_setsockopt, @intCast(usize, fd), level, optname, @intCast(usize, optval), @ptrToInt(optlen)); } pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { - return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); + return syscall5(SYS_getsockopt, @intCast(usize, fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { - return syscall3(SYS_sendmsg, usize(fd), @ptrToInt(msg), flags); + return syscall3(SYS_sendmsg, @intCast(usize, fd), @ptrToInt(msg), flags); } pub fn connect(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_connect, usize(fd), @ptrToInt(addr), usize(len)); + return syscall3(SYS_connect, @intCast(usize, fd), @ptrToInt(addr), @intCast(usize, len)); } pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { - return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags); + return syscall3(SYS_recvmsg, @intCast(usize, fd), @ptrToInt(msg), flags); } pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { - return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); + return syscall6(SYS_recvfrom, @intCast(usize, fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } pub fn shutdown(fd: i32, how: i32) usize { - return syscall2(SYS_shutdown, usize(fd), usize(how)); + return syscall2(SYS_shutdown, @intCast(usize, fd), @intCast(usize, how)); } pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len)); + return syscall3(SYS_bind, @intCast(usize, fd), @ptrToInt(addr), @intCast(usize, len)); } pub fn listen(fd: i32, backlog: u32) usize { - return syscall2(SYS_listen, usize(fd), backlog); + return syscall2(SYS_listen, @intCast(usize, fd), backlog); } pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { - return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen)); + return syscall6(SYS_sendto, @intCast(usize, fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { - return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(*fd[0])); + return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(*fd[0])); } pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { @@ -1117,11 +1117,11 @@ pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { } pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { - return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags); + return syscall4(SYS_accept4, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len), flags); } pub fn fstat(fd: i32, stat_buf: *Stat) usize { - return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf)); + return syscall2(SYS_fstat, @intCast(usize, fd), @ptrToInt(stat_buf)); } // TODO https://github.com/ziglang/zig/issues/265 @@ -1214,15 +1214,15 @@ pub fn epoll_create1(flags: usize) usize { } pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { - return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); + return syscall4(SYS_epoll_ctl, @intCast(usize, epoll_fd), @intCast(usize, op), @intCast(usize, fd), @ptrToInt(ev)); } pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { - return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); + return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout)); } pub fn timerfd_create(clockid: i32, flags: u32) usize { - return syscall2(SYS_timerfd_create, usize(clockid), usize(flags)); + return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags)); } pub const itimerspec = extern struct { @@ -1231,11 +1231,11 @@ pub const itimerspec = extern struct { }; pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { - return syscall2(SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value)); + return syscall2(SYS_timerfd_gettime, @intCast(usize, fd), @ptrToInt(curr_value)); } pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { - return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value)); + return syscall4(SYS_timerfd_settime, @intCast(usize, fd), @intCast(usize, flags), @ptrToInt(new_value), @ptrToInt(old_value)); } pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; @@ -1345,7 +1345,7 @@ pub const cap_user_data_t = extern struct { }; pub fn unshare(flags: usize) usize { - return syscall1(SYS_unshare, usize(flags)); + return syscall1(SYS_unshare, @intCast(usize, flags)); } pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 948a3ac96b..e7dae3a584 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -21,7 +21,7 @@ test "timer" { .it_value = time_interval, }; - err = linux.timerfd_settime(i32(timer_fd), 0, &new_time, null); + err = linux.timerfd_settime(@intCast(i32, timer_fd), 0, &new_time, null); assert(err == 0); var event = linux.epoll_event{ @@ -29,12 +29,12 @@ test "timer" { .data = linux.epoll_data{ .ptr = 0 }, }; - err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event); + err = linux.epoll_ctl(@intCast(i32, epoll_fd), linux.EPOLL_CTL_ADD, @intCast(i32, timer_fd), &event); assert(err == 0); const events_one: linux.epoll_event = undefined; var events = []linux.epoll_event{events_one} ** 8; // TODO implicit cast from *[N]T to [*]T - err = linux.epoll_wait(i32(epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); + err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); } diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index cbd0cd1df5..a78e3370e6 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -62,8 +62,8 @@ pub fn lookup(vername: []const u8, name: []const u8) usize { var i: usize = 0; while (i < hashtab[1]) : (i += 1) { - if (0 == (u32(1) << u5(syms[i].st_info & 0xf) & OK_TYPES)) continue; - if (0 == (u32(1) << u5(syms[i].st_info >> 4) & OK_BINDS)) continue; + if (0 == (u32(1) << @intCast(u5, syms[i].st_info & 0xf) & OK_TYPES)) continue; + if (0 == (u32(1) << @intCast(u5, syms[i].st_info >> 4) & OK_BINDS)) continue; if (0 == syms[i].st_shndx) continue; if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (maybe_versym) |versym| { diff --git a/std/os/time.zig b/std/os/time.zig index ffb506cd7d..73ba5bba82 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -14,12 +14,12 @@ pub const epoch = @import("epoch.zig"); pub fn sleep(seconds: usize, nanoseconds: usize) void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { - posixSleep(u63(seconds), u63(nanoseconds)); + posixSleep(@intCast(u63, seconds), @intCast(u63, nanoseconds)); }, Os.windows => { const ns_per_ms = ns_per_s / ms_per_s; const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms; - windows.Sleep(windows.DWORD(milliseconds)); + windows.Sleep(@intCast(windows.DWORD, milliseconds)); }, else => @compileError("Unsupported OS"), } @@ -83,8 +83,8 @@ fn milliTimestampDarwin() u64 { var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); - const sec_ms = u64(tv.tv_sec) * ms_per_s; - const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s); + const sec_ms = @intCast(u64, tv.tv_sec) * ms_per_s; + const usec_ms = @divFloor(@intCast(u64, tv.tv_usec), us_per_s / ms_per_s); return u64(sec_ms) + u64(usec_ms); } @@ -95,8 +95,8 @@ fn milliTimestampPosix() u64 { var ts: posix.timespec = undefined; const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts); debug.assert(err == 0); - const sec_ms = u64(ts.tv_sec) * ms_per_s; - const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s); + const sec_ms = @intCast(u64, ts.tv_sec) * ms_per_s; + const nsec_ms = @divFloor(@intCast(u64, ts.tv_nsec), ns_per_s / ms_per_s); return sec_ms + nsec_ms; } @@ -162,13 +162,13 @@ pub const Timer = struct { var freq: i64 = undefined; var err = windows.QueryPerformanceFrequency(&freq); if (err == windows.FALSE) return error.TimerUnsupported; - self.frequency = u64(freq); + self.frequency = @intCast(u64, freq); self.resolution = @divFloor(ns_per_s, self.frequency); var start_time: i64 = undefined; err = windows.QueryPerformanceCounter(&start_time); debug.assert(err != windows.FALSE); - self.start_time = u64(start_time); + self.start_time = @intCast(u64, start_time); }, Os.linux => { //On Linux, seccomp can do arbitrary things to our ability to call @@ -184,12 +184,12 @@ pub const Timer = struct { posix.EINVAL => return error.TimerUnsupported, else => return std.os.unexpectedErrorPosix(errno), } - self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + self.resolution = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); result = posix.clock_gettime(monotonic_clock_id, &ts); errno = posix.getErrno(result); if (errno != 0) return std.os.unexpectedErrorPosix(errno); - self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + self.start_time = @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); }, Os.macosx, Os.ios => { darwin.mach_timebase_info(&self.frequency); @@ -236,7 +236,7 @@ pub const Timer = struct { var result: i64 = undefined; var err = windows.QueryPerformanceCounter(&result); debug.assert(err != windows.FALSE); - return u64(result); + return @intCast(u64, result); } fn clockDarwin() u64 { @@ -247,7 +247,7 @@ pub const Timer = struct { var ts: posix.timespec = undefined; var result = posix.clock_gettime(monotonic_clock_id, &ts); debug.assert(posix.getErrno(result) == 0); - return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec); + return @intCast(u64, ts.tv_sec) * u64(ns_per_s) + @intCast(u64, ts.tv_nsec); } }; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 88a9e7952e..cb4788ba17 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -42,7 +42,7 @@ pub const WriteError = error{ }; pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { + if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), @intCast(u32, bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, @@ -68,7 +68,12 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const size = @sizeOf(windows.FILE_NAME_INFO); var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH); - if (windows.GetFileInformationByHandleEx(handle, windows.FileNameInfo, @ptrCast(*c_void, &name_info_bytes[0]), u32(name_info_bytes.len)) == 0) { + if (windows.GetFileInformationByHandleEx( + handle, + windows.FileNameInfo, + @ptrCast(*c_void, &name_info_bytes[0]), + @intCast(u32, name_info_bytes.len), + ) == 0) { return true; } diff --git a/std/rand/index.zig b/std/rand/index.zig index 3a1a559cd9..13694f4c09 100644 --- a/std/rand/index.zig +++ b/std/rand/index.zig @@ -55,16 +55,16 @@ pub const Random = struct { if (T.is_signed) { const uint = @IntType(false, T.bit_count); if (start >= 0 and end >= 0) { - return T(r.range(uint, uint(start), uint(end))); + return @intCast(T, r.range(uint, @intCast(uint, start), @intCast(uint, end))); } else if (start < 0 and end < 0) { // Can't overflow because the range is over signed ints return math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1) catch unreachable; } else if (start < 0 and end >= 0) { - const end_uint = uint(end); + const end_uint = @intCast(uint, end); const total_range = math.absCast(start) + end_uint; const value = r.range(uint, 0, total_range); const result = if (value < end_uint) x: { - break :x T(value); + break :x @intCast(T, value); } else if (value == end_uint) x: { break :x start; } else x: { @@ -213,9 +213,9 @@ pub const Pcg = struct { self.s = l *% default_multiplier +% (self.i | 1); const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27); - const rot = u32(l >> 59); + const rot = @intCast(u32, l >> 59); - return (xor_s >> u5(rot)) | (xor_s << u5((0 -% rot) & 31)); + return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31)); } fn seed(self: *Pcg, init_s: u64) void { @@ -322,7 +322,7 @@ pub const Xoroshiro128 = struct { inline for (table) |entry| { var b: usize = 0; while (b < 64) : (b += 1) { - if ((entry & (u64(1) << u6(b))) != 0) { + if ((entry & (u64(1) << @intCast(u6, b))) != 0) { s0 ^= self.s[0]; s1 ^= self.s[1]; } @@ -667,13 +667,13 @@ test "Random range" { } fn testRange(r: *Random, start: i32, end: i32) void { - const count = usize(end - start); + const count = @intCast(usize, end - start); var values_buffer = []bool{false} ** 20; const values = values_buffer[0..count]; var i: usize = 0; while (i < count) { const value = r.range(i32, start, end); - const index = usize(value - start); + const index = @intCast(usize, value - start); if (!values[index]) { i += 1; values[index] = true; diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 9f10f4d44a..6e3f32e9d6 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -104,7 +104,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } pub fn deinit(self: *Self) void { - self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0); + self.freeShelves(@intCast(ShelfIndex, self.dynamic_segments.len), 0); self.allocator.free(self.dynamic_segments); self.* = undefined; } @@ -158,7 +158,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Only grows capacity, or retains current capacity pub fn growCapacity(self: *Self, new_capacity: usize) !void { const new_cap_shelf_count = shelfCount(new_capacity); - const old_shelf_count = ShelfIndex(self.dynamic_segments.len); + const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); if (new_cap_shelf_count > old_shelf_count) { self.dynamic_segments = try self.allocator.realloc([*]T, self.dynamic_segments, new_cap_shelf_count); var i = old_shelf_count; @@ -175,7 +175,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Only shrinks capacity or retains current capacity pub fn shrinkCapacity(self: *Self, new_capacity: usize) void { if (new_capacity <= prealloc_item_count) { - const len = ShelfIndex(self.dynamic_segments.len); + const len = @intCast(ShelfIndex, self.dynamic_segments.len); self.freeShelves(len, 0); self.allocator.free(self.dynamic_segments); self.dynamic_segments = [][*]T{}; @@ -183,7 +183,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type } const new_cap_shelf_count = shelfCount(new_capacity); - const old_shelf_count = ShelfIndex(self.dynamic_segments.len); + const old_shelf_count = @intCast(ShelfIndex, self.dynamic_segments.len); assert(new_cap_shelf_count <= old_shelf_count); if (new_cap_shelf_count == old_shelf_count) { return; @@ -338,7 +338,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { { var i: usize = 0; while (i < 100) : (i += 1) { - try list.push(i32(i + 1)); + try list.push(@intCast(i32, i + 1)); assert(list.len == i + 1); } } @@ -346,7 +346,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { { var i: usize = 0; while (i < 100) : (i += 1) { - assert(list.at(i).* == i32(i + 1)); + assert(list.at(i).* == @intCast(i32, i + 1)); } } diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index dd37f1edb6..5c8a330a92 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -80,7 +80,7 @@ extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 { var env_count: usize = 0; while (c_envp[env_count] != null) : (env_count += 1) {} const envp = @ptrCast([*][*]u8, c_envp)[0..env_count]; - return callMainWithArgs(usize(c_argc), c_argv, envp); + return callMainWithArgs(@intCast(usize, c_argc), c_argv, envp); } fn callMain() u8 { diff --git a/std/special/builtin.zig b/std/special/builtin.zig index e97b0a89e4..07e735d931 100644 --- a/std/special/builtin.zig +++ b/std/special/builtin.zig @@ -135,9 +135,9 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { const mask = if (T == f32) 0xff else 0x7ff; var ux = @bitCast(uint, x); var uy = @bitCast(uint, y); - var ex = i32((ux >> digits) & mask); - var ey = i32((uy >> digits) & mask); - const sx = if (T == f32) u32(ux & 0x80000000) else i32(ux >> bits_minus_1); + var ex = @intCast(i32, (ux >> digits) & mask); + var ey = @intCast(i32, (uy >> digits) & mask); + const sx = if (T == f32) @intCast(u32, ux & 0x80000000) else @intCast(i32, ux >> bits_minus_1); var i: uint = undefined; if (uy << 1 == 0 or isNan(uint, uy) or ex == mask) @@ -156,7 +156,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { ex -= 1; i <<= 1; }) {} - ux <<= log2uint(@bitCast(u32, -ex + 1)); + ux <<= @intCast(log2uint, @bitCast(u32, -ex + 1)); } else { ux &= @maxValue(uint) >> exp_bits; ux |= 1 << digits; @@ -167,7 +167,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { ey -= 1; i <<= 1; }) {} - uy <<= log2uint(@bitCast(u32, -ey + 1)); + uy <<= @intCast(log2uint, @bitCast(u32, -ey + 1)); } else { uy &= @maxValue(uint) >> exp_bits; uy |= 1 << digits; @@ -199,12 +199,12 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { ux -%= 1 << digits; ux |= uint(@bitCast(u32, ex)) << digits; } else { - ux >>= log2uint(@bitCast(u32, -ex + 1)); + ux >>= @intCast(log2uint, @bitCast(u32, -ex + 1)); } if (T == f32) { ux |= sx; } else { - ux |= uint(sx) << bits_minus_1; + ux |= @intCast(uint, sx) << bits_minus_1; } return @bitCast(T, ux); } @@ -227,8 +227,8 @@ export fn sqrt(x: f64) f64 { const sign: u32 = 0x80000000; const u = @bitCast(u64, x); - var ix0 = u32(u >> 32); - var ix1 = u32(u & 0xFFFFFFFF); + var ix0 = @intCast(u32, u >> 32); + var ix1 = @intCast(u32, u & 0xFFFFFFFF); // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan if (ix0 & 0x7FF00000 == 0x7FF00000) { @@ -245,7 +245,7 @@ export fn sqrt(x: f64) f64 { } // normalize x - var m = i32(ix0 >> 20); + var m = @intCast(i32, ix0 >> 20); if (m == 0) { // subnormal while (ix0 == 0) { @@ -259,9 +259,9 @@ export fn sqrt(x: f64) f64 { while (ix0 & 0x00100000 == 0) : (i += 1) { ix0 <<= 1; } - m -= i32(i) - 1; - ix0 |= ix1 >> u5(32 - i); - ix1 <<= u5(i); + m -= @intCast(i32, i) - 1; + ix0 |= ix1 >> @intCast(u5, 32 - i); + ix1 <<= @intCast(u5, i); } // unbias exponent @@ -345,10 +345,10 @@ export fn sqrt(x: f64) f64 { // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same // behaviour at least. - var iix0 = i32(ix0); + var iix0 = @intCast(i32, ix0); iix0 = iix0 +% (m << 20); - const uz = (u64(iix0) << 32) | ix1; + const uz = (@intCast(u64, iix0) << 32) | ix1; return @bitCast(f64, uz); } diff --git a/std/special/compiler_rt/divti3.zig b/std/special/compiler_rt/divti3.zig index 60460ea62d..712cccba82 100644 --- a/std/special/compiler_rt/divti3.zig +++ b/std/special/compiler_rt/divti3.zig @@ -13,7 +13,7 @@ pub extern fn __divti3(a: i128, b: i128) i128 { const r = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), null); const s = s_a ^ s_b; - return (i128(r) ^ s) -% s; + return (@bitCast(i128, r) ^ s) -% s; } pub extern fn __divti3_windows_x86_64(a: *const i128, b: *const i128) void { diff --git a/std/special/compiler_rt/fixuint.zig b/std/special/compiler_rt/fixuint.zig index bd9b2fc395..48d63ed914 100644 --- a/std/special/compiler_rt/fixuint.zig +++ b/std/special/compiler_rt/fixuint.zig @@ -32,14 +32,14 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t const aAbs: rep_t = aRep & absMask; const sign = if ((aRep & signBit) != 0) i32(-1) else i32(1); - const exponent = i32(aAbs >> significandBits) - exponentBias; + const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; const significand: rep_t = (aAbs & significandMask) | implicitBit; // If either the value or the exponent is negative, the result is zero. if (sign == -1 or exponent < 0) return 0; // If the value is too large for the integer type, saturate. - if (c_uint(exponent) >= fixuint_t.bit_count) return ~fixuint_t(0); + if (@intCast(c_uint, exponent) >= fixuint_t.bit_count) return ~fixuint_t(0); // If 0 <= exponent < significandBits, right shift to get the result. // Otherwise, shift left. @@ -47,11 +47,11 @@ pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t // TODO this is a workaround for the mysterious "integer cast truncated bits" // happening on the next line @setRuntimeSafety(false); - return fixuint_t(significand >> Log2Int(rep_t)(significandBits - exponent)); + return @intCast(fixuint_t, significand >> @intCast(Log2Int(rep_t), significandBits - exponent)); } else { // TODO this is a workaround for the mysterious "integer cast truncated bits" // happening on the next line @setRuntimeSafety(false); - return fixuint_t(significand) << Log2Int(fixuint_t)(exponent - significandBits); + return @intCast(fixuint_t, significand) << @intCast(Log2Int(fixuint_t), exponent - significandBits); } } diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index f952730353..dc95aa23f2 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -292,7 +292,7 @@ extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { @setRuntimeSafety(is_test); const d = __udivsi3(a, b); - rem.* = u32(i32(a) -% (i32(d) * i32(b))); + rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); return d; } @@ -316,12 +316,12 @@ extern fn __udivsi3(n: u32, d: u32) u32 { sr += 1; // 1 <= sr <= n_uword_bits - 1 // Not a special case - var q: u32 = n << u5(n_uword_bits - sr); - var r: u32 = n >> u5(sr); + var q: u32 = n << @intCast(u5, n_uword_bits - sr); + var r: u32 = n >> @intCast(u5, sr); var carry: u32 = 0; while (sr > 0) : (sr -= 1) { // r:q = ((r:q) << 1) | carry - r = (r << 1) | (q >> u5(n_uword_bits - 1)); + r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); q = (q << 1) | carry; // carry = 0; // if (r.all >= d.all) @@ -329,8 +329,8 @@ extern fn __udivsi3(n: u32, d: u32) u32 { // r.all -= d.all; // carry = 1; // } - const s = i32(d -% r -% 1) >> u5(n_uword_bits - 1); - carry = u32(s & 1); + const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); + carry = @intCast(u32, s & 1); r -= d & @bitCast(u32, s); } q = (q << 1) | carry; diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 894dd02239..e6b4ee0482 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -71,7 +71,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: r[high] = n[high] & (d[high] - 1); rem.* = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 } - return n[high] >> Log2SingleInt(@ctz(d[high])); + return n[high] >> @intCast(Log2SingleInt, @ctz(d[high])); } // K K // --- @@ -88,10 +88,10 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // 1 <= sr <= SingleInt.bit_count - 1 // q.all = a << (DoubleInt.bit_count - sr); q[low] = 0; - q[high] = n[low] << Log2SingleInt(SingleInt.bit_count - sr); + q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr); // r.all = a >> sr; - r[high] = n[high] >> Log2SingleInt(sr); - r[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); + r[high] = n[high] >> @intCast(Log2SingleInt, sr); + r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); } else { // d[low] != 0 if (d[high] == 0) { @@ -107,8 +107,8 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: return a; } sr = @ctz(d[low]); - q[high] = n[high] >> Log2SingleInt(sr); - q[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); + q[high] = n[high] >> @intCast(Log2SingleInt, sr); + q[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); return @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &q[0]).*; // TODO issue #421 } // K X @@ -126,15 +126,15 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: } else if (sr < SingleInt.bit_count) { // 2 <= sr <= SingleInt.bit_count - 1 q[low] = 0; - q[high] = n[low] << Log2SingleInt(SingleInt.bit_count - sr); - r[high] = n[high] >> Log2SingleInt(sr); - r[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); + q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr); + r[high] = n[high] >> @intCast(Log2SingleInt, sr); + r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); } else { // SingleInt.bit_count + 1 <= sr <= DoubleInt.bit_count - 1 - q[low] = n[low] << Log2SingleInt(DoubleInt.bit_count - sr); - q[high] = (n[high] << Log2SingleInt(DoubleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr - SingleInt.bit_count)); + q[low] = n[low] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr); + q[high] = (n[high] << @intCast(Log2SingleInt, DoubleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count)); r[high] = 0; - r[low] = n[high] >> Log2SingleInt(sr - SingleInt.bit_count); + r[low] = n[high] >> @intCast(Log2SingleInt, sr - SingleInt.bit_count); } } else { // K X @@ -158,9 +158,9 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: r[high] = 0; r[low] = n[high]; } else { - r[high] = n[high] >> Log2SingleInt(sr); - r[low] = (n[high] << Log2SingleInt(SingleInt.bit_count - sr)) | (n[low] >> Log2SingleInt(sr)); - q[high] = n[low] << Log2SingleInt(SingleInt.bit_count - sr); + r[high] = n[high] >> @intCast(Log2SingleInt, sr); + r[low] = (n[high] << @intCast(Log2SingleInt, SingleInt.bit_count - sr)) | (n[low] >> @intCast(Log2SingleInt, sr)); + q[high] = n[low] << @intCast(Log2SingleInt, SingleInt.bit_count - sr); } } } @@ -184,8 +184,8 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: // carry = 1; // } r_all = @ptrCast(*align(@alignOf(SingleInt)) DoubleInt, &r[0]).*; // TODO issue #421 - const s: SignedDoubleInt = SignedDoubleInt(b -% r_all -% 1) >> (DoubleInt.bit_count - 1); - carry = u32(s & 1); + const s: SignedDoubleInt = @intCast(SignedDoubleInt, b -% r_all -% 1) >> (DoubleInt.bit_count - 1); + carry = @intCast(u32, s & 1); r_all -= b & @bitCast(DoubleInt, s); r = @ptrCast(*[2]SingleInt, &r_all).*; // TODO issue #421 } diff --git a/std/unicode.zig b/std/unicode.zig index ec808ca4fe..9c329acc68 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -35,22 +35,22 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 { // - Increasing the initial shift by 6 each time // - Each time after the first shorten the shifted // value to a max of 0b111111 (63) - 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range + 1 => out[0] = @intCast(u8, c), // Can just do 0 + codepoint for initial range 2 => { - out[0] = u8(0b11000000 | (c >> 6)); - out[1] = u8(0b10000000 | (c & 0b111111)); + out[0] = @intCast(u8, 0b11000000 | (c >> 6)); + out[1] = @intCast(u8, 0b10000000 | (c & 0b111111)); }, 3 => { if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf; - out[0] = u8(0b11100000 | (c >> 12)); - out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); - out[2] = u8(0b10000000 | (c & 0b111111)); + out[0] = @intCast(u8, 0b11100000 | (c >> 12)); + out[1] = @intCast(u8, 0b10000000 | ((c >> 6) & 0b111111)); + out[2] = @intCast(u8, 0b10000000 | (c & 0b111111)); }, 4 => { - out[0] = u8(0b11110000 | (c >> 18)); - out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); - out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); - out[3] = u8(0b10000000 | (c & 0b111111)); + out[0] = @intCast(u8, 0b11110000 | (c >> 18)); + out[1] = @intCast(u8, 0b10000000 | ((c >> 12) & 0b111111)); + out[2] = @intCast(u8, 0b10000000 | ((c >> 6) & 0b111111)); + out[3] = @intCast(u8, 0b10000000 | (c & 0b111111)); }, else => unreachable, } diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 4534529f36..79f1871b64 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -1128,7 +1128,7 @@ pub const Tokenizer = struct { // check utf8-encoded character. const length = std.unicode.utf8ByteSequenceLength(c0) catch return 1; if (self.index + length > self.buffer.len) { - return u3(self.buffer.len - self.index); + return @intCast(u3, self.buffer.len - self.index); } const bytes = self.buffer[self.index .. self.index + length]; switch (length) { diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 4c216010eb..7035740c54 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -343,7 +343,7 @@ fn testPeerErrorAndArray2(x: u8) error![]const u8 { test "explicit cast float number literal to integer if no fraction component" { const x = i32(1e4); assert(x == 10000); - const y = i32(f32(1e4)); + const y = @floatToInt(i32, f32(1e4)); assert(y == 10000); } @@ -398,3 +398,19 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const x: [*]const ?[*]const u8 = &window_name; assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } + +test "@intCast comptime_int" { + const result = @intCast(i32, 1234); + assert(@typeOf(result) == i32); + assert(result == 1234); +} + +test "@floatCast comptime_int and comptime_float" { + const result = @floatCast(f32, 1234); + assert(@typeOf(result) == f32); + assert(result == 1234.0); + + const result2 = @floatCast(f32, 1234.0); + assert(@typeOf(result) == f32); + assert(result == 1234.0); +} diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 5c78d73092..6a02a47784 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -99,7 +99,7 @@ test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(IntToEnumNumber(u3(x)) == IntToEnumNumber.Three); + assert(IntToEnumNumber(@intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 08d3f3a841..6c919e17a6 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -5,7 +5,7 @@ const builtin = @import("builtin"); test "compile time recursion" { assert(some_data.len == 21); } -var some_data: [usize(fibonacci(7))]u8 = undefined; +var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; fn fibonacci(x: i32) i32 { if (x <= 1) return 1; return fibonacci(x - 1) + fibonacci(x - 2); @@ -356,7 +356,7 @@ const global_array = x: { test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; - const byte = u8(spartan_count); + const byte = @intCast(u8, spartan_count); assert(byte == 255); } } @@ -440,7 +440,7 @@ test "binary math operator in partially inlined function" { var b: [16]u8 = undefined; for (b) |*r, i| - r.* = u8(i + 1); + r.* = @intCast(u8, i + 1); copyWithPartialInline(s[0..], b[0..]); assert(s[0] == 0x1020304); @@ -480,7 +480,7 @@ fn generateTable(comptime T: type) [1010]T { var res: [1010]T = undefined; var i: usize = 0; while (i < 1010) : (i += 1) { - res[i] = T(i); + res[i] = @intCast(T, i); } return res; } diff --git a/test/cases/fn.zig b/test/cases/fn.zig index 12f22bfc35..47f7d5e688 100644 --- a/test/cases/fn.zig +++ b/test/cases/fn.zig @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == u32(i) + 5); + assert(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { diff --git a/test/cases/for.zig b/test/cases/for.zig index bdbab312f6..59d90c1b85 100644 --- a/test/cases/for.zig +++ b/test/cases/for.zig @@ -46,7 +46,7 @@ test "basic for loop" { buf_index += 1; } for (array) |item, index| { - buffer[buf_index] = u8(index); + buffer[buf_index] = @intCast(u8, index); buf_index += 1; } const unknown_size: []const u8 = array; @@ -55,7 +55,7 @@ test "basic for loop" { buf_index += 1; } for (unknown_size) |item, index| { - buffer[buf_index] = u8(index); + buffer[buf_index] = @intCast(u8, index); buf_index += 1; } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 6952611a8c..94a2ba6336 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -365,14 +365,14 @@ test "runtime struct initialization of bitfield" { .y = x1, }; const s2 = Nibbles{ - .x = u4(x2), - .y = u4(x2), + .x = @intCast(u4, x2), + .y = @intCast(u4, x2), }; assert(s1.x == x1); assert(s1.y == x1); - assert(s2.x == u4(x2)); - assert(s2.y == u4(x2)); + assert(s2.x == @intCast(u4, x2)); + assert(s2.y == @intCast(u4, x2)); } var x1 = u4(1); -- cgit v1.2.3 From e5956f23ca702b79a3a4b0f0440a2fe88e0231e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 17 Jun 2018 12:47:27 -0400 Subject: add target C int type information for msp430 target closes #1125 --- src/target.cpp | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/target.cpp b/src/target.cpp index bd4aa4d4c2..c717c533df 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -686,21 +686,41 @@ static int get_arch_pointer_bit_width(ZigLLVM_ArchType arch) { uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { switch (target->os) { case OsFreestanding: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - return 32; - case CIntTypeLong: - case CIntTypeULong: - return get_arch_pointer_bit_width(target->arch.arch); - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); + switch (target->arch.arch) { + case ZigLLVM_msp430: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 16; + case CIntTypeLong: + case CIntTypeULong: + return get_arch_pointer_bit_width(target->arch.arch); + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + default: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 32; + case CIntTypeLong: + case CIntTypeULong: + return get_arch_pointer_bit_width(target->arch.arch); + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } } case OsLinux: case OsMacOSX: -- cgit v1.2.3 From 92a36040b1d0882c1e9439b998d3b855e84b9f2c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 01:03:45 -0400 Subject: msp430 target: c_long is always 32 bits closes #1125 --- src/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/target.cpp b/src/target.cpp index c717c533df..91d36c5109 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -697,7 +697,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { return 16; case CIntTypeLong: case CIntTypeULong: - return get_arch_pointer_bit_width(target->arch.arch); + return 32; case CIntTypeLongLong: case CIntTypeULongLong: return 64; -- cgit v1.2.3 From 4210f1f6a0f55fb7c7b287ac691582752340f79d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 03:07:16 -0400 Subject: remove bool to int syntax. add @boolToInt add missing docs See #1061 --- doc/langref.html.in | 90 ++++++++++++++++++++++++++-------- src/all_types.hpp | 8 +++ src/codegen.cpp | 2 + src/ir.cpp | 60 ++++++++++++++++++++--- src/ir_print.cpp | 9 ++++ std/fmt/errol/index.zig | 4 +- std/math/big/int.zig | 18 +++---- std/special/compiler_rt/comparetf2.zig | 2 +- test/cases/bool.zig | 8 +-- 9 files changed, 158 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 35ca9a13b4..7c1f9b53d9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4560,6 +4560,19 @@ comptime {

      {#see_also|Alignment#} {#header_close#} + + {#header_open|@boolToInt#} +
      @boolToInt(value: bool) u1
      +

      + Converts true to u1(1) and false to + u1(0). +

      +

      + If the value is known at compile-time, the return type is comptime_int + instead of u1. +

      + {#header_close#} + {#header_open|@cDefine#}
      @cDefine(comptime name: []u8, value)

      @@ -4834,21 +4847,6 @@ test "main" { Creates a symbol in the output object file.

      {#header_close#} - {#header_open|@tagName#} -
      @tagName(value: var) []const u8
      -

      - Converts an enum value or union value to a slice of bytes representing the name. -

      - {#header_close#} - {#header_open|@TagType#} -
      @TagType(T: type) type
      -

      - For an enum, returns the integer type that is used to store the enumeration value. -

      -

      - For a union, returns the enum type that is used to store the tag value. -

      - {#header_close#} {#header_open|@errorName#}
      @errorName(err: error) []u8

      @@ -4883,6 +4881,12 @@ test "main" {

      {#see_also|Compile Variables#} {#header_close#} + + {#header_open|@field#} +
      @field(lhs: var, comptime field_name: []const u8) (field)
      +

      Preforms field access equivalent to lhs.->field_name-<.

      + {#header_close#} + {#header_open|@fieldParentPtr#}
      @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
           field_ptr: *T) *ParentType
      @@ -4890,6 +4894,23 @@ test "main" { Given a pointer to a field, returns the base pointer of a struct.

      {#header_close#} + + {#header_open|@floatCast#} +
      @floatCast(comptime DestType: type, value: var) DestType
      +

      + Convert from one float type to another. This cast is safe, but may cause the + numeric value to lose precision. +

      + {#header_close#} + + {#header_open|@floatToInt#} +
      @floatToInt(comptime DestType: type, float: var) DestType
      +

      + Converts the integer part of a floating point number to the destination type. + To convert the other way, use {#link|@intToFloat#}. This cast is always safe. +

      + {#header_close#} + {#header_open|@frameAddress#}
      @frameAddress()

      @@ -4944,12 +4965,30 @@ fn add(a: i32, b: i32) i32 { return a + b; }

      {#see_also|@noInlineCall#} {#header_close#} + + {#header_open|@intCast#} +
      @intCast(comptime DestType: type, int: var) DestType
      +

      + Converts an integer to another integer while keeping the same numerical value. + Attempting to convert a number which is out of range of the destination type results in + {#link|Undefined Behavior#}. +

      + {#header_close#} + + {#header_open|@intToFloat#} +
      @intToFloat(comptime DestType: type, int: var) DestType
      +

      + Converts an integer to the closest floating point representation. To convert the other way, use {#link|@floatToInt#}. This cast is always safe. +

      + {#header_close#} + {#header_open|@intToPtr#}
      @intToPtr(comptime DestType: type, int: usize) DestType

      Converts an integer to a pointer. To convert the other way, use {#link|@ptrToInt#}.

      {#header_close#} + {#header_open|@IntType#}
      @IntType(comptime is_signed: bool, comptime bit_count: u8) type

      @@ -4987,10 +5026,6 @@ fn add(a: i32, b: i32) i32 { return a + b; } It does not include functions, variables, or constants.

      {#header_close#} - {#header_open|@field#} -
      @field(lhs: var, comptime field_name: []const u8) (field)
      -

      Preforms field access equivalent to lhs.->field_name-<.

      - {#header_close#} {#header_open|@memberType#}
      @memberType(comptime T: type, comptime index: usize) type

      Returns the field type of a struct or union.

      @@ -5370,6 +5405,21 @@ pub const FloatMode = enum { If no overflow or underflow occurs, returns false.

      {#header_close#} + {#header_open|@tagName#} +
      @tagName(value: var) []const u8
      +

      + Converts an enum value or union value to a slice of bytes representing the name. +

      + {#header_close#} + {#header_open|@TagType#} +
      @TagType(T: type) type
      +

      + For an enum, returns the integer type that is used to store the enumeration value. +

      +

      + For a union, returns the enum type that is used to store the tag value. +

      + {#header_close#} {#header_open|@truncate#}
      @truncate(comptime T: type, integer) T

      @@ -6665,7 +6715,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index bc2fe07c18..d94dfa0fcb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1361,6 +1361,7 @@ enum BuiltinFnId { BuiltinFnIdFloatCast, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, + BuiltinFnIdBoolToInt, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2048,6 +2049,7 @@ enum IrInstructionId { IrInstructionIdFloatCast, IrInstructionIdIntToFloat, IrInstructionIdFloatToInt, + IrInstructionIdBoolToInt, IrInstructionIdIntType, IrInstructionIdBoolNot, IrInstructionIdMemset, @@ -2668,6 +2670,12 @@ struct IrInstructionFloatToInt { IrInstruction *target; }; +struct IrInstructionBoolToInt { + IrInstruction base; + + IrInstruction *target; +}; + struct IrInstructionIntType { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4108cbbd68..84335d4e06 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4726,6 +4726,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6318,6 +6319,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); + create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 0b847fc4e4..e6339a72f6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -476,6 +476,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) { return IrInstructionIdFloatToInt; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolToInt *) { + return IrInstructionIdBoolToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } @@ -1959,6 +1963,15 @@ static IrInstruction *ir_build_float_to_int(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } +static IrInstruction *ir_build_bool_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { + IrInstructionBoolToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) { IrInstructionIntType *instruction = ir_build_instruction(irb, scope, source_node); instruction->is_signed = is_signed; @@ -4071,6 +4084,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdBoolToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntType: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10055,13 +10078,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } - // explicit cast from bool to int - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdBool) - { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBoolToInt, false); - } - // explicit widening conversion if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && @@ -17605,6 +17621,33 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdBool) { + ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + if (instr_is_comptime(target)) { + bool is_true; + if (!ir_resolve_bool(ira, target, &is_true)) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, is_true ? 1 : 0); + return ira->codegen->builtin_types.entry_num_lit_int; + } + + TypeTableEntry *u1_type = get_int_type(ira->codegen, false, 1); + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt, false); + ir_link_new_instruction(result, &instruction->base); + return u1_type; +} + static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) { IrInstruction *is_signed_value = instruction->is_signed->other; bool is_signed; @@ -20143,6 +20186,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction); + case IrInstructionIdBoolToInt: + return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); case IrInstructionIdBoolNot: @@ -20490,6 +20535,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFloatCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: + case IrInstructionIdBoolToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5722c52fa..cb91720180 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -680,6 +680,12 @@ static void ir_print_float_to_int(IrPrint *irp, IrInstructionFloatToInt *instruc fprintf(irp->f, ")"); } +static void ir_print_bool_to_int(IrPrint *irp, IrInstructionBoolToInt *instruction) { + fprintf(irp->f, "@boolToInt("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, "@IntType("); ir_print_other_instruction(irp, instruction->is_signed); @@ -1461,6 +1467,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFloatToInt: ir_print_float_to_int(irp, (IrInstructionFloatToInt *)instruction); break; + case IrInstructionIdBoolToInt: + ir_print_bool_to_int(irp, (IrInstructionBoolToInt *)instruction); + break; case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig index a5fb692857..3222913107 100644 --- a/std/fmt/errol/index.zig +++ b/std/fmt/errol/index.zig @@ -329,7 +329,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var mi: i32 = mismatch10(l64, h64); var x: u64 = 1; { - var i = i32(lf == hf); + var i: i32 = @boolToInt(lf == hf); while (i < mi) : (i += 1) { x *= 10; } @@ -341,7 +341,7 @@ fn errolInt(val: f64, buffer: []u8) FloatDecimal { var buf_index = u64toa(m64, buffer) - 1; if (mi != 0) { - buffer[buf_index - 1] += u8(buffer[buf_index] >= '5'); + buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); } else { buf_index += 1; } diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 21d9347c01..29673538eb 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -117,7 +117,7 @@ pub const Int = struct { fn bitcount(self: Int) usize { const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1])); - return usize(!self.positive) + u_bit_count; + return usize(@boolToInt(!self.positive)) + u_bit_count; } pub fn sizeInBase(self: Int, base: usize) usize { @@ -499,13 +499,13 @@ pub const Int = struct { while (i < b.len) : (i += 1) { var c: Limb = 0; - c += Limb(@addWithOverflow(Limb, a[i], b[i], &r[i])); - c += Limb(@addWithOverflow(Limb, r[i], carry, &r[i])); + c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i])); + c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); carry = c; } while (i < a.len) : (i += 1) { - carry = Limb(@addWithOverflow(Limb, a[i], carry, &r[i])); + carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i])); } r[i] = carry; @@ -577,13 +577,13 @@ pub const Int = struct { while (i < b.len) : (i += 1) { var c: Limb = 0; - c += Limb(@subWithOverflow(Limb, a[i], b[i], &r[i])); - c += Limb(@subWithOverflow(Limb, r[i], borrow, &r[i])); + c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i])); + c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i])); borrow = c; } while (i < a.len) : (i += 1) { - borrow = Limb(@subWithOverflow(Limb, a[i], borrow, &r[i])); + borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i])); } debug.assert(borrow == 0); @@ -624,7 +624,7 @@ pub const Int = struct { var r1: Limb = undefined; // r1 = a + *carry - const c1 = Limb(@addWithOverflow(Limb, a, carry.*, &r1)); + const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); // r2 = b * c // @@ -639,7 +639,7 @@ pub const Int = struct { const c2 = @truncate(Limb, bc >> Limb.bit_count); // r1 = r1 + r2 - const c3 = Limb(@addWithOverflow(Limb, r1, r2, &r1)); + const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1)); // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then // c2 is at least <= @maxValue(Limb) - 2. diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig index d63b7a7c92..479ba51962 100644 --- a/std/special/compiler_rt/comparetf2.zig +++ b/std/special/compiler_rt/comparetf2.zig @@ -91,5 +91,5 @@ pub extern fn __unordtf2(a: f128, b: f128) c_int { const aAbs = @bitCast(rep_t, a) & absMask; const bAbs = @bitCast(rep_t, b) & absMask; - return c_int(aAbs > infRep or bAbs > infRep); + return @boolToInt(aAbs > infRep or bAbs > infRep); } diff --git a/test/cases/bool.zig b/test/cases/bool.zig index 07d30454ee..3e4ac9c1cf 100644 --- a/test/cases/bool.zig +++ b/test/cases/bool.zig @@ -8,14 +8,14 @@ test "bool literals" { test "cast bool to int" { const t = true; const f = false; - assert(i32(t) == i32(1)); - assert(i32(f) == i32(0)); + assert(@boolToInt(t) == u32(1)); + assert(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(i32(t) == i32(1)); - assert(i32(f) == i32(0)); + assert(@boolToInt(t) == u32(1)); + assert(@boolToInt(f) == u32(0)); } test "bool cmp" { -- cgit v1.2.3 From e6b69151c0d30d2df95d1f92b65784eac7d186d9 Mon Sep 17 00:00:00 2001 From: Bodie Solomon Date: Sun, 17 Jun 2018 14:35:00 -0400 Subject: Fix 1117: Use realpath in stage1 Darwin os_self_exe_path Issue: https://github.com/ziglang/zig/issues/1117 The macOS stage1 Zig compiler should look in Zig's real absolute path for the Zig stdlib, but os_self_exe_path looks in its path as returned by _NSGetExecutablePath, which may be a symlink. This means that a symlinked Zig cannot find the Zig stdlib. This patch fixes the issue by resolving the _NSGetExecutablePath result to the real path using realpath() before copying the result to the output path. --- src/os.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index 97462bd658..75e6dd8658 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -989,12 +989,28 @@ int os_self_exe_path(Buf *out_path) { } #elif defined(ZIG_OS_DARWIN) + // How long is the executable's path? uint32_t u32_len = 0; int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - buf_resize(out_path, u32_len); - int ret2 = _NSGetExecutablePath(buf_ptr(out_path), &u32_len); + + // Allocate a buffer for this path. + Buf *path_tmp = buf_alloc_fixed(u32_len); + // Fill the buffer with the path, which may be a symlink. + int ret2 = _NSGetExecutablePath(buf_ptr(path_tmp), &u32_len); assert(ret2 == 0); + + // Make a buffer with room for the real path. + Buf *resolve_tmp = buf_alloc_fixed(PATH_MAX); + + // Fill it with the real resolved path. + char *real_path = realpath(buf_ptr(path_tmp), buf_ptr(resolve_tmp)); + // IEEE Std 1003.1-2017: realpath() shall return a pointer to the + // buffer containing the resolved name. + assert(real_path == buf_ptr(resolve_tmp)); + + // Resize out_path and copy the resulting resolved absolute path. + buf_init_from_buf(out_path, resolve_tmp); return 0; #elif defined(ZIG_OS_LINUX) buf_resize(out_path, 256); -- cgit v1.2.3 From 045682289243c6186363e984babc706c3ed93152 Mon Sep 17 00:00:00 2001 From: Bodie Solomon Date: Mon, 18 Jun 2018 07:01:59 -0400 Subject: Fix 1117: Tweak realpath logic to use out_path as scratch space --- src/os.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index 75e6dd8658..0e3c3f9a60 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -994,23 +994,23 @@ int os_self_exe_path(Buf *out_path) { int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - // Allocate a buffer for this path. - Buf *path_tmp = buf_alloc_fixed(u32_len); - // Fill the buffer with the path, which may be a symlink. - int ret2 = _NSGetExecutablePath(buf_ptr(path_tmp), &u32_len); + // Make room for the executable path and resolved path in out_path, + // then subslice it for convenience. + buf_resize(out_path, u32_len + PATH_MAX); + Buf *tmp = buf_slice(out_path, 0, u32_len); + Buf *resolved = buf_slice(out_path, u32_len, u32_len + PATH_MAX); + + // Fill the executable path. + int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); assert(ret2 == 0); - // Make a buffer with room for the real path. - Buf *resolve_tmp = buf_alloc_fixed(PATH_MAX); + // Resolve the real path from that. + char *real_path = realpath(buf_ptr(tmp), buf_ptr(resolved)); + assert(real_path == buf_ptr(resolved)); - // Fill it with the real resolved path. - char *real_path = realpath(buf_ptr(path_tmp), buf_ptr(resolve_tmp)); - // IEEE Std 1003.1-2017: realpath() shall return a pointer to the - // buffer containing the resolved name. - assert(real_path == buf_ptr(resolve_tmp)); - - // Resize out_path and copy the resulting resolved absolute path. - buf_init_from_buf(out_path, resolve_tmp); + // Write the real path back into the beginning of out_path, resize. + buf_init_from_buf(out_path, resolved); + assert(buf_len(out_path) == buf_len(resolved)); return 0; #elif defined(ZIG_OS_LINUX) buf_resize(out_path, 256); -- cgit v1.2.3 From c7057bd25b2a99e5ac61963efd588f5fe4ba93c6 Mon Sep 17 00:00:00 2001 From: Bodie Solomon Date: Mon, 18 Jun 2018 07:37:26 -0400 Subject: Fix 1117: Revise realpath scratch logic --- src/os.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index 0e3c3f9a60..75f748e2f0 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -994,23 +994,24 @@ int os_self_exe_path(Buf *out_path) { int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - // Make room for the executable path and resolved path in out_path, - // then subslice it for convenience. - buf_resize(out_path, u32_len + PATH_MAX); - Buf *tmp = buf_slice(out_path, 0, u32_len); - Buf *resolved = buf_slice(out_path, u32_len, u32_len + PATH_MAX); + // Make a buffer having room for the temp path. + Buf *tmp = buf_alloc_fixed(u32_len); // Fill the executable path. int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); assert(ret2 == 0); // Resolve the real path from that. - char *real_path = realpath(buf_ptr(tmp), buf_ptr(resolved)); - assert(real_path == buf_ptr(resolved)); + buf_resize(out_path, PATH_MAX); + char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); + assert(real_path == buf_ptr(out_path)); + + // Deallocate our scratch space. + buf_deinit(tmp); + + // Resize out_path for the correct length. + buf_resize(out_path, strlen(buf_ptr(out_path))); - // Write the real path back into the beginning of out_path, resize. - buf_init_from_buf(out_path, resolved); - assert(buf_len(out_path) == buf_len(resolved)); return 0; #elif defined(ZIG_OS_LINUX) buf_resize(out_path, 256); -- cgit v1.2.3 From d49d6f0cde782f4ec3c1d623d58644c3e51a6ce9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 11:04:18 -0400 Subject: fix compiler crash when using @intToFloat with float literal closes #1132 --- src/ir.cpp | 6 ++++++ test/cases/cast.zig | 6 ++++++ test/compile_errors.zig | 8 ++++++++ 3 files changed, 20 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index e6339a72f6..a312b501ab 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17602,6 +17602,12 @@ static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrIns if (type_is_invalid(target->value.type)) return ira->codegen->builtin_types.entry_invalid; + if (target->value.type->id != TypeTableEntryIdInt && target->value.type->id != TypeTableEntryIdComptimeInt) { + ir_add_error(ira, instruction->target, buf_sprintf("expected int type, found '%s'", + buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat, false); ir_link_new_instruction(result, &instruction->base); return dest_type; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 7035740c54..f1e49c6d1f 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -414,3 +414,9 @@ test "@floatCast comptime_int and comptime_float" { assert(@typeOf(result) == f32); assert(result == 1234.0); } + +test "comptime_int @intToFloat" { + const result = @intToFloat(f32, 1234); + assert(@typeOf(result) == f32); + assert(result == 1234.0); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b51a6e9761..23337ca479 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,14 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "non int passed to @intToFloat", + \\export fn entry() void { + \\ const x = @intToFloat(f32, 1.1); + \\} + , + ".tmp_source.zig:2:32: error: expected int type, found 'comptime_float'", + ); cases.add( "use implicit casts to assign null to non-nullable pointer", \\export fn entry() void { -- cgit v1.2.3 From 8fd7cc11e167c0b23892d6f22841bb6856d0f499 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 11:12:15 -0400 Subject: disallow opaque as a return type of fn type syntax closes #1115 --- src/analyze.cpp | 1 + src/ir.cpp | 5 +++++ test/compile_errors.zig | 10 ++++++++++ 3 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 758bc1a045..10cdb0af6f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1022,6 +1022,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { ensure_complete_type(g, fn_type_id->return_type); if (type_is_invalid(fn_type_id->return_type)) return g->builtin_types.entry_invalid; + assert(fn_type_id->return_type->id != TypeTableEntryIdOpaque); } else { zig_panic("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"); } diff --git a/src/ir.cpp b/src/ir.cpp index a312b501ab..c75a3ae7c1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18676,6 +18676,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc fn_type_id.return_type = ir_resolve_type(ira, return_type_value); if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->builtin_types.entry_invalid; + if (fn_type_id.return_type->id == TypeTableEntryIdOpaque) { + ir_add_error(ira, instruction->return_type, + buf_sprintf("return type cannot be opaque")); + return ira->codegen->builtin_types.entry_invalid; + } if (fn_type_id.cc == CallingConventionAsync) { if (instruction->async_allocator_type_value == nullptr) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 23337ca479..8c5abaaccc 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "use c_void as return type of fn ptr", + \\export fn entry() void { + \\ const a: fn () c_void = undefined; + \\} + , + ".tmp_source.zig:2:20: error: return type cannot be opaque", + ); + cases.add( "non int passed to @intToFloat", \\export fn entry() void { @@ -9,6 +18,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:2:32: error: expected int type, found 'comptime_float'", ); + cases.add( "use implicit casts to assign null to non-nullable pointer", \\export fn entry() void { -- cgit v1.2.3 From 4ce36a64755b4b2ca8bb28e3ac91b23dfe90ade8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 12:18:39 -0400 Subject: adjust logic for finding the path to zig executable on darwin --- src/os.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index 75f748e2f0..14e9effb1e 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1004,10 +1004,10 @@ int os_self_exe_path(Buf *out_path) { // Resolve the real path from that. buf_resize(out_path, PATH_MAX); char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); - assert(real_path == buf_ptr(out_path)); - - // Deallocate our scratch space. - buf_deinit(tmp); + if (!real_path) { + buf_init_from_buf(out_path, tmp); + return 0; + } // Resize out_path for the correct length. buf_resize(out_path, strlen(buf_ptr(out_path))); -- cgit v1.2.3 From cd4676a2338430d9e424f297b8b576143c5be180 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 12:54:03 -0400 Subject: stage1: update darwin code to workaround old libc bug See #1128 --- src/os.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index 14e9effb1e..b7d2fd1de0 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -994,15 +994,15 @@ int os_self_exe_path(Buf *out_path) { int ret1 = _NSGetExecutablePath(nullptr, &u32_len); assert(ret1 != 0); - // Make a buffer having room for the temp path. Buf *tmp = buf_alloc_fixed(u32_len); // Fill the executable path. int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); assert(ret2 == 0); - // Resolve the real path from that. - buf_resize(out_path, PATH_MAX); + // According to libuv project, PATH_MAX*2 works around a libc bug where + // the resolved path is sometimes bigger than PATH_MAX. + buf_resize(out_path, PATH_MAX*2); char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); if (!real_path) { buf_init_from_buf(out_path, tmp); -- cgit v1.2.3 From 5d705fc6e35e75a604d3dbbb377ab01bf2b2b575 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 15:01:42 -0400 Subject: remove error set casting syntax. add `@errSetCast` See #1061 --- doc/langref.html.in | 31 ++++++++++++++++++------ src/all_types.hpp | 9 +++++++ src/codegen.cpp | 2 ++ src/ir.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++------ src/ir_print.cpp | 11 +++++++++ test/cases/error.zig | 4 ++-- 6 files changed, 109 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index f1ae2bafaa..48f525fedc 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4919,12 +4919,15 @@ test "main" {

      {#see_also|@import#} {#header_close#} - {#header_open|@export#} -
      @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8
      + + {#header_open|@errSetCast#} +
      @errSetCast(comptime T: DestType, value: var) DestType

      - Creates a symbol in the output object file. + Converts an error value from one error set to another error set. Attempting to convert an error + which is not in the destination error set results in {#link|Undefined Behavior#}.

      {#header_close#} + {#header_open|@errorName#}
      @errorName(err: error) []u8

      @@ -4949,6 +4952,14 @@ test "main" { stack trace object. Otherwise returns `null`.

      {#header_close#} + + {#header_open|@export#} +
      @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8
      +

      + Creates a symbol in the output object file. +

      + {#header_close#} + {#header_open|@fence#}
      @fence(order: AtomicOrder)

      @@ -5817,10 +5828,10 @@ pub fn build(b: &Builder) void { {#header_open|Undefined Behavior#}

      Zig has many instances of undefined behavior. If undefined behavior is - detected at compile-time, Zig emits an error. Most undefined behavior that - cannot be detected at compile-time can be detected at runtime. In these cases, - Zig has safety checks. Safety checks can be disabled on a per-block basis - with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#} + detected at compile-time, Zig emits a compile error and refuses to continue. + Most undefined behavior that cannot be detected at compile-time can be detected + at runtime. In these cases, Zig has safety checks. Safety checks can be disabled + on a per-block basis with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#} build mode disables all safety checks in order to facilitate optimizations.

      @@ -6101,6 +6112,11 @@ comptime {

      TODO

      {#header_close#} + + {#header_open|Invalid Error Set Cast#} +

      TODO

      + {#header_close#} + {#header_open|Incorrect Pointer Alignment#}

      TODO

      @@ -6109,6 +6125,7 @@ comptime {

      TODO

      {#header_close#} + {#header_close#} {#header_open|Memory#}

      TODO: explain no default allocator in zig

      diff --git a/src/all_types.hpp b/src/all_types.hpp index d94dfa0fcb..732af239e2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1359,6 +1359,7 @@ enum BuiltinFnId { BuiltinFnIdTruncate, BuiltinFnIdIntCast, BuiltinFnIdFloatCast, + BuiltinFnIdErrSetCast, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, BuiltinFnIdBoolToInt, @@ -2121,6 +2122,7 @@ enum IrInstructionId { IrInstructionIdMergeErrRetTraces, IrInstructionIdMarkErrRetTracePtr, IrInstructionIdSqrt, + IrInstructionIdErrSetCast, }; struct IrInstruction { @@ -2656,6 +2658,13 @@ struct IrInstructionFloatCast { IrInstruction *target; }; +struct IrInstructionErrSetCast { + IrInstruction base; + + IrInstruction *dest_type; + IrInstruction *target; +}; + struct IrInstructionIntToFloat { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 84335d4e06..585881a9a5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4727,6 +4727,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: + case IrInstructionIdErrSetCast: zig_unreachable(); case IrInstructionIdReturn: @@ -6356,6 +6357,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); + create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index c75a3ae7c1..e5ba4114cf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -468,6 +468,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) { return IrInstructionIdFloatCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) { + return IrInstructionIdErrSetCast; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { return IrInstructionIdIntToFloat; } @@ -1941,6 +1945,17 @@ static IrInstruction *ir_build_float_cast(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { + IrInstructionErrSetCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; @@ -4054,6 +4069,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdErrSetCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10104,13 +10134,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit error set cast - if (wanted_type->id == TypeTableEntryIdErrorSet && - actual_type->id == TypeTableEntryIdErrorSet) - { - return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); - } - // explicit cast from [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; @@ -17593,6 +17616,34 @@ static TypeTableEntry *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstr return dest_type; } +static TypeTableEntry *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { + TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdErrorSet) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdErrorSet) { + ir_add_error(ira, instruction->target, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); + if (type_is_invalid(result->value.type)) + return ira->codegen->builtin_types.entry_invalid; + ir_link_new_instruction(result, &instruction->base); + return dest_type; +} + static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) @@ -20193,6 +20244,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_cast(ira, (IrInstructionIntCast *)instruction); case IrInstructionIdFloatCast: return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); + case IrInstructionIdErrSetCast: + return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction); case IrInstructionIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: @@ -20544,6 +20597,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAtomicLoad: case IrInstructionIdIntCast: case IrInstructionIdFloatCast: + case IrInstructionIdErrSetCast: case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cb91720180..2667c246a5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -664,6 +664,14 @@ static void ir_print_float_cast(IrPrint *irp, IrInstructionFloatCast *instructio fprintf(irp->f, ")"); } +static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruction) { + fprintf(irp->f, "@errSetCast("); + ir_print_other_instruction(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { fprintf(irp->f, "@intToFloat("); ir_print_other_instruction(irp, instruction->dest_type); @@ -1461,6 +1469,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFloatCast: ir_print_float_cast(irp, (IrInstructionFloatCast *)instruction); break; + case IrInstructionIdErrSetCast: + ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction); + break; case IrInstructionIdIntToFloat: ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); break; diff --git a/test/cases/error.zig b/test/cases/error.zig index 693631fe2d..95890d8384 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -124,8 +124,8 @@ const Set2 = error{ }; fn testExplicitErrorSetCast(set1: Set1) void { - var x = Set2(set1); - var y = Set1(x); + var x = @errSetCast(Set2, set1); + var y = @errSetCast(Set1, x); assert(y == error.A); } -- cgit v1.2.3 From 1aafbae5be518309b4c2194cdc24e22642514519 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 17:25:29 -0400 Subject: remove []u8 casting syntax. add `@bytesToSlice` and `@sliceToBytes` See #1061 --- doc/langref.html.in | 45 +++++++--- src/all_types.hpp | 28 +++++++ src/codegen.cpp | 4 + src/ir.cpp | 217 ++++++++++++++++++++++++++++++++++++------------ src/ir_print.cpp | 20 +++++ std/heap.zig | 2 +- std/macho.zig | 2 +- std/mem.zig | 12 +-- std/net.zig | 2 +- std/os/windows/util.zig | 2 +- test/cases/align.zig | 2 +- test/cases/cast.zig | 8 +- test/cases/misc.zig | 4 +- test/cases/struct.zig | 4 +- test/compile_errors.zig | 21 ++--- 15 files changed, 277 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 48f525fedc..9a3d5e5f17 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1456,8 +1456,7 @@ test "pointer array access" { // Taking an address of an individual element gives a // pointer to a single item. This kind of pointer // does not support pointer arithmetic. - - var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const ptr = &array[2]; assert(@typeOf(ptr) == *u8); @@ -1469,7 +1468,7 @@ test "pointer array access" { test "pointer slicing" { // In Zig, we prefer using slices over null-terminated pointers. // You can turn an array into a slice using slice syntax: - var array = []u8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + var array = []u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; const slice = array[2..4]; assert(slice.len == 2); @@ -1541,13 +1540,13 @@ test "pointer casting" { // To convert one pointer type to another, use @ptrCast. This is an unsafe // operation that Zig cannot protect you against. Use @ptrCast only when other // conversions are not possible. - const bytes align(@alignOf(u32)) = []u8{0x12, 0x12, 0x12, 0x12}; + const bytes align(@alignOf(u32)) = []u8{ 0x12, 0x12, 0x12, 0x12 }; const u32_ptr = @ptrCast(*const u32, &bytes[0]); assert(u32_ptr.* == 0x12121212); // Even this example is contrived - there are better ways to do the above than // pointer casting. For example, using a slice narrowing cast: - const u32_value = ([]const u32)(bytes[0..])[0]; + const u32_value = @bytesToSlice(u32, bytes[0..])[0]; assert(u32_value == 0x12121212); // And even another way, the most straightforward way to do it: @@ -1630,13 +1629,13 @@ test "function alignment" { const assert = @import("std").debug.assert; test "pointer alignment safety" { - var array align(4) = []u32{0x11111111, 0x11111111}; - const bytes = ([]u8)(array[0..]); + var array align(4) = []u32{ 0x11111111, 0x11111111 }; + const bytes = @sliceToBytes(array[0..]); assert(foo(bytes) == 0x11111111); } fn foo(bytes: []u8) u32 { const slice4 = bytes[1..5]; - const int_slice = ([]u32)(@alignCast(4, slice4)); + const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); return int_slice[0]; } {#code_end#} @@ -1728,8 +1727,8 @@ test "slice pointer" { test "slice widening" { // Zig supports slice widening and slice narrowing. Cast a slice of u8 // to a slice of anything else, and Zig will perform the length conversion. - const array align(@alignOf(u32)) = []u8{0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13}; - const slice = ([]const u32)(array[0..]); + const array align(@alignOf(u32)) = []u8{ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13 }; + const slice = @bytesToSlice(u32, array[0..]); assert(slice.len == 2); assert(slice[0] == 0x12121212); assert(slice[1] == 0x13131313); @@ -4651,6 +4650,18 @@ comptime {

      {#header_close#} + {#header_open|@bytesToSlice#} +
      @bytesToSlice(comptime Element: type, bytes: []u8) []Element
      +

      + Converts a slice of bytes or array of bytes into a slice of Element. + The resulting slice has the same {#link|pointer|Pointers#} properties as the parameter. +

      +

      + Attempting to convert a number of bytes with a length that does not evenly divide into a slice of + elements results in {#link|Undefined Behavior#}. +

      + {#header_close#} + {#header_open|@cDefine#}
      @cDefine(comptime name: []u8, value)

      @@ -5467,8 +5478,9 @@ pub const FloatMode = enum {

      {#see_also|@shlExact|@shlWithOverflow#} {#header_close#} + {#header_open|@sizeOf#} -
      @sizeOf(comptime T: type) (number literal)
      +
      @sizeOf(comptime T: type) comptime_int

      This function returns the number of bytes it takes to store T in memory.

      @@ -5476,6 +5488,15 @@ pub const FloatMode = enum { The result is a target-specific compile time constant.

      {#header_close#} + + {#header_open|@sliceToBytes#} +
      @sliceToBytes(value: var) []u8
      +

      + Converts a slice or array to a slice of u8. The resulting slice has the same + {#link|pointer|Pointers#} properties as the parameter. +

      + {#header_close#} + {#header_open|@sqrt#}
      @sqrt(comptime T: type, value: T) T

      @@ -6810,7 +6831,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 732af239e2..d8432232c2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -234,6 +234,16 @@ enum RuntimeHintPtr { RuntimeHintPtrNonStack, }; +enum RuntimeHintSliceId { + RuntimeHintSliceIdUnknown, + RuntimeHintSliceIdLen, +}; + +struct RuntimeHintSlice { + enum RuntimeHintSliceId id; + uint64_t len; +}; + struct ConstGlobalRefs { LLVMValueRef llvm_value; LLVMValueRef llvm_global; @@ -270,6 +280,7 @@ struct ConstExprValue { RuntimeHintErrorUnion rh_error_union; RuntimeHintOptional rh_maybe; RuntimeHintPtr rh_ptr; + RuntimeHintSlice rh_slice; } data; }; @@ -1360,6 +1371,8 @@ enum BuiltinFnId { BuiltinFnIdIntCast, BuiltinFnIdFloatCast, BuiltinFnIdErrSetCast, + BuiltinFnIdToBytes, + BuiltinFnIdFromBytes, BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, BuiltinFnIdBoolToInt, @@ -2123,6 +2136,8 @@ enum IrInstructionId { IrInstructionIdMarkErrRetTracePtr, IrInstructionIdSqrt, IrInstructionIdErrSetCast, + IrInstructionIdToBytes, + IrInstructionIdFromBytes, }; struct IrInstruction { @@ -2665,6 +2680,19 @@ struct IrInstructionErrSetCast { IrInstruction *target; }; +struct IrInstructionToBytes { + IrInstruction base; + + IrInstruction *target; +}; + +struct IrInstructionFromBytes { + IrInstruction base; + + IrInstruction *dest_child_type; + IrInstruction *target; +}; + struct IrInstructionIntToFloat { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 585881a9a5..1bc9a17804 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4728,6 +4728,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: case IrInstructionIdErrSetCast: + case IrInstructionIdFromBytes: + case IrInstructionIdToBytes: zig_unreachable(); case IrInstructionIdReturn: @@ -6358,6 +6360,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); + create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1); + create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index e5ba4114cf..c5ca12af1e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -472,6 +472,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) { return IrInstructionIdErrSetCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytes *) { + return IrInstructionIdToBytes; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytes *) { + return IrInstructionIdFromBytes; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) { return IrInstructionIdIntToFloat; } @@ -1956,6 +1964,26 @@ static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } +static IrInstruction *ir_build_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { + IrInstructionToBytes *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_child_type, IrInstruction *target) { + IrInstructionFromBytes *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_child_type = dest_child_type; + instruction->target = target; + + ir_ref_instruction(dest_child_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); instruction->dest_type = dest_type; @@ -4084,6 +4112,31 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdFromBytes: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdToBytes: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdIntToFloat: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -9103,11 +9156,6 @@ static bool is_container(TypeTableEntry *type) { type->id == TypeTableEntryIdUnion; } -static bool is_u8(TypeTableEntry *type) { - return type->id == TypeTableEntryIdInt && - !type->data.integral.is_signed && type->data.integral.bit_count == 8; -} - static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) { assert(old_bb); @@ -9661,6 +9709,8 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope, source_instr->source_node, array_ptr, start, end, false); result->value.type = wanted_type; + result->value.data.rh_slice.id = RuntimeHintSliceIdLen; + result->value.data.rh_slice.len = array_type->data.array.len; ir_add_alloca(ira, result, result->value.type); return result; @@ -10103,7 +10153,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ira->codegen->invalid_instruction; } - // explicit match or non-const to const + // perfect match or non-const to const if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -10214,52 +10264,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from []T to []u8 or []u8 to []T - if (is_slice(wanted_type) && is_slice(actual_type)) { - TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; - if ((is_u8(wanted_ptr_type->data.pointer.child_type) || is_u8(actual_ptr_type->data.pointer.child_type)) && - (wanted_ptr_type->data.pointer.is_const || !actual_ptr_type->data.pointer.is_const)) - { - uint32_t src_align_bytes = get_ptr_align(actual_ptr_type); - uint32_t dest_align_bytes = get_ptr_align(wanted_ptr_type); - - if (dest_align_bytes > src_align_bytes) { - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("cast increases pointer alignment")); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), src_align_bytes)); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), dest_align_bytes)); - return ira->codegen->invalid_instruction; - } - - 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 []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)) - { - if (!ir_emit_global_runtime_side_effect(ira, source_instr)) - return ira->codegen->invalid_instruction; - uint64_t child_type_size = type_size(ira->codegen, - wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type); - if (actual_type->data.array.len % child_type_size == 0) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpBytesToSlice, true); - } else { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("unable to convert %s to %s: size mismatch", - buf_ptr(&actual_type->name), buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_instruction; - } - } - // explicit *[N]T to [*]T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenUnknown && @@ -17644,6 +17648,109 @@ static TypeTableEntry *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { + TypeTableEntry *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->other); + if (type_is_invalid(dest_child_type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + bool src_ptr_const; + bool src_ptr_volatile; + uint32_t src_ptr_align; + if (target->value.type->id == TypeTableEntryIdPointer) { + src_ptr_const = target->value.type->data.pointer.is_const; + src_ptr_volatile = target->value.type->data.pointer.is_volatile; + src_ptr_align = target->value.type->data.pointer.alignment; + } else if (is_slice(target->value.type)) { + TypeTableEntry *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + src_ptr_const = src_ptr_type->data.pointer.is_const; + src_ptr_volatile = src_ptr_type->data.pointer.is_volatile; + src_ptr_align = src_ptr_type->data.pointer.alignment; + } else { + src_ptr_const = true; + src_ptr_volatile = false; + src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); + } + + TypeTableEntry *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type, + src_ptr_const, src_ptr_volatile, PtrLenUnknown, + src_ptr_align, 0, 0); + TypeTableEntry *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); + + TypeTableEntry *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + src_ptr_const, src_ptr_volatile, PtrLenUnknown, + src_ptr_align, 0, 0); + TypeTableEntry *u8_slice = get_slice_type(ira->codegen, u8_ptr); + + IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice); + if (type_is_invalid(casted_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + bool have_known_len = false; + uint64_t known_len; + + if (instr_is_comptime(casted_value)) { + ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); + if (!val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index]; + if (value_is_comptime(len_val)) { + known_len = bigint_as_unsigned(&len_val->data.x_bigint); + have_known_len = true; + } + } + + if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) { + known_len = casted_value->value.data.rh_slice.len; + have_known_len = true; + } + + if (have_known_len) { + uint64_t child_type_size = type_size(ira->codegen, dest_child_type); + uint64_t remainder = known_len % child_type_size; + if (remainder != 0) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base, + buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch", + known_len, buf_ptr(&dest_slice_type->name))); + add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node, + buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64, + buf_ptr(&dest_child_type->name), child_type_size, remainder)); + return ira->codegen->builtin_types.entry_invalid; + } + } + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); + ir_link_new_instruction(result, &instruction->base); + return dest_slice_type; +} + +static TypeTableEntry *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (!is_slice(target->value.type)) { + ir_add_error(ira, instruction->target, + buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + TypeTableEntry *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + + TypeTableEntry *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown, + src_ptr_type->data.pointer.alignment, 0, 0); + TypeTableEntry *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); + + IrInstruction *result = ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); + ir_link_new_instruction(result, &instruction->base); + return dest_slice_type; +} + static TypeTableEntry *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { TypeTableEntry *dest_type = ir_resolve_type(ira, instruction->dest_type->other); if (type_is_invalid(dest_type)) @@ -20246,6 +20353,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction); case IrInstructionIdErrSetCast: return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction); + case IrInstructionIdFromBytes: + return ir_analyze_instruction_from_bytes(ira, (IrInstructionFromBytes *)instruction); + case IrInstructionIdToBytes: + return ir_analyze_instruction_to_bytes(ira, (IrInstructionToBytes *)instruction); case IrInstructionIdIntToFloat: return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction); case IrInstructionIdFloatToInt: @@ -20601,6 +20712,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdIntToFloat: case IrInstructionIdFloatToInt: case IrInstructionIdBoolToInt: + case IrInstructionIdFromBytes: + case IrInstructionIdToBytes: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 2667c246a5..1b35ecf57f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -672,6 +672,20 @@ static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruc fprintf(irp->f, ")"); } +static void ir_print_from_bytes(IrPrint *irp, IrInstructionFromBytes *instruction) { + fprintf(irp->f, "@bytesToSlice("); + ir_print_other_instruction(irp, instruction->dest_child_type); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_to_bytes(IrPrint *irp, IrInstructionToBytes *instruction) { + fprintf(irp->f, "@sliceToBytes("); + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) { fprintf(irp->f, "@intToFloat("); ir_print_other_instruction(irp, instruction->dest_type); @@ -1472,6 +1486,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdErrSetCast: ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction); break; + case IrInstructionIdFromBytes: + ir_print_from_bytes(irp, (IrInstructionFromBytes *)instruction); + break; + case IrInstructionIdToBytes: + ir_print_to_bytes(irp, (IrInstructionToBytes *)instruction); + break; case IrInstructionIdIntToFloat: ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction); break; diff --git a/std/heap.zig b/std/heap.zig index 2a2c8c0b59..c948818e3d 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -221,7 +221,7 @@ pub const ArenaAllocator = struct { if (len >= actual_min_size) break; } const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); - const buf_node_slice = ([]BufNode)(buf[0..@sizeOf(BufNode)]); + const buf_node_slice = @bytesToSlice(BufNode, buf[0..@sizeOf(BufNode)]); const buf_node = &buf_node_slice[0]; buf_node.* = BufNode{ .data = buf, diff --git a/std/macho.zig b/std/macho.zig index 64f78ae4a3..fe5409ad4d 100644 --- a/std/macho.zig +++ b/std/macho.zig @@ -161,7 +161,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable } fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void { - return in.stream.readNoEof(([]u8)(result)); + return in.stream.readNoEof(@sliceToBytes(result)); } fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void { return readNoEof(in, T, (*[1]T)(result)[0..]); diff --git a/std/mem.zig b/std/mem.zig index b02589b0dd..55844b88db 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -70,7 +70,7 @@ pub const Allocator = struct { for (byte_slice) |*byte| { byte.* = undefined; } - return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); + return @bytesToSlice(T, @alignCast(alignment, byte_slice)); } pub fn realloc(self: *Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { @@ -86,7 +86,7 @@ pub const Allocator = struct { return ([*]align(alignment) T)(undefined)[0..0]; } - const old_byte_slice = ([]u8)(old_mem); + const old_byte_slice = @sliceToBytes(old_mem); const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); assert(byte_slice.len == byte_count); @@ -96,7 +96,7 @@ pub const Allocator = struct { byte.* = undefined; } } - return ([]T)(@alignCast(alignment, byte_slice)); + return @bytesToSlice(T, @alignCast(alignment, byte_slice)); } /// Reallocate, but `n` must be less than or equal to `old_mem.len`. @@ -118,13 +118,13 @@ pub const Allocator = struct { // n <= old_mem.len and the multiplication didn't overflow for that operation. const byte_count = @sizeOf(T) * n; - const byte_slice = self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment) catch unreachable; + const byte_slice = self.reallocFn(self, @sliceToBytes(old_mem), byte_count, alignment) catch unreachable; assert(byte_slice.len == byte_count); - return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); + return @bytesToSlice(T, @alignCast(alignment, byte_slice)); } pub fn free(self: *Allocator, memory: var) void { - const bytes = ([]const u8)(memory); + const bytes = @sliceToBytes(memory); if (bytes.len == 0) return; const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); self.freeFn(self, non_const_ptr[0..bytes.len]); diff --git a/std/net.zig b/std/net.zig index f21611ff91..8c1aeb92d7 100644 --- a/std/net.zig +++ b/std/net.zig @@ -68,7 +68,7 @@ pub const Address = struct { pub fn parseIp4(buf: []const u8) !u32 { var result: u32 = undefined; - const out_ptr = ([]u8)((*[1]u32)(&result)[0..]); + const out_ptr = @sliceToBytes((*[1]u32)(&result)[0..]); var x: u8 = 0; var index: u8 = 0; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index cb4788ba17..45b205451d 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -79,7 +79,7 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)]; - const name_wide = ([]u16)(name_bytes); + const name_wide = @bytesToSlice(u16, name_bytes); return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null; } diff --git a/test/cases/align.zig b/test/cases/align.zig index 682c185e86..64f0788efc 100644 --- a/test/cases/align.zig +++ b/test/cases/align.zig @@ -90,7 +90,7 @@ fn testBytesAlignSlice(b: u8) void { b, b, }; - const slice = ([]u32)(bytes[0..]); + const slice: []u32 = @bytesToSlice(u32, bytes[0..]); assert(slice[0] == 0x33333333); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index f1e49c6d1f..0b79f0df48 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -372,7 +372,7 @@ test "const slice widen cast" { 0x12, }; - const u32_value = ([]const u32)(bytes[0..])[0]; + const u32_value = @bytesToSlice(u32, bytes[0..])[0]; assert(u32_value == 0x12121212); assert(@bitCast(u32, bytes) == 0x12121212); @@ -420,3 +420,9 @@ test "comptime_int @intToFloat" { assert(@typeOf(result) == f32); assert(result == 1234.0); } + +test "@bytesToSlice keeps pointer alignment" { + var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; + const numbers = @bytesToSlice(u32, bytes[0..]); + comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); +} diff --git a/test/cases/misc.zig b/test/cases/misc.zig index beb0d6d456..89c441e7f9 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -422,14 +422,14 @@ test "cast slice to u8 slice" { 4, }; const big_thing_slice: []i32 = big_thing_array[0..]; - const bytes = ([]u8)(big_thing_slice); + const bytes = @sliceToBytes(big_thing_slice); assert(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; assert(big_thing_slice[1] == 0); - const big_thing_again = ([]align(1) i32)(bytes); + const big_thing_again = @bytesToSlice(i32, bytes); assert(big_thing_again[2] == 3); big_thing_again[2] = -1; assert(bytes[8] == @maxValue(u8)); diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 94a2ba6336..2941ecb56a 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -302,7 +302,7 @@ test "packed array 24bits" { var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; - const ptr = &([]FooArray24Bits)(bytes[0 .. bytes.len - 1])[0]; + const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; assert(ptr.a == 0); assert(ptr.b[0].field == 0); assert(ptr.b[1].field == 0); @@ -351,7 +351,7 @@ test "aligned array of packed struct" { } var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); - const ptr = &([]FooArrayOfAligned)(bytes[0..bytes.len])[0]; + const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; assert(ptr.a[0].a == 0xbb); assert(ptr.a[0].b == 0xbb); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 8c5abaaccc..2f4a22553b 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -404,10 +404,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const Set2 = error {A, C}; \\comptime { \\ var x = Set1.B; - \\ var y = Set2(x); + \\ var y = @errSetCast(Set2, x); \\} , - ".tmp_source.zig:5:17: error: error.B not a member of error set 'Set2'", + ".tmp_source.zig:5:13: error: error.B not a member of error set 'Set2'", ); cases.add( @@ -2086,10 +2086,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "convert fixed size array to slice with invalid size", \\export fn f() void { \\ var array: [5]u8 = undefined; - \\ var foo = ([]const u32)(array)[0]; + \\ var foo = @bytesToSlice(u32, array)[0]; \\} , - ".tmp_source.zig:3:28: error: unable to convert [5]u8 to []const u32: size mismatch", + ".tmp_source.zig:3:15: error: unable to convert [5]u8 to []align(1) const u32: size mismatch", + ".tmp_source.zig:3:29: note: u32 has size 4; remaining bytes: 1", ); cases.add( @@ -3239,18 +3240,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:26: note: '*u32' has alignment 4", ); - cases.add( - "increase pointer alignment in slice resize", - \\export fn entry() u32 { - \\ var bytes = []u8{0x01, 0x02, 0x03, 0x04}; - \\ return ([]u32)(bytes[0..])[0]; - \\} - , - ".tmp_source.zig:3:19: error: cast increases pointer alignment", - ".tmp_source.zig:3:19: note: '[]u8' has alignment 1", - ".tmp_source.zig:3:19: note: '[]u32' has alignment 4", - ); - cases.add( "@alignCast expects pointer or slice", \\export fn entry() void { -- cgit v1.2.3 From a430853a48a5e4dbcd0094c632957e28898159f3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 17:43:01 -0400 Subject: standard library fixes --- src/ir.cpp | 2 +- std/cstr.zig | 2 +- std/os/index.zig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index c5ca12af1e..3ed9f66947 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10215,7 +10215,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to &const []const N + // explicit cast from [N]T to &const []const T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && diff --git a/std/cstr.zig b/std/cstr.zig index d9106769c1..e83d5a39e9 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -79,7 +79,7 @@ pub const NullTerminated2DArray = struct { errdefer allocator.free(buf); var write_index = index_size; - const index_buf = ([]?[*]u8)(buf); + const index_buf = @bytesToSlice(?[*]u8, buf); var i: usize = 0; for (slices) |slice| { diff --git a/std/os/index.zig b/std/os/index.zig index f1c3ab2128..dd0d4e2ea1 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1805,7 +1805,7 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); errdefer allocator.free(buf); - const result_slice_list = ([][]u8)(buf[0..slice_list_bytes]); + const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); const result_contents = buf[slice_list_bytes..]; mem.copy(u8, result_contents, contents_slice); -- cgit v1.2.3 From 626b73e8beeaae1cab23f883f877d89d64bbfa39 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Jun 2018 18:48:29 -0400 Subject: remove error to/from int casting syntax; add `@errorToInt`/`@intToError` See #1061 --- doc/langref.html.in | 48 +++++++++++++++++++++++++++---- src/all_types.hpp | 2 ++ src/codegen.cpp | 2 ++ src/ir.cpp | 74 +++++++++++++++++++++++++++++++++++++----------- std/os/child_process.zig | 4 +-- test/cases/cast.zig | 4 +-- test/cases/error.zig | 10 +++---- test/cases/type_info.zig | 2 +- test/compile_errors.zig | 38 ++++++++++++------------- test/runtime_safety.zig | 14 ++++----- 10 files changed, 138 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 9a3d5e5f17..24bf6e1b16 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4658,7 +4658,7 @@ comptime {

      Attempting to convert a number of bytes with a length that does not evenly divide into a slice of - elements results in {#link|Undefined Behavior#}. + elements results in safety-protected {#link|Undefined Behavior#}.

      {#header_close#} @@ -4935,7 +4935,7 @@ test "main" {
      @errSetCast(comptime T: DestType, value: var) DestType

      Converts an error value from one error set to another error set. Attempting to convert an error - which is not in the destination error set results in {#link|Undefined Behavior#}. + which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.

      {#header_close#} @@ -4955,6 +4955,7 @@ test "main" { error name table will be generated.

      {#header_close#} + {#header_open|@errorReturnTrace#}
      @errorReturnTrace() ?*builtin.StackTrace

      @@ -4964,6 +4965,25 @@ test "main" {

      {#header_close#} + {#header_open|@errorToInt#} +
      @errorToInt(err: var) @IntType(false, @sizeOf(error) * 8)
      +

      + Supports the following types: +

      +
        +
      • error unions
      • +
      • E!void
      • +
      +

      + Converts an error to the integer representation of an error. +

      +

      + It is generally recommended to avoid this + cast, as the integer representation of an error is not stable across source code changes. +

      + {#see_also|@intToError#} + {#header_close#} + {#header_open|@export#}
      @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8

      @@ -5071,8 +5091,24 @@ fn add(a: i32, b: i32) i32 { return a + b; }

      Converts an integer to another integer while keeping the same numerical value. Attempting to convert a number which is out of range of the destination type results in - {#link|Undefined Behavior#}. + safety-protected {#link|Undefined Behavior#}. +

      + {#header_close#} + + {#header_open|@intToError#} +
      @intToError(value: @IntType(false, @sizeOf(error) * 8)) error
      +

      + Converts from the integer representation of an error into the global error set type. +

      +

      + It is generally recommended to avoid this + cast, as the integer representation of an error is not stable across source code changes. +

      +

      + Attempting to convert an integer that does not correspond to any error results in + safety-protected {#link|Undefined Behavior#}.

      + {#see_also|@errorToInt#} {#header_close#} {#header_open|@intToFloat#} @@ -6123,8 +6159,8 @@ fn getNumberOrFail() !i32 { {#code_begin|test_err|integer value 11 represents no error#} comptime { const err = error.AnError; - const number = u32(err) + 10; - const invalid_err = error(number); + const number = @errorToInt(err) + 10; + const invalid_err = @intToError(number); } {#code_end#}

      At runtime crashes with the message invalid error code and a stack trace.

      @@ -6831,7 +6867,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index d8432232c2..e1a4ed7510 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1376,6 +1376,8 @@ enum BuiltinFnId { BuiltinFnIdIntToFloat, BuiltinFnIdFloatToInt, BuiltinFnIdBoolToInt, + BuiltinFnIdErrToInt, + BuiltinFnIdIntToErr, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, diff --git a/src/codegen.cpp b/src/codegen.cpp index 1bc9a17804..c9b4ade4c6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6323,6 +6323,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); + create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1); + create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 3ed9f66947..350c80017e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4167,6 +4167,26 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, result, lval); } + case BuiltinFnIdErrToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdIntToErr: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } case BuiltinFnIdBoolToInt: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -10465,21 +10485,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from T!void to integer type which can fit it - bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && - !type_has_bits(actual_type->data.error_union.payload_type); - bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet; - if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) { - return ir_analyze_err_to_int(ira, source_instr, value, wanted_type); - } - - // explicit cast from integer to error set - if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt && - !actual_type->data.integral.is_signed) - { - return ir_analyze_int_to_err(ira, source_instr, value, wanted_type); - } - // explicit cast from integer to enum type with no payload if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) { return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); @@ -17785,6 +17790,39 @@ static TypeTableEntry *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrIns return dest_type; } +static TypeTableEntry *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_target; + if (target->value.type->id == TypeTableEntryIdErrorSet) { + casted_target = target; + } else { + casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + } + + IrInstruction *result = ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) { IrInstruction *target = instruction->target->other; if (type_is_invalid(target->value.type)) @@ -20229,8 +20267,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: case IrInstructionIdIntToEnum: - case IrInstructionIdIntToErr: - case IrInstructionIdErrToInt: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: @@ -20491,6 +20527,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); case IrInstructionIdSqrt: return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); + case IrInstructionIdIntToErr: + return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); + case IrInstructionIdErrToInt: + return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction); } zig_unreachable(); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 3a0fa7f461..da5e708555 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -318,7 +318,7 @@ pub const ChildProcess = struct { // Here we potentially return the fork child's error // from the parent pid. if (err_int != @maxValue(ErrInt)) { - return SpawnError(err_int); + return @errSetCast(SpawnError, @intToError(err_int)); } return statusToTerm(status); @@ -756,7 +756,7 @@ fn destroyPipe(pipe: *const [2]i32) void { // Child of fork calls this to report an error to the fork parent. // Then the child exits. fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { - _ = writeIntFd(fd, ErrInt(err)); + _ = writeIntFd(fd, ErrInt(@errorToInt(err))); posix.exit(1); } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 0b79f0df48..156835bcb3 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -140,8 +140,8 @@ test "explicit cast from integer to error type" { comptime testCastIntToErr(error.ItBroke); } fn testCastIntToErr(err: error) void { - const x = usize(err); - const y = error(x); + const x = @errorToInt(err); + const y = @intToError(x); assert(error.ItBroke == y); } diff --git a/test/cases/error.zig b/test/cases/error.zig index 95890d8384..45971fd40d 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -31,8 +31,8 @@ test "@errorName" { } test "error values" { - const a = i32(error.err1); - const b = i32(error.err2); + const a = @errorToInt(error.err1); + const b = @errorToInt(error.err2); assert(a != b); } @@ -147,14 +147,14 @@ test "syntax: optional operator in front of error union operator" { } test "comptime err to int of error set with only 1 possible value" { - testErrToIntWithOnePossibleValue(error.A, u32(error.A)); - comptime testErrToIntWithOnePossibleValue(error.A, u32(error.A)); + testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); + comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); } fn testErrToIntWithOnePossibleValue( x: error{A}, comptime value: u32, ) void { - if (u32(x) != value) { + if (@errorToInt(x) != value) { @compileError("bad"); } } diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 1bc58b14e1..46fb3852f3 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -130,7 +130,7 @@ fn testErrorSet() void { assert(TypeId(error_set_info) == TypeId.ErrorSet); assert(error_set_info.ErrorSet.errors.len == 3); assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third)); + assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); const error_union_info = @typeInfo(TestErrorSet!usize); assert(TypeId(error_union_info) == TypeId.ErrorUnion); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2f4a22553b..866b303082 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -467,25 +467,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "int to err global invalid number", - \\const Set1 = error{A, B}; + \\const Set1 = error{ + \\ A, + \\ B, + \\}; \\comptime { - \\ var x: usize = 3; - \\ var y = error(x); + \\ var x: u16 = 3; + \\ var y = @intToError(x); \\} , - ".tmp_source.zig:4:18: error: integer value 3 represents no error", + ".tmp_source.zig:7:13: error: integer value 3 represents no error", ); cases.add( "int to err non global invalid number", - \\const Set1 = error{A, B}; - \\const Set2 = error{A, C}; + \\const Set1 = error{ + \\ A, + \\ B, + \\}; + \\const Set2 = error{ + \\ A, + \\ C, + \\}; \\comptime { - \\ var x = usize(Set1.B); - \\ var y = Set2(x); + \\ var x = @errorToInt(Set1.B); + \\ var y = @errSetCast(Set2, @intToError(x)); \\} , - ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'", + ".tmp_source.zig:11:13: error: error.B not a member of error set 'Set2'", ); cases.add( @@ -2612,17 +2621,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", ); - cases.add( - "too many error values to cast to small integer", - \\const Error = error { A, B, C, D, E, F, G, H }; - \\fn foo(e: Error) u2 { - \\ return u2(e); - \\} - \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , - ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'", - ); - cases.add( "asm at compile time", \\comptime { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 96384066e5..ea5beafe8d 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -175,7 +175,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ if (x.len == 0) return error.Whatever; \\} \\fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { - \\ return ([]align(1) const i32)(slice); + \\ return @bytesToSlice(i32, slice); \\} ); @@ -227,12 +227,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub fn main() void { \\ _ = bar(9999); \\} - \\fn bar(x: u32) error { - \\ return error(x); + \\fn bar(x: u16) error { + \\ return @intToError(x); \\} ); - cases.addRuntimeSafety("cast integer to non-global error set and no match", + cases.addRuntimeSafety("@errSetCast error not present in destination", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} @@ -242,7 +242,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ _ = foo(Set1.B); \\} \\fn foo(set1: Set1) Set2 { - \\ return Set2(set1); + \\ return @errSetCast(Set2, set1); \\} ); @@ -252,12 +252,12 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\pub fn main() !void { \\ var array align(4) = []u32{0x11111111, 0x11111111}; - \\ const bytes = ([]u8)(array[0..]); + \\ const bytes = @sliceToBytes(array[0..]); \\ if (foo(bytes) != 0x11111111) return error.Wrong; \\} \\fn foo(bytes: []u8) u32 { \\ const slice4 = bytes[1..5]; - \\ const int_slice = ([]u32)(@alignCast(4, slice4)); + \\ const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); \\ return int_slice[0]; \\} ); -- cgit v1.2.3 From a3ddd0826bd9c799768c0c707de72c21befa742a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 03:50:38 -0400 Subject: remove enum to/from int casting syntax; add `@enumToInt`/`@intToEnum` see #1061 --- doc/langref.html.in | 34 +++++++++++--- src/all_types.hpp | 10 ++++ src/codegen.cpp | 3 ++ src/ir.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++------ src/ir_print.cpp | 14 ++++++ std/json.zig | 2 +- test/cases/enum.zig | 16 +++---- test/cases/union.zig | 4 +- test/compile_errors.zig | 24 ++-------- 9 files changed, 174 insertions(+), 51 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 24bf6e1b16..19f023f6e1 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1900,9 +1900,9 @@ const Value = enum(u2) { // Now you can cast between u2 and Value. // The ordinal value starts from 0, counting up for each member. test "enum ordinal value" { - assert(u2(Value.Zero) == 0); - assert(u2(Value.One) == 1); - assert(u2(Value.Two) == 2); + assert(@enumToInt(Value.Zero) == 0); + assert(@enumToInt(Value.One) == 1); + assert(@enumToInt(Value.Two) == 2); } // You can override the ordinal value for an enum. @@ -1912,9 +1912,9 @@ const Value2 = enum(u32) { Million = 1000000, }; test "set enum ordinal value" { - assert(u32(Value2.Hundred) == 100); - assert(u32(Value2.Thousand) == 1000); - assert(u32(Value2.Million) == 1000000); + assert(@enumToInt(Value2.Hundred) == 100); + assert(@enumToInt(Value2.Thousand) == 1000); + assert(@enumToInt(Value2.Million) == 1000000); } // Enums can have methods, the same as structs and unions. @@ -4931,6 +4931,14 @@ test "main" { {#see_also|@import#} {#header_close#} + {#header_open|@enumToInt#} +
      @enumToInt(enum_value: var) var
      +

      + Converts an enumeration value into its integer tag type. +

      + {#see_also|@intToEnum#} + {#header_close#} + {#header_open|@errSetCast#}
      @errSetCast(comptime T: DestType, value: var) DestType

      @@ -5095,6 +5103,18 @@ fn add(a: i32, b: i32) i32 { return a + b; }

      {#header_close#} + {#header_open|@intToEnum#} +
      @intToEnum(comptime DestType: type, int_value: @TagType(DestType)) DestType
      +

      + Converts an integer into an {#link|enum#} value. +

      +

      + Attempting to convert an integer which represents no value in the chosen enum type invokes + safety-checked {#link|Undefined Behavior#}. +

      + {#see_also|@enumToInt#} + {#header_close#} + {#header_open|@intToError#}
      @intToError(value: @IntType(false, @sizeOf(error) * 8)) error

      @@ -6867,7 +6887,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index e1a4ed7510..41c9fe18c3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1378,6 +1378,8 @@ enum BuiltinFnId { BuiltinFnIdBoolToInt, BuiltinFnIdErrToInt, BuiltinFnIdIntToErr, + BuiltinFnIdEnumToInt, + BuiltinFnIdIntToEnum, BuiltinFnIdIntType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, @@ -2092,6 +2094,7 @@ enum IrInstructionId { IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, IrInstructionIdIntToEnum, + IrInstructionIdEnumToInt, IrInstructionIdIntToErr, IrInstructionIdErrToInt, IrInstructionIdCheckSwitchProngs, @@ -2905,6 +2908,13 @@ struct IrInstructionIntToPtr { struct IrInstructionIntToEnum { IrInstruction base; + IrInstruction *dest_type; + IrInstruction *target; +}; + +struct IrInstructionEnumToInt { + IrInstruction base; + IrInstruction *target; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index c9b4ade4c6..21520e0dd0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4730,6 +4730,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdErrSetCast: case IrInstructionIdFromBytes: case IrInstructionIdToBytes: + case IrInstructionIdEnumToInt: zig_unreachable(); case IrInstructionIdReturn: @@ -6325,6 +6326,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1); create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1); + create_builtin_fn(g, BuiltinFnIdEnumToInt, "enumToInt", 1); + create_builtin_fn(g, BuiltinFnIdIntToEnum, "intToEnum", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int diff --git a/src/ir.cpp b/src/ir.cpp index 350c80017e..a8599e7aae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -600,6 +600,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) { return IrInstructionIdIntToEnum; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumToInt *) { + return IrInstructionIdEnumToInt; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) { return IrInstructionIdIntToErr; } @@ -2378,10 +2382,26 @@ static IrInstruction *ir_build_ptr_to_int(IrBuilder *irb, Scope *scope, AstNode } static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *target) + IrInstruction *dest_type, IrInstruction *target) { IrInstructionIntToEnum *instruction = ir_build_instruction( irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + + + +static IrInstruction *ir_build_enum_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *target) +{ + IrInstructionEnumToInt *instruction = ir_build_instruction( + irb, scope, source_node); instruction->target = target; ir_ref_instruction(target, irb->current_basic_block); @@ -4708,6 +4728,31 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo // this value does not mean anything since we passed non-null values for other arg AtomicOrderMonotonic); } + case BuiltinFnIdIntToEnum: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval); + } + case BuiltinFnIdEnumToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval); + } } zig_unreachable(); } @@ -9951,7 +9996,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour } IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope, - source_instr->source_node, target); + source_instr->source_node, nullptr, target); result->value.type = wanted_type; return result; } @@ -10485,16 +10530,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from integer to enum type with no payload - if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdEnum) { - return ir_analyze_int_to_enum(ira, source_instr, value, wanted_type); - } - - // explicit cast from enum type with no payload to integer - if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdEnum) { - return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type); - } - // explicit cast from union to the enum type of the union if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { type_ensure_zero_bits_known(ira->codegen, actual_type); @@ -20262,11 +20297,63 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (target->value.type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, instruction->target, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + type_ensure_zero_bits_known(ira->codegen, target->value.type); + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *tag_type = target->value.type->data.enumeration.tag_int_type; + + IrInstruction *result = ir_analyze_enum_to_int(ira, &instruction->base, target, tag_type); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + +static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { + IrInstruction *dest_type_value = instruction->dest_type->other; + TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (dest_type->id != TypeTableEntryIdEnum) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + type_ensure_zero_bits_known(ira->codegen, dest_type); + if (type_is_invalid(dest_type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *tag_type = dest_type->data.enumeration.tag_int_type; + + IrInstruction *target = instruction->target->other; + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *casted_target = ir_implicit_cast(ira, target, tag_type); + if (type_is_invalid(casted_target->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *result = ir_analyze_int_to_enum(ira, &instruction->base, casted_target, dest_type); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdWidenOrShorten: - case IrInstructionIdIntToEnum: case IrInstructionIdStructInit: case IrInstructionIdUnionInit: case IrInstructionIdStructFieldPtr: @@ -20531,6 +20618,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); case IrInstructionIdErrToInt: return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction); + case IrInstructionIdIntToEnum: + return ir_analyze_instruction_int_to_enum(ira, (IrInstructionIntToEnum *)instruction); + case IrInstructionIdEnumToInt: + return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); } zig_unreachable(); } @@ -20754,6 +20845,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBoolToInt: case IrInstructionIdFromBytes: case IrInstructionIdToBytes: + case IrInstructionIdEnumToInt: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 1b35ecf57f..5e5a71382c 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -928,6 +928,17 @@ static void ir_print_int_to_ptr(IrPrint *irp, IrInstructionIntToPtr *instruction static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instruction) { fprintf(irp->f, "@intToEnum("); + if (instruction->dest_type == nullptr) { + fprintf(irp->f, "(null)"); + } else { + ir_print_other_instruction(irp, instruction->dest_type); + } + ir_print_other_instruction(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_enum_to_int(IrPrint *irp, IrInstructionEnumToInt *instruction) { + fprintf(irp->f, "@enumToInt("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } @@ -1717,6 +1728,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAtomicLoad: ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); break; + case IrInstructionIdEnumToInt: + ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/std/json.zig b/std/json.zig index 2930cd21bb..8986034fb4 100644 --- a/std/json.zig +++ b/std/json.zig @@ -180,7 +180,7 @@ pub const StreamingParser = struct { pub fn fromInt(x: var) State { debug.assert(x == 0 or x == 1); const T = @TagType(State); - return State(@intCast(T, x)); + return @intToEnum(State, @intCast(T, x)); } }; diff --git a/test/cases/enum.zig b/test/cases/enum.zig index 6a02a47784..50edfda536 100644 --- a/test/cases/enum.zig +++ b/test/cases/enum.zig @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(u3(n) == expected); + assert(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(IntToEnumNumber(@intCast(u3, x)) == IntToEnumNumber.Three); + assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(u2(value) == 1); + assert(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,7 +784,7 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(u32(x) == 60); + assert(@enumToInt(x) == 60); assert(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, @@ -811,7 +811,7 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(u32(x) == 1000); + assert(@enumToInt(x) == 1000); assert(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1); - assert(MultipleChoice2(40) == MultipleChoice2.B); + assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,7 +865,7 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(u4(EnumWithTagValues.C) == 0b0100); + assert(@enumToInt(EnumWithTagValues.C) == 0b0100); } test "enum with 1 field but explicit tag type should still have the tag type" { diff --git a/test/cases/union.zig b/test/cases/union.zig index bdcbbdb452..78b2dc8dd7 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -126,7 +126,7 @@ const MultipleChoice = union(enum(u32)) { test "simple union(enum(u32))" { var x = MultipleChoice.C; assert(x == MultipleChoice.C); - assert(u32(@TagType(MultipleChoice)(x)) == 60); + assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -148,7 +148,7 @@ test "union(enum(u32)) with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: *const MultipleChoice2) void { - assert(u32(@TagType(MultipleChoice2)(x.*)) == 60); + assert(@enumToInt(@TagType(MultipleChoice2)(x.*)) == 60); assert(1123 == switch (x.*) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 866b303082..609e3f103f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3709,22 +3709,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", ); - cases.add( - "explicitly casting enum to non tag type", - \\const Small = enum(u2) { - \\ One, - \\ Two, - \\ Three, - \\ Four, - \\}; - \\ - \\export fn entry() void { - \\ var x = u3(Small.Two); - \\} - , - ".tmp_source.zig:9:15: error: enum to integer cast to 'u3' instead of its tag type, 'u2'", - ); - cases.add( "explicitly casting non tag type to enum", \\const Small = enum(u2) { @@ -3736,10 +3720,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() void { \\ var y = u3(3); - \\ var x = Small(y); + \\ var x = @intToEnum(Small, y); \\} , - ".tmp_source.zig:10:18: error: integer to enum cast from 'u3' instead of its tag type, 'u2'", + ".tmp_source.zig:10:31: error: expected type 'u2', found 'u3'", ); cases.add( @@ -4020,10 +4004,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ B = 11, \\}; \\export fn entry() void { - \\ var x = Foo(0); + \\ var x = @intToEnum(Foo, 0); \\} , - ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0", + ".tmp_source.zig:6:13: error: enum 'Foo' has no tag matching integer value 0", ".tmp_source.zig:1:13: note: 'Foo' declared here", ); -- cgit v1.2.3 From 13923132363b26b9982951fd79f01dbab0046e81 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Tue, 19 Jun 2018 17:45:19 +0300 Subject: @typeInfo now uses optional types instead of @typeOf(undefined) --- doc/langref.html.in | 8 +++--- src/codegen.cpp | 10 +++---- src/ir.cpp | 75 +++++++++++++++++++++++++++++++----------------- test/cases/type_info.zig | 16 +++++------ 4 files changed, 65 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index f1ae2bafaa..551f3ff769 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5684,20 +5684,20 @@ pub const TypeInfo = union(TypeId) { pub const FnArg = struct { is_generic: bool, is_noalias: bool, - arg_type: type, + arg_type: ?type, }; pub const Fn = struct { calling_convention: CallingConvention, is_generic: bool, is_var_args: bool, - return_type: type, - async_allocator_type: type, + return_type: ?type, + async_allocator_type: ?type, args: []FnArg, }; pub const Promise = struct { - child: type, + child: ?type, }; pub const Definition = struct { diff --git a/src/codegen.cpp b/src/codegen.cpp index 84335d4e06..dcb5e8cb46 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6627,7 +6627,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { "\n" " pub const Union = struct {\n" " layout: ContainerLayout,\n" - " tag_type: type,\n" + " tag_type: ?type,\n" " fields: []UnionField,\n" " defs: []Definition,\n" " };\n" @@ -6644,20 +6644,20 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " pub const FnArg = struct {\n" " is_generic: bool,\n" " is_noalias: bool,\n" - " arg_type: type,\n" + " arg_type: ?type,\n" " };\n" "\n" " pub const Fn = struct {\n" " calling_convention: CallingConvention,\n" " is_generic: bool,\n" " is_var_args: bool,\n" - " return_type: type,\n" - " async_allocator_type: type,\n" + " return_type: ?type,\n" + " async_allocator_type: ?type,\n" " args: []FnArg,\n" " };\n" "\n" " pub const Promise = struct {\n" - " child: type,\n" + " child: ?type,\n" " };\n" "\n" " pub const Definition = struct {\n" diff --git a/src/ir.cpp b/src/ir.cpp index c75a3ae7c1..433637513e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16722,16 +16722,20 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *fields = create_const_vals(1); result->data.x_struct.fields = fields; - // @TODO ?type instead of using @typeOf(undefined) when we have no type. - // child: type + // child: ?type ensure_field_index(result->type, "child", 0); fields[0].special = ConstValSpecialStatic; - fields[0].type = ira->codegen->builtin_types.entry_type; + fields[0].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.promise.result_type == nullptr) - fields[0].data.x_type = ira->codegen->builtin_types.entry_undef; - else - fields[0].data.x_type = type_entry->data.promise.result_type; + fields[0].data.x_optional = nullptr; + else { + ConstExprValue *child_type = create_const_vals(1); + child_type->special = ConstValSpecialStatic; + child_type->type = ira->codegen->builtin_types.entry_type; + child_type->data.x_type = type_entry->data.promise.result_type; + fields[0].data.x_optional = child_type; + } break; } @@ -16872,19 +16876,23 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fields[0].special = ConstValSpecialStatic; fields[0].type = ir_type_info_get_type(ira, "ContainerLayout"); bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout); - // tag_type: type + // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); fields[1].special = ConstValSpecialStatic; - fields[1].type = ira->codegen->builtin_types.entry_type; - // @TODO ?type instead of using @typeOf(undefined) when we have no type. + fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || union_decl_node->data.container_decl.init_arg_expr != nullptr) { - fields[1].data.x_type = type_entry->data.unionation.tag_type; + ConstExprValue *tag_type = create_const_vals(1); + tag_type->special = ConstValSpecialStatic; + tag_type->type = ira->codegen->builtin_types.entry_type; + tag_type->data.x_type = type_entry->data.unionation.tag_type; + fields[1].data.x_optional = tag_type; } else - fields[1].data.x_type = ira->codegen->builtin_types.entry_undef; + fields[1].data.x_optional = nullptr; // fields: []TypeInfo.UnionField ensure_field_index(result->type, "fields", 2); @@ -16913,7 +16921,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].special = ConstValSpecialStatic; inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); - if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) { + if (fields[1].data.x_optional == nullptr) { inner_fields[1].data.x_optional = nullptr; } else { inner_fields[1].data.x_optional = create_const_vals(1); @@ -17022,8 +17030,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *fields = create_const_vals(6); result->data.x_struct.fields = fields; - // @TODO Fix type = undefined with ?type - // calling_convention: TypeInfo.CallingConvention ensure_field_index(result->type, "calling_convention", 0); fields[0].special = ConstValSpecialStatic; @@ -17041,22 +17047,32 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t fields[2].special = ConstValSpecialStatic; fields[2].type = ira->codegen->builtin_types.entry_bool; fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; - // return_type: type + // return_type: ?type ensure_field_index(result->type, "return_type", 3); fields[3].special = ConstValSpecialStatic; - fields[3].type = ira->codegen->builtin_types.entry_type; + fields[3].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) - fields[3].data.x_type = ira->codegen->builtin_types.entry_undef; - else - fields[3].data.x_type = type_entry->data.fn.fn_type_id.return_type; + fields[3].data.x_optional = nullptr; + else { + ConstExprValue *return_type = create_const_vals(1); + return_type->special = ConstValSpecialStatic; + return_type->type = ira->codegen->builtin_types.entry_type; + return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; + fields[3].data.x_optional = return_type; + } // async_allocator_type: type ensure_field_index(result->type, "async_allocator_type", 4); fields[4].special = ConstValSpecialStatic; - fields[4].type = ira->codegen->builtin_types.entry_type; + fields[4].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) - fields[4].data.x_type = ira->codegen->builtin_types.entry_undef; - else - fields[4].data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; + fields[4].data.x_optional = nullptr; + else { + ConstExprValue *async_alloc_type = create_const_vals(1); + async_alloc_type->special = ConstValSpecialStatic; + async_alloc_type->type = ira->codegen->builtin_types.entry_type; + async_alloc_type->data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type; + fields[4].data.x_optional = async_alloc_type; + } // args: []TypeInfo.FnArg TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg"); size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - @@ -17090,12 +17106,17 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = ira->codegen->builtin_types.entry_bool; inner_fields[1].data.x_bool = fn_param_info->is_noalias; inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = ira->codegen->builtin_types.entry_type; + inner_fields[2].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) - inner_fields[2].data.x_type = ira->codegen->builtin_types.entry_undef; - else - inner_fields[2].data.x_type = fn_param_info->type; + inner_fields[2].data.x_optional = nullptr; + else { + ConstExprValue *arg_type = create_const_vals(1); + arg_type->special = ConstValSpecialStatic; + arg_type->type = ira->codegen->builtin_types.entry_type; + arg_type->data.x_type = fn_param_info->type; + inner_fields[2].data.x_optional = arg_type; + } fn_arg_val->data.x_struct.fields = inner_fields; fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig index 1bc58b14e1..ab4f3678e8 100644 --- a/test/cases/type_info.zig +++ b/test/cases/type_info.zig @@ -107,11 +107,11 @@ test "type info: promise info" { fn testPromise() void { const null_promise_info = @typeInfo(promise); assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == @typeOf(undefined)); + assert(null_promise_info.Promise.child == null); const promise_info = @typeInfo(promise->usize); assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child == usize); + assert(promise_info.Promise.child.? == usize); } test "type info: error set, error union info" { @@ -165,7 +165,7 @@ fn testUnion() void { const typeinfo_info = @typeInfo(TypeInfo); assert(TypeId(typeinfo_info) == TypeId.Union); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type == TypeId); + assert(typeinfo_info.Union.tag_type.? == TypeId); assert(typeinfo_info.Union.fields.len == 25); assert(typeinfo_info.Union.fields[4].enum_field != null); assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); @@ -179,7 +179,7 @@ fn testUnion() void { const notag_union_info = @typeInfo(TestNoTagUnion); assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == @typeOf(undefined)); + assert(notag_union_info.Union.tag_type == null); assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(notag_union_info.Union.fields.len == 2); assert(notag_union_info.Union.fields[0].enum_field == null); @@ -191,7 +191,7 @@ fn testUnion() void { const extern_union_info = @typeInfo(TestExternUnion); assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == @typeOf(undefined)); + assert(extern_union_info.Union.tag_type == null); assert(extern_union_info.Union.fields[0].enum_field == null); assert(extern_union_info.Union.fields[0].field_type == *c_void); } @@ -238,13 +238,13 @@ fn testFunction() void { assert(fn_info.Fn.is_generic); assert(fn_info.Fn.args.len == 2); assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == @typeOf(undefined)); - assert(fn_info.Fn.async_allocator_type == @typeOf(undefined)); + assert(fn_info.Fn.return_type == null); + assert(fn_info.Fn.async_allocator_type == null); const test_instance: TestStruct = undefined; const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type == *const TestStruct); + assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); } fn foo(comptime a: usize, b: bool, args: ...) usize { -- cgit v1.2.3 From c7804277bf390eeba368e3565b2aff0cf96f86b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 16:06:10 -0400 Subject: `@floatToInt` now has safety-checked undefined behavior when the integer part does not fit in the destination integer type * Also fix incorrect safety triggered for integer casting an `i32` to a `u7`. closes #1138 * adds compiler-rt function: `__floatuntidf` --- CMakeLists.txt | 1 + doc/langref.html.in | 9 +++- src/all_types.hpp | 1 + src/codegen.cpp | 36 +++++++++++-- src/ir.cpp | 25 +++++++-- std/math/expm1.zig | 8 +++ std/special/compiler_rt/floatuntidf.zig | 60 +++++++++++++++++++++ std/special/compiler_rt/floatuntidf_test.zig | 81 ++++++++++++++++++++++++++++ std/special/compiler_rt/index.zig | 2 + test/cases/cast.zig | 21 +++++++- test/compile_errors.zig | 17 ++++++ test/runtime_safety.zig | 39 ++++++++++++++ 12 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 std/special/compiler_rt/floatuntidf.zig create mode 100644 std/special/compiler_rt/floatuntidf_test.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index e502901bd2..030398d71c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -568,6 +568,7 @@ set(ZIG_STD_FILES "special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfti.zig" + "special/compiler_rt/floatuntidf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/index.zig" "special/compiler_rt/udivmod.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index 4070ef0ac0..1bd28f9c3e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5035,8 +5035,12 @@ test "main" {

      @floatToInt(comptime DestType: type, float: var) DestType

      Converts the integer part of a floating point number to the destination type. - To convert the other way, use {#link|@intToFloat#}. This cast is always safe.

      +

      + If the integer part of the floating point number cannot fit in the destination type, + it invokes safety-checked {#link|Undefined Behavior#}. +

      + {#see_also|@intToFloat#} {#header_close#} {#header_open|@frameAddress#} @@ -6207,7 +6211,10 @@ comptime { {#header_close#} {#header_open|Wrong Union Field Access#}

      TODO

      + {#header_close#} + {#header_open|Out of Bounds Float To Integer Cast#} +

      TODO

      {#header_close#} {#header_close#} diff --git a/src/all_types.hpp b/src/all_types.hpp index 41c9fe18c3..12e054cbeb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1434,6 +1434,7 @@ enum PanicMsgId { PanicMsgIdIncorrectAlignment, PanicMsgIdBadUnionField, PanicMsgIdBadEnumValue, + PanicMsgIdFloatToInt, PanicMsgIdCount, }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 5f7fd60392..20268d636a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -865,6 +865,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { return buf_create_from_str("access of inactive union field"); case PanicMsgIdBadEnumValue: return buf_create_from_str("invalid enum value"); + case PanicMsgIdFloatToInt: + return buf_create_from_str("integer part of floating point value out of bounds"); } zig_unreachable(); } @@ -1671,7 +1673,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, T return trunc_val; } LLVMValueRef orig_val; - if (actual_type->data.integral.is_signed) { + if (wanted_type->data.integral.is_signed) { orig_val = LLVMBuildSExt(g->builder, trunc_val, actual_type->type_ref, ""); } else { orig_val = LLVMBuildZExt(g->builder, trunc_val, actual_type->type_ref, ""); @@ -2546,15 +2548,41 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, } else { return LLVMBuildUIToFP(g->builder, expr_val, wanted_type->type_ref, ""); } - case CastOpFloatToInt: + case CastOpFloatToInt: { assert(wanted_type->id == TypeTableEntryIdInt); ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base)); + + bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); + + LLVMValueRef result; if (wanted_type->data.integral.is_signed) { - return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); + result = LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); } else { - return LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); + result = LLVMBuildFPToUI(g->builder, expr_val, wanted_type->type_ref, ""); } + if (want_safety) { + LLVMValueRef back_to_float; + if (wanted_type->data.integral.is_signed) { + back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } else { + back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } + LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); + LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); + LLVMValueRef one_neg = LLVMConstReal(LLVMTypeOf(expr_val), -1.0f); + LLVMValueRef ok_bit_pos = LLVMBuildFCmp(g->builder, LLVMRealOLT, difference, one_pos, ""); + LLVMValueRef ok_bit_neg = LLVMBuildFCmp(g->builder, LLVMRealOGT, difference, one_neg, ""); + LLVMValueRef ok_bit = LLVMBuildAnd(g->builder, ok_bit_pos, ok_bit_neg, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckOk"); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, bad_block); + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdFloatToInt); + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + return result; + } case CastOpBoolToInt: assert(wanted_type->id == TypeTableEntryIdInt); assert(actual_type->id == TypeTableEntryIdBool); diff --git a/src/ir.cpp b/src/ir.cpp index 55da8ad944..1fe078dd3f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9057,7 +9057,8 @@ static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_ } } -static void eval_const_expr_implicit_cast(CastOp cast_op, +static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr, + CastOp cast_op, ConstExprValue *other_val, TypeTableEntry *other_type, ConstExprValue *const_val, TypeTableEntry *new_type) { @@ -9129,6 +9130,20 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, } case CastOpFloatToInt: float_init_bigint(&const_val->data.x_bigint, other_val); + if (new_type->id == TypeTableEntryIdInt) { + if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count, + new_type->data.integral.is_signed)) + { + Buf *int_buf = buf_alloc(); + bigint_append_buf(int_buf, &const_val->data.x_bigint, 10); + + ir_add_error(ira, source_instr, + buf_sprintf("integer value '%s' cannot be stored in type '%s'", + buf_ptr(int_buf), buf_ptr(&new_type->name))); + return false; + } + } + const_val->special = ConstValSpecialStatic; break; case CastOpBoolToInt: @@ -9136,6 +9151,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, const_val->special = ConstValSpecialStatic; break; } + return true; } static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca) @@ -9145,8 +9161,11 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst { IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - eval_const_expr_implicit_cast(cast_op, &value->value, value->value.type, - &result->value, wanted_type); + if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, + &result->value, wanted_type)) + { + return ira->codegen->invalid_instruction; + } return result; } else { IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op); diff --git a/std/math/expm1.zig b/std/math/expm1.zig index 438e44ccce..6fa0194b32 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -20,6 +20,10 @@ pub fn expm1(x: var) @typeOf(x) { fn expm1_32(x_: f32) f32 { @setFloatMode(this, builtin.FloatMode.Strict); + + if (math.isNan(x_)) + return math.nan(f32); + const o_threshold: f32 = 8.8721679688e+01; const ln2_hi: f32 = 6.9313812256e-01; const ln2_lo: f32 = 9.0580006145e-06; @@ -146,6 +150,10 @@ fn expm1_32(x_: f32) f32 { fn expm1_64(x_: f64) f64 { @setFloatMode(this, builtin.FloatMode.Strict); + + if (math.isNan(x_)) + return math.nan(f64); + const o_threshold: f64 = 7.09782712893383973096e+02; const ln2_hi: f64 = 6.93147180369123816490e-01; const ln2_lo: f64 = 1.90821492927058770002e-10; diff --git a/std/special/compiler_rt/floatuntidf.zig b/std/special/compiler_rt/floatuntidf.zig new file mode 100644 index 0000000000..3aabcb7b8a --- /dev/null +++ b/std/special/compiler_rt/floatuntidf.zig @@ -0,0 +1,60 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +const DBL_MANT_DIG = 53; + +pub extern fn __floatuntidf(arg: u128) f64 { + @setRuntimeSafety(is_test); + + if (arg == 0) + return 0.0; + + var a = arg; + const N: u32 = @sizeOf(u128) * 8; + const sd = @bitCast(i32, N -% @clz(a)); // number of significant digits + var e: i32 = sd -% 1; // exponent + if (sd > DBL_MANT_DIG) { + // start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx + // finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR + // 12345678901234567890123456 + // 1 = msb 1 bit + // P = bit DBL_MANT_DIG-1 bits to the right of 1 + // Q = bit DBL_MANT_DIG bits to the right of 1 + // R = "or" of all bits to the right of Q + switch (sd) { + DBL_MANT_DIG + 1 => { + a <<= 1; + }, + DBL_MANT_DIG + 2 => {}, + else => { + const shift_amt = @bitCast(i32, N +% (DBL_MANT_DIG + 2)) -% sd; + const shift_amt_u7 = @intCast(u7, shift_amt); + a = (a >> @intCast(u7, sd -% (DBL_MANT_DIG + 2))) | + @boolToInt((a & (u128(@maxValue(u128)) >> shift_amt_u7)) != 0); + }, + } + // finish + a |= @boolToInt((a & 4) != 0); // Or P into R + a +%= 1; // round - this step may add a significant bit + a >>= 2; // dump Q and R + // a is now rounded to DBL_MANT_DIG or DBL_MANT_DIG+1 bits + if ((a & (u128(1) << DBL_MANT_DIG)) != 0) { + a >>= 1; + e +%= 1; + } + // a is now rounded to DBL_MANT_DIG bits + } else { + a <<= @intCast(u7, DBL_MANT_DIG -% sd); + // a is now rounded to DBL_MANT_DIG bits + } + + const high: u64 = @bitCast(u32, (e +% 1023) << 20) | // exponent + (@truncate(u32, a >> 32) & 0x000FFFFF); // mantissa-high + const low = @truncate(u32, a); // mantissa-low + + return @bitCast(f64, low | (high << 32)); +} + +test "import floatuntidf" { + _ = @import("floatuntidf_test.zig"); +} diff --git a/std/special/compiler_rt/floatuntidf_test.zig b/std/special/compiler_rt/floatuntidf_test.zig new file mode 100644 index 0000000000..e2c79378e2 --- /dev/null +++ b/std/special/compiler_rt/floatuntidf_test.zig @@ -0,0 +1,81 @@ +const __floatuntidf = @import("floatuntidf.zig").__floatuntidf; +const assert = @import("std").debug.assert; + +fn test__floatuntidf(a: u128, expected: f64) void { + const x = __floatuntidf(a); + assert(x == expected); +} + +test "floatuntidf" { + test__floatuntidf(0, 0.0); + + test__floatuntidf(1, 1.0); + test__floatuntidf(2, 2.0); + test__floatuntidf(20, 20.0); + + test__floatuntidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62); + test__floatuntidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62); + test__floatuntidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62); + test__floatuntidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62); + + test__floatuntidf(make_ti(0x8000008000000000, 0), 0x1.000001p+127); + test__floatuntidf(make_ti(0x8000000000000800, 0), 0x1.0000000000001p+127); + test__floatuntidf(make_ti(0x8000010000000000, 0), 0x1.000002p+127); + test__floatuntidf(make_ti(0x8000000000001000, 0), 0x1.0000000000002p+127); + + test__floatuntidf(make_ti(0x8000000000000000, 0), 0x1.000000p+127); + test__floatuntidf(make_ti(0x8000000000000001, 0), 0x1.0000000000000002p+127); + + test__floatuntidf(0x0007FB72E8000000, 0x1.FEDCBAp+50); + + test__floatuntidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50); + test__floatuntidf(0x0007FB72EB000000, 0x1.FEDCBACp+50); + test__floatuntidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50); + test__floatuntidf(0x0007FB72EC000000, 0x1.FEDCBBp+50); + test__floatuntidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50); + + test__floatuntidf(0x0007FB72E6000000, 0x1.FEDCB98p+50); + test__floatuntidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50); + test__floatuntidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50); + test__floatuntidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50); + test__floatuntidf(0x0007FB72E4000000, 0x1.FEDCB9p+50); + + test__floatuntidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57); + test__floatuntidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57); + test__floatuntidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57); + test__floatuntidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57); + test__floatuntidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57); + test__floatuntidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57); + + test__floatuntidf(make_ti(0x023479FD0E092DC0, 0), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DA1, 1), 0x1.1A3CFE870496Dp+121); + test__floatuntidf(make_ti(0x023479FD0E092DB0, 2), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DB8, 3), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DB6, 4), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DBF, 5), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DC1, 6), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DC7, 7), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DC8, 8), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DCF, 9), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DD0, 0), 0x1.1A3CFE870496Ep+121); + test__floatuntidf(make_ti(0x023479FD0E092DD1, 11), 0x1.1A3CFE870496Fp+121); + test__floatuntidf(make_ti(0x023479FD0E092DD8, 12), 0x1.1A3CFE870496Fp+121); + test__floatuntidf(make_ti(0x023479FD0E092DDF, 13), 0x1.1A3CFE870496Fp+121); + test__floatuntidf(make_ti(0x023479FD0E092DE0, 14), 0x1.1A3CFE870496Fp+121); +} + +fn make_ti(high: u64, low: u64) u128 { + var result: u128 = high; + result <<= 64; + result |= low; + return result; +} diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index dc95aa23f2..6ad7768cb2 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -19,6 +19,8 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); + @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); + @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); @export("__fixunssfti", @import("fixunssfti.zig").__fixunssfti, linkage); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 156835bcb3..7b36bcd04a 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -340,11 +340,23 @@ fn testPeerErrorAndArray2(x: u8) error![]const u8 { }; } -test "explicit cast float number literal to integer if no fraction component" { +test "@floatToInt" { + testFloatToInts(); + comptime testFloatToInts(); +} + +fn testFloatToInts() void { const x = i32(1e4); assert(x == 10000); const y = @floatToInt(i32, f32(1e4)); assert(y == 10000); + expectFloatToInt(u8, 255.1, 255); + expectFloatToInt(i8, 127.2, 127); + expectFloatToInt(i8, -128.2, -128); +} + +fn expectFloatToInt(comptime T: type, f: f32, i: T) void { + assert(@floatToInt(T, f) == i); } test "cast u128 to f128 and back" { @@ -426,3 +438,10 @@ test "@bytesToSlice keeps pointer alignment" { const numbers = @bytesToSlice(u32, bytes[0..]); comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } + +test "@intCast i32 to u7" { + var x: u128 = @maxValue(u128); + var y: i32 = 120; + var z = x >> @intCast(u7, y); + assert(z == 0xff); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 609e3f103f..39107b4a21 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,23 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@floatToInt comptime safety", + \\comptime { + \\ _ = @floatToInt(i8, f32(-129.1)); + \\} + \\comptime { + \\ _ = @floatToInt(u8, f32(-1.1)); + \\} + \\comptime { + \\ _ = @floatToInt(u8, f32(256.1)); + \\} + , + ".tmp_source.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", + ".tmp_source.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", + ".tmp_source.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", + ); + cases.add( "use c_void as return type of fn ptr", \\export fn entry() void { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index ea5beafe8d..a7e8d6dc0e 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,45 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ baz(bar(-1.1)); + \\} + \\fn bar(a: f32) u8 { + \\ return @floatToInt(u8, a); + \\} + \\fn baz(a: u8) void { } + ); + + cases.addRuntimeSafety("@floatToInt cannot fit - negative out of range", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ baz(bar(-129.1)); + \\} + \\fn bar(a: f32) i8 { + \\ return @floatToInt(i8, a); + \\} + \\fn baz(a: i8) void { } + ); + + cases.addRuntimeSafety("@floatToInt cannot fit - positive out of range", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ baz(bar(256.2)); + \\} + \\fn bar(a: f32) u8 { + \\ return @floatToInt(u8, a); + \\} + \\fn baz(a: u8) void { } + ); + cases.addRuntimeSafety("calling panic", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); -- cgit v1.2.3 From 42db807f375d0c8fe248eebcb4eaeed71b43075d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 18:51:46 -0400 Subject: remove redundant implicit casting code and introduce better type mismatch errors closes #1061 --- src/ir.cpp | 564 +++++++++++++++--------------------------------- test/compile_errors.zig | 19 +- 2 files changed, 189 insertions(+), 394 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 1fe078dd3f..ba2a9b3fe2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -65,12 +65,7 @@ enum ConstCastResultId { ConstCastResultIdNullWrapPtr, }; -struct ConstCastErrSetMismatch { - ZigList missing_errors; -}; - struct ConstCastOnly; - struct ConstCastArg { size_t arg_index; ConstCastOnly *child; @@ -80,15 +75,22 @@ struct ConstCastArgNoAlias { size_t arg_index; }; +struct ConstCastOptionalMismatch; +struct ConstCastPointerMismatch; +struct ConstCastSliceMismatch; +struct ConstCastErrUnionErrSetMismatch; +struct ConstCastErrUnionPayloadMismatch; +struct ConstCastErrSetMismatch; + struct ConstCastOnly { ConstCastResultId id; union { - ConstCastErrSetMismatch error_set; - ConstCastOnly *pointer_child; - ConstCastOnly *slice_child; - ConstCastOnly *optional_child; - ConstCastOnly *error_union_payload; - ConstCastOnly *error_union_error_set; + ConstCastErrSetMismatch *error_set_mismatch; + ConstCastPointerMismatch *pointer_mismatch; + ConstCastSliceMismatch *slice_mismatch; + ConstCastOptionalMismatch *optional; + ConstCastErrUnionPayloadMismatch *error_union_payload; + ConstCastErrUnionErrSetMismatch *error_union_error_set; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; ConstCastOnly *null_wrap_ptr_child; @@ -97,6 +99,39 @@ struct ConstCastOnly { } data; }; +struct ConstCastOptionalMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_child; + TypeTableEntry *actual_child; +}; + +struct ConstCastPointerMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_child; + TypeTableEntry *actual_child; +}; + +struct ConstCastSliceMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_child; + TypeTableEntry *actual_child; +}; + +struct ConstCastErrUnionErrSetMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_err_set; + TypeTableEntry *actual_err_set; +}; + +struct ConstCastErrUnionPayloadMismatch { + ConstCastOnly child; + TypeTableEntry *wanted_payload; + TypeTableEntry *actual_payload; +}; + +struct ConstCastErrSetMismatch { + ZigList missing_errors; +}; static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); @@ -7972,8 +8007,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdPointerChild; - result.data.pointer_child = allocate_nonzero(1); - *result.data.pointer_child = child; + result.data.pointer_mismatch = allocate_nonzero(1); + result.data.pointer_mismatch->child = child; + result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type; + result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; } return result; } @@ -7992,8 +8029,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdSliceChild; - result.data.slice_child = allocate_nonzero(1); - *result.data.slice_child = child; + result.data.slice_mismatch = allocate_nonzero(1); + result.data.slice_mismatch->child = child; + result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; + result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; } return result; } @@ -8005,8 +8044,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_type->data.maybe.child_type, source_node, wanted_is_mutable); if (child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdOptionalChild; - result.data.optional_child = allocate_nonzero(1); - *result.data.optional_child = child; + result.data.optional = allocate_nonzero(1); + result.data.optional->child = child; + result.data.optional->wanted_child = wanted_type->data.maybe.child_type; + result.data.optional->actual_child = actual_type->data.maybe.child_type; } return result; } @@ -8017,16 +8058,20 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry actual_type->data.error_union.payload_type, source_node, wanted_is_mutable); if (payload_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionPayload; - result.data.error_union_payload = allocate_nonzero(1); - *result.data.error_union_payload = payload_child; + result.data.error_union_payload = allocate_nonzero(1); + result.data.error_union_payload->child = payload_child; + result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type; + result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type; return result; } ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable); if (error_set_child.id != ConstCastResultIdOk) { result.id = ConstCastResultIdErrorUnionErrorSet; - result.data.error_union_error_set = allocate_nonzero(1); - *result.data.error_union_error_set = error_set_child; + result.data.error_union_error_set = allocate_nonzero(1); + result.data.error_union_error_set->child = error_set_child; + result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type; + result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type; return result; } return result; @@ -8068,8 +8113,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry if (error_entry == nullptr) { if (result.id == ConstCastResultIdOk) { result.id = ConstCastResultIdErrSet; + result.data.error_set_mismatch = allocate(1); } - result.data.error_set.missing_errors.append(contained_error_entry); + result.data.error_set_mismatch->missing_errors.append(contained_error_entry); } } free(errors); @@ -8164,325 +8210,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry return result; } -enum ImplicitCastMatchResult { - ImplicitCastMatchResultNo, - ImplicitCastMatchResultYes, - ImplicitCastMatchResultReportedError, -}; - -static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *wanted_type, - TypeTableEntry *actual_type, IrInstruction *value) -{ - AstNode *source_node = value->source_node; - ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, - source_node, false); - if (const_cast_result.id == ConstCastResultIdOk) { - return ImplicitCastMatchResultYes; - } - - // if we got here with error sets, make an error showing the incompatibilities - ZigList *missing_errors = nullptr; - if (const_cast_result.id == ConstCastResultIdErrSet) { - missing_errors = &const_cast_result.data.error_set.missing_errors; - } - if (const_cast_result.id == ConstCastResultIdErrorUnionErrorSet) { - if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSet) { - missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors; - } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) { - ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); - add_error_note(ira->codegen, msg, value->source_node, - buf_sprintf("unable to cast global error set into smaller set")); - return ImplicitCastMatchResultReportedError; - } - } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) { - ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); - add_error_note(ira->codegen, msg, value->source_node, - buf_sprintf("unable to cast global error set into smaller set")); - return ImplicitCastMatchResultReportedError; - } - if (missing_errors != nullptr) { - ErrorMsg *msg = ir_add_error(ira, value, - buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); - for (size_t i = 0; i < missing_errors->length; i += 1) { - ErrorTableEntry *error_entry = missing_errors->at(i); - add_error_note(ira->codegen, msg, error_entry->decl_node, - buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); - } - - return ImplicitCastMatchResultReportedError; - } - - // implicit conversion from ?T to ?U - if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) { - ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type, - actual_type->data.maybe.child_type, value); - if (res != ImplicitCastMatchResultNo) - return res; - } - - // implicit conversion from non maybe type to maybe type - if (wanted_type->id == TypeTableEntryIdOptional) { - ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type, - actual_type, value); - if (res != ImplicitCastMatchResultNo) - return res; - } - - // implicit conversion from null literal to maybe type - if (wanted_type->id == TypeTableEntryIdOptional && - actual_type->id == TypeTableEntryIdNull) - { - return ImplicitCastMatchResultYes; - } - - // implicit T to U!T - if (wanted_type->id == TypeTableEntryIdErrorUnion && - ir_types_match_with_implicit_cast(ira, wanted_type->data.error_union.payload_type, actual_type, value)) - { - return ImplicitCastMatchResultYes; - } - - // implicit conversion from error set to error union type - if (wanted_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdErrorSet) - { - return ImplicitCastMatchResultYes; - } - - // implicit conversion from T to U!?T - if (wanted_type->id == TypeTableEntryIdErrorUnion && - wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && - ir_types_match_with_implicit_cast(ira, - wanted_type->data.error_union.payload_type->data.maybe.child_type, - actual_type, value)) - { - return ImplicitCastMatchResultYes; - } - - // implicit widening conversion - if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdInt && - wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && - wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) - { - return ImplicitCastMatchResultYes; - } - - // small enough unsigned ints can get casted to large enough signed ints - if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed && - actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && - wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) - { - return ImplicitCastMatchResultYes; - } - - // implicit float widening conversion - if (wanted_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdFloat && - wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) - { - return ImplicitCastMatchResultYes; - } - - // implicit [N]T to []const T - 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(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit &const [N]T to []const T - if (is_slice(wanted_type) && - actual_type->id == TypeTableEntryIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.is_const && - actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(ptr_type->id == TypeTableEntryIdPointer); - - TypeTableEntry *array_type = actual_type->data.pointer.child_type; - - if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, - source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit [N]T to &const []const T - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.is_const && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - is_slice(wanted_type->data.pointer.child_type) && - actual_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *ptr_type = - wanted_type->data.pointer.child_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(ira, ptr_type->data.pointer.child_type, - actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit *[N]T to [*]T - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenUnknown && - actual_type->id == TypeTableEntryIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == TypeTableEntryIdArray && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - - // implicit *[N]T to []T - if (is_slice(wanted_type) && - actual_type->id == TypeTableEntryIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; - assert(slice_ptr_type->id == TypeTableEntryIdPointer); - if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - // implicit [N]T to ?[]const T - if (wanted_type->id == TypeTableEntryIdOptional && - is_slice(wanted_type->data.maybe.child_type) && - actual_type->id == TypeTableEntryIdArray) - { - TypeTableEntry *ptr_type = - wanted_type->data.maybe.child_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(ira, ptr_type->data.pointer.child_type, - actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - - // implicit number literal to typed number - // implicit number literal to &const integer - if (actual_type->id == TypeTableEntryIdComptimeFloat || - actual_type->id == TypeTableEntryIdComptimeInt) - { - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - wanted_type->data.pointer.is_const) - { - if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.pointer.child_type, false)) { - return ImplicitCastMatchResultYes; - } else { - return ImplicitCastMatchResultReportedError; - } - } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, false)) { - return ImplicitCastMatchResultYes; - } else { - return ImplicitCastMatchResultReportedError; - } - } - - // implicit typed number to integer or float literal. - // works when the number is known - if (value->value.special == ConstValSpecialStatic) { - if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) { - return ImplicitCastMatchResultYes; - } else if (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat) { - return ImplicitCastMatchResultYes; - } - } - - // implicit union to its enum tag type - if (wanted_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion && - (actual_type->data.unionation.decl_node->data.container_decl.auto_enum || - actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) - { - type_ensure_zero_bits_known(ira->codegen, actual_type); - if (actual_type->data.unionation.tag_type == wanted_type) { - return ImplicitCastMatchResultYes; - } - } - - // implicit enum to union which has the enum as the tag type - if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && - (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || - wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) - { - type_ensure_zero_bits_known(ira->codegen, wanted_type); - if (wanted_type->data.unionation.tag_type == actual_type) { - return ImplicitCastMatchResultYes; - } - } - - // implicit enum to &const union which has the enum as the tag type - if (actual_type->id == TypeTableEntryIdEnum && - wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle) - { - TypeTableEntry *union_type = wanted_type->data.pointer.child_type; - if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || - union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr) - { - type_ensure_zero_bits_known(ira->codegen, union_type); - if (union_type->data.unionation.tag_type == actual_type) { - return ImplicitCastMatchResultYes; - } - } - } - - // implicit T to *T where T is zero bits - if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type, source_node, false).id == ConstCastResultIdOk) - { - type_ensure_zero_bits_known(ira->codegen, actual_type); - if (!type_has_bits(actual_type)) { - return ImplicitCastMatchResultYes; - } - } - - // implicit undefined literal to anything - if (actual_type->id == TypeTableEntryIdUndefined) { - return ImplicitCastMatchResultYes; - } - - // implicitly take a const pointer to something - if (!type_requires_comptime(actual_type)) { - TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (wanted_type->id == TypeTableEntryIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, wanted_type, const_ptr_actual, - source_node, false).id == ConstCastResultIdOk) - { - return ImplicitCastMatchResultYes; - } - } - - return ImplicitCastMatchResultNo; -} - static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) { size_t old_errors_count = *errors_count; *errors_count = g->errors_by_index.length; @@ -10227,6 +9954,83 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou return result; } +static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCastOnly *cast_result, + ErrorMsg *parent_msg) +{ + switch (cast_result->id) { + case ConstCastResultIdOk: + zig_unreachable(); + case ConstCastResultIdOptionalChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'", + buf_ptr(&cast_result->data.optional->actual_child->name), + buf_ptr(&cast_result->data.optional->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg); + break; + } + case ConstCastResultIdErrorUnionErrorSet: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("error set '%s' cannot cast into error set '%s'", + buf_ptr(&cast_result->data.error_union_error_set->actual_err_set->name), + buf_ptr(&cast_result->data.error_union_error_set->wanted_err_set->name))); + report_recursive_error(ira, source_node, &cast_result->data.error_union_error_set->child, msg); + break; + } + case ConstCastResultIdErrSet: { + ZigList *missing_errors = &cast_result->data.error_set_mismatch->missing_errors; + for (size_t i = 0; i < missing_errors->length; i += 1) { + ErrorTableEntry *error_entry = missing_errors->at(i); + add_error_note(ira->codegen, parent_msg, error_entry->decl_node, + buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); + } + break; + } + case ConstCastResultIdErrSetGlobal: { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("cannot cast global error set into smaller set")); + break; + } + case ConstCastResultIdPointerChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'", + buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name), + buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg); + break; + } + case ConstCastResultIdSliceChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'", + buf_ptr(&cast_result->data.slice_mismatch->actual_child->name), + buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg); + break; + } + case ConstCastResultIdErrorUnionPayload: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'", + buf_ptr(&cast_result->data.error_union_payload->actual_payload->name), + buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name))); + report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); + break; + } + case ConstCastResultIdFnAlign: // TODO + case ConstCastResultIdFnCC: // TODO + case ConstCastResultIdFnVarArgs: // TODO + case ConstCastResultIdFnIsGeneric: // TODO + case ConstCastResultIdFnReturnType: // TODO + case ConstCastResultIdFnArgCount: // TODO + case ConstCastResultIdFnGenericArgCount: // TODO + case ConstCastResultIdFnArg: // TODO + case ConstCastResultIdFnArgNoAlias: // TODO + case ConstCastResultIdType: // TODO + case ConstCastResultIdUnresolvedInferredErrSet: // TODO + case ConstCastResultIdAsyncAllocatorType: // TODO + case ConstCastResultIdNullWrapPtr: // TODO + break; + } +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -10238,11 +10042,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // perfect match or non-const to const - if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false).id == ConstCastResultIdOk) { + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, + source_node, false); + if (const_cast_result.id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } - // explicit widening conversion + // widening conversion if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && @@ -10259,7 +10065,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // explicit float widening conversion + // float widening conversion if (wanted_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdFloat && wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) @@ -10268,7 +10074,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from [N]T to []const T + // cast from [N]T to []const T 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); @@ -10280,7 +10086,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from &const [N]T to []const T + // cast from &const [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.is_const && @@ -10299,7 +10105,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to &const []const T + // cast from [N]T to &const []const T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && @@ -10324,7 +10130,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to ?[]const N + // cast from [N]T to ?[]const N if (wanted_type->id == TypeTableEntryIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) @@ -10348,7 +10154,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit *[N]T to [*]T + // *[N]T to [*]T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenUnknown && actual_type->id == TypeTableEntryIdPointer && @@ -10362,7 +10168,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); } - // explicit *[N]T to []T + // *[N]T to []T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && @@ -10379,7 +10185,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from T to ?T + // cast from T to ?T // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism if (wanted_type->id == TypeTableEntryIdOptional) { TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type; @@ -10411,14 +10217,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from null literal to maybe type + // cast from null literal to maybe type if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdNull) { return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); } - // explicit cast from child type of error type to error type + // cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node, false).id == ConstCastResultIdOk) @@ -10435,7 +10241,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from [N]T to E![]const T + // cast from [N]T to E![]const T if (wanted_type->id == TypeTableEntryIdErrorUnion && is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == TypeTableEntryIdArray) @@ -10459,14 +10265,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from error set to error union type + // cast from error set to error union type if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorSet) { return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); } - // explicit cast from T to E!?T + // cast from T to E!?T if (wanted_type->id == TypeTableEntryIdErrorUnion && wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional && actual_type->id != TypeTableEntryIdOptional) @@ -10489,8 +10295,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from number literal to another type - // explicit cast from number literal to *const integer + // cast from number literal to another type + // cast from number literal to *const integer if (actual_type->id == TypeTableEntryIdComptimeFloat || actual_type->id == TypeTableEntryIdComptimeInt) { @@ -10540,7 +10346,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from typed number to integer or float literal. + // cast from typed number to integer or float literal. // works when the number is known at compile time if (instr_is_comptime(value) && ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) || @@ -10549,7 +10355,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from union to the enum type of the union + // cast from union to the enum type of the union if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) { type_ensure_zero_bits_known(ira->codegen, actual_type); if (type_is_invalid(actual_type)) @@ -10560,7 +10366,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit enum to union which has the enum as the tag type + // enum to union which has the enum as the tag type if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum && (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum || wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) @@ -10571,7 +10377,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit enum to &const union which has the enum as the tag type + // enum to &const union which has the enum as the tag type if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) { TypeTableEntry *union_type = wanted_type->data.pointer.child_type; if (union_type->data.unionation.decl_node->data.container_decl.auto_enum || @@ -10592,7 +10398,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from *T to *[1]T + // cast from *T to *[1]T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) { @@ -10616,7 +10422,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from T to *T where T is zero bits + // cast from T to *T where T is zero bits if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) @@ -10631,12 +10437,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } - // explicit cast from undefined to anything + // cast from undefined to anything if (actual_type->id == TypeTableEntryIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); } - // explicit cast from something to const pointer of it + // cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) { @@ -10644,10 +10450,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("invalid cast from type '%s' to '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->name))); + ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("expected type '%s', found '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); return ira->codegen->invalid_instruction; } @@ -10664,22 +10471,7 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ if (value->value.type->id == TypeTableEntryIdUnreachable) return value; - ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->value.type, value); - switch (result) { - case ImplicitCastMatchResultNo: - ir_add_error(ira, value, - buf_sprintf("expected type '%s', found '%s'", - buf_ptr(&expected_type->name), - buf_ptr(&value->value.type->name))); - return ira->codegen->invalid_instruction; - - case ImplicitCastMatchResultYes: - return ir_analyze_cast(ira, value, expected_type, value); - case ImplicitCastMatchResultReportedError: - return ira->codegen->invalid_instruction; - } - - zig_unreachable(); + return ir_analyze_cast(ira, value, expected_type, value); } static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 39107b4a21..17896f9ab9 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -100,7 +100,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var rule_set = try Foo.init(); \\} , - ".tmp_source.zig:2:13: error: invalid cast from type 'type' to 'i32'", + ".tmp_source.zig:2:13: error: expected type 'i32', found 'type'", ); cases.add( @@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "invalid deref on switch target", + "nested error set mismatch", \\const NextError = error{NextError}; \\const OtherError = error{OutOfMemory}; \\ @@ -134,7 +134,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return null; \\} , - ".tmp_source.zig:5:34: error: expected 'NextError!i32', found 'OtherError!i32'", + ".tmp_source.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", + ".tmp_source.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", + ".tmp_source.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", ); @@ -437,8 +439,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32', found 'error!i32'", - ".tmp_source.zig:3:35: note: unable to cast global error set into smaller set", + ".tmp_source.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'error!i32'", + ".tmp_source.zig:3:35: note: error set 'error' cannot cast into error set 'SmallErrorSet'", + ".tmp_source.zig:3:35: note: cannot cast global error set into smaller set", ); cases.add( @@ -451,8 +454,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:31: error: expected 'SmallErrorSet', found 'error'", - ".tmp_source.zig:3:31: note: unable to cast global error set into smaller set", + ".tmp_source.zig:3:31: error: expected type 'SmallErrorSet', found 'error'", + ".tmp_source.zig:3:31: note: cannot cast global error set into smaller set", ); cases.add( @@ -478,7 +481,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: Set2 = set1; \\} , - ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'", + ".tmp_source.zig:7:19: error: expected type 'Set2', found 'Set1'", ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", ); -- cgit v1.2.3 From 7c99c30bf406342a45833963ce630bb104aef00e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Jun 2018 19:35:35 -0400 Subject: fix calling method with comptime pass-by-non-copyign-value self arg closes #1124 --- src/analyze.cpp | 11 +++++++++++ test/cases/eval.zig | 14 ++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 10cdb0af6f..479abef16a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1470,6 +1470,17 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } + if (param_node->data.param_decl.type != nullptr) { + TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); + if (type_is_invalid(type_entry)) { + return g->builtin_types.entry_invalid; + } + FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; + param_info->type = type_entry; + param_info->is_noalias = param_node->data.param_decl.is_noalias; + fn_type_id.next_param_index += 1; + } + return get_generic_fn_type(g, &fn_type_id); } else if (param_is_var_args) { if (fn_type_id.cc == CallingConventionC) { diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 6c919e17a6..756ffe339a 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -623,3 +623,17 @@ test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; assert(ty == i32); } + +test "call method with comptime pass-by-non-copying-value self parameter" { + const S = struct { + a: u8, + + fn b(comptime s: this) u8 { + return s.a; + } + }; + + const s = S{ .a = 2 }; + var b = s.b(); + assert(b == 2); +} -- cgit v1.2.3 From 55193cb13bbc69350474f6a66728319b41149274 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Jun 2018 06:45:45 -0400 Subject: fix runtime fn ptr equality codegen closes #1140 --- src/codegen.cpp | 10 +++++----- test/cases/misc.zig | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 20268d636a..c2406f0838 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2246,12 +2246,12 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } else if (type_entry->id == TypeTableEntryIdInt) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == TypeTableEntryIdEnum) { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); - return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == TypeTableEntryIdErrorSet || + } else if (type_entry->id == TypeTableEntryIdEnum || + type_entry->id == TypeTableEntryIdErrorSet || type_entry->id == TypeTableEntryIdPointer || - type_entry->id == TypeTableEntryIdBool) + type_entry->id == TypeTableEntryIdBool || + type_entry->id == TypeTableEntryIdPromise || + type_entry->id == TypeTableEntryIdFn) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 89c441e7f9..d539f79a57 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -701,3 +701,8 @@ test "comptime cast fn to ptr" { const addr2 = @ptrCast(*const u8, emptyFn); comptime assert(addr1 == addr2); } + +test "equality compare fn ptrs" { + var a = emptyFn; + assert(a == a); +} -- cgit v1.2.3 From eb6a8e6a3bba061c4a7fb18f53976e1fb683c3d4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 20 Jun 2018 21:51:18 +0200 Subject: fix f128 remainder division bug The modulo operation computed rem(b+rem(a,b), b) which produces -1 for a=1 and b=2. Switch to a - b * trunc(a/b) which produces the expected result, 1. closes #1137 --- src/ir.cpp | 12 +++++++++--- test/cases/math.zig | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index ba2a9b3fe2..950d051492 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7710,6 +7710,14 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } +// c = a - b * trunc(a / b) +static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { + f128M_div(a, b, c); + f128M_roundToInt(c, softfloat_round_min, true, c); + f128M_mul(b, c, c); + f128M_sub(a, c, c); +} + static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) { assert(op1->type == op2->type); out_val->type = op1->type; @@ -7724,9 +7732,7 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); return; case 128: - f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - f128M_add(&out_val->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - f128M_rem(&out_val->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); diff --git a/test/cases/math.zig b/test/cases/math.zig index 0bf99cff0e..08388d3df8 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -434,6 +434,20 @@ test "comptime float rem int" { } } +test "remainder division" { + comptime remdiv(f32); + comptime remdiv(f64); + comptime remdiv(f128); + remdiv(f32); + remdiv(f64); + remdiv(f128); +} + +fn remdiv(comptime T: type) void { + assert(T(1) == T(1) % T(2)); + assert(T(1) == T(7) % T(3)); +} + test "@sqrt" { testSqrt(f64, 12.0); comptime testSqrt(f64, 12.0); -- cgit v1.2.3 From 0ab4afbf4236e6823be6b8845b7e9fa77f7c61f9 Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Thu, 21 Jun 2018 08:14:26 -0400 Subject: Fix increment operation for bigint -1 --- src/bigint.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/bigint.cpp b/src/bigint.cpp index 367ae79b6c..088703402d 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1683,10 +1683,20 @@ void bigint_incr(BigInt *x) { bigint_init_unsigned(x, 1); return; } - - if (x->digit_count == 1 && x->data.digit != UINT64_MAX) { - x->data.digit += 1; - return; + + if (x->digit_count == 1) { + if (x->is_negative) { + if (x->data.digit != 0) { + x->data.digit -= 1; + } + return; + } + else { + if (x->data.digit != UINT64_MAX) { + x->data.digit += 1; + } + return; + } } BigInt copy; -- cgit v1.2.3 From eeda1a1396eb2732e8fe9234bd13d21ac334cfac Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Thu, 21 Jun 2018 08:17:08 -0400 Subject: Fix logic --- src/bigint.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/bigint.cpp b/src/bigint.cpp index 088703402d..bb227a7c3d 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1685,16 +1685,11 @@ void bigint_incr(BigInt *x) { } if (x->digit_count == 1) { - if (x->is_negative) { - if (x->data.digit != 0) { - x->data.digit -= 1; - } + if (x->is_negative && x->data.digit != 0) { + x->data.digit -= 1; return; - } - else { - if (x->data.digit != UINT64_MAX) { - x->data.digit += 1; - } + } else if (!x->is_negative && x->data.digit != UINT64_MAX) { + x->data.digit += 1; return; } } -- cgit v1.2.3 From 5f38d6e2e97829ed74f06a96b5d07a2c68516063 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jun 2018 14:43:55 -0400 Subject: add casting docs, __extenddftf2, and __extendsftf2 --- CMakeLists.txt | 1 + doc/langref.html.in | 171 ++++++++++++++++++++++++--- src/ir.cpp | 6 +- std/special/compiler_rt/extendXfYf2.zig | 87 ++++++++++++++ std/special/compiler_rt/extendXfYf2_test.zig | 108 +++++++++++++++++ std/special/compiler_rt/index.zig | 2 + test/behavior.zig | 1 + test/cases/widening.zig | 26 ++++ 8 files changed, 384 insertions(+), 18 deletions(-) create mode 100644 std/special/compiler_rt/extendXfYf2.zig create mode 100644 std/special/compiler_rt/extendXfYf2_test.zig create mode 100644 test/cases/widening.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 030398d71c..99de2328d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -558,6 +558,7 @@ set(ZIG_STD_FILES "special/compiler_rt/aullrem.zig" "special/compiler_rt/comparetf2.zig" "special/compiler_rt/divti3.zig" + "special/compiler_rt/extendXfYf2.zig" "special/compiler_rt/fixuint.zig" "special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfsi.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index bdc33cb808..b76fc69385 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3573,14 +3573,161 @@ const optional_value: ?i32 = null; {#header_close#} {#header_close#} {#header_open|Casting#} -

      TODO: explain implicit vs explicit casting

      -

      TODO: resolve peer types builtin

      -

      TODO: truncate builtin

      -

      TODO: bitcast builtin

      -

      TODO: int to ptr builtin

      -

      TODO: ptr to int builtin

      -

      TODO: ptrcast builtin

      -

      TODO: explain number literals vs concrete types

      +

      + A type cast converts a value of one type to another. + Zig has {#link|Implicit Casts#} for conversions that are known to be completely safe and unambiguous, + and {#link|Explicit Casts#} for conversions that one would not want to happen on accident. + There is also a third kind of type conversion called {#link|Peer Type Resolution#} for + the case when a result type must be decided given multiple operand types. +

      + {#header_open|Implicit Casts#} +

      + An implicit cast occurs when one type is expected, but different type is provided: +

      + {#code_begin|test#} +test "implicit cast - variable declaration" { + var a: u8 = 1; + var b: u16 = a; +} + +test "implicit cast - function call" { + var a: u8 = 1; + foo(a); +} + +fn foo(b: u16) void {} + +test "implicit cast - invoke a type as a function" { + var a: u8 = 1; + var b = u16(a); +} + {#code_end#} + {#header_open|Implicit Cast: Stricter Qualification#} +

      + Values which have the same representation at runtime can be cast to increase the strictness + of the qualifiers, no matter how nested the qualifiers are: +

      +
        +
      • const - non-const to const is allowed
      • +
      • volatile - non-volatile to volatile is allowed
      • +
      • align - bigger to smaller alignment is allowed
      • +
      • {#link|error sets|Error Set Type#} to supersets is allowed
      • +
      +

      + These casts are no-ops at runtime since the value representation does not change. +

      + {#code_begin|test#} +test "implicit cast - const qualification" { + var a: i32 = 1; + var b: *i32 = &a; + foo(b); +} + +fn foo(a: *const i32) void {} + {#code_end#} +

      + In addition, pointers implicitly cast to const optional pointers: +

      + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +test "cast *[1][*]const u8 to [*]const ?[*]const u8" { + const window_name = [1][*]const u8{c"window name"}; + const x: [*]const ?[*]const u8 = &window_name; + assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); +} + {#code_end#} + {#header_close#} + {#header_open|Implicit Cast: Integer and Float Widening#} +

      + {#link|Integers#} implicitly cast to integer types which can represent every value of the old type, and likewise + {#link|Floats#} implicitly cast to float types which can represent every value of the old type. +

      + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +test "integer widening" { + var a: u8 = 250; + var b: u16 = a; + var c: u32 = b; + var d: u64 = c; + var e: u64 = d; + var f: u128 = e; + assert(f == a); +} + +test "implicit unsigned integer to signed integer" { + var a: u8 = 250; + var b: i16 = a; + assert(b == 250); +} + +test "float widening" { + var a: f32 = 12.34; + var b: f64 = a; + var c: f128 = b; + assert(c == a); +} + {#code_end#} + {#header_close#} + {#header_open|Implicit Cast: Arrays#} +

      TODO: [N]T to []const T

      +

      TODO: *const [N]T to []const T

      +

      TODO: [N]T to *const []const T

      +

      TODO: [N]T to ?[]const T

      +

      TODO: *[N]T to []T

      +

      TODO: *[N]T to [*]T

      +

      TODO: *T to *[1]T

      +

      TODO: [N]T to E![]const T

      + {#header_close#} + {#header_open|Implicit Cast: Optionals#} +

      TODO: T to ?T

      +

      TODO: T to E!?T

      +

      TODO: null to ?T

      + {#header_close#} + {#header_open|Implicit Cast: T to E!T#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: E to E!T#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: comptime_int to *const integer#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: comptime_float to *const float#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: compile-time known numbers#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: union to enum#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: enum to union#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: T to *T when @sizeOf(T) == 0#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: undefined#} +

      TODO

      + {#header_close#} + {#header_open|Implicit Cast: T to *const T#} +

      TODO

      + {#header_close#} + {#header_close#} + + {#header_open|Explicit Casts#} +

      TODO

      + {#header_close#} + + {#header_open|Peer Type Resolution#} +

      TODO

      + {#header_close#} {#header_close#} {#header_open|void#} @@ -5522,12 +5669,6 @@ pub const FloatMode = enum {

      {#see_also|Compile Variables#} {#header_close#} - {#header_open|@setGlobalSection#} -
      @setGlobalSection(global_variable_name, comptime section_name: []const u8) bool
      -

      - Puts the global variable in the specified section. -

      - {#header_close#} {#header_open|@shlExact#}
      @shlExact(value: T, shift_amt: Log2T) T

      @@ -6928,7 +7069,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/ir.cpp b/src/ir.cpp index 950d051492..c6078e755d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10092,7 +10092,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from &const [N]T to []const T + // cast from *const [N]T to []const T if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdPointer && actual_type->data.pointer.is_const && @@ -10111,7 +10111,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from [N]T to &const []const T + // cast from [N]T to *const []const T if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.is_const && is_slice(wanted_type->data.pointer.child_type) && @@ -10136,7 +10136,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from [N]T to ?[]const N + // cast from [N]T to ?[]const T if (wanted_type->id == TypeTableEntryIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == TypeTableEntryIdArray) diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig new file mode 100644 index 0000000000..6fa8cf4654 --- /dev/null +++ b/std/special/compiler_rt/extendXfYf2.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +pub extern fn __extenddftf2(a: f64) f128 { + return extendXfYf2(f128, f64, a); +} + +pub extern fn __extendsftf2(a: f32) f128 { + return extendXfYf2(f128, f32, a); +} + +const CHAR_BIT = 8; + +pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { + const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); + const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); + const srcSigBits = std.math.floatMantissaBits(src_t); + const dstSigBits = std.math.floatMantissaBits(dst_t); + const SrcShift = std.math.Log2Int(src_rep_t); + const DstShift = std.math.Log2Int(dst_rep_t); + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT; + const srcExpBits: i32 = srcBits - srcSigBits - 1; + const srcInfExp: i32 = (1 << srcExpBits) - 1; + const srcExpBias: i32 = srcInfExp >> 1; + + const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits; + const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits; + const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits); + const srcAbsMask: src_rep_t = srcSignMask -% 1; + const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1); + const srcNaNCode: src_rep_t = srcQNaN -% 1; + + const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT; + const dstExpBits: i32 = dstBits - dstSigBits - 1; + const dstInfExp: i32 = (1 << dstExpBits) - 1; + const dstExpBias: i32 = dstInfExp >> 1; + + const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits; + + // Break a into a sign and representation of the absolute value + const aRep: src_rep_t = @bitCast(src_rep_t, a); + const aAbs: src_rep_t = aRep & srcAbsMask; + const sign: src_rep_t = aRep & srcSignMask; + var absResult: dst_rep_t = undefined; + + // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted + // to (signed) int. To avoid that, explicitly cast to src_rep_t. + if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) { + // a is a normal number. + // Extend to the destination type by shifting the significand and + // exponent into the proper position and rebiasing the exponent. + absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits); + absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits; + } else if (aAbs >= srcInfinity) { + // a is NaN or infinity. + // Conjure the result by beginning with infinity, then setting the qNaN + // bit (if needed) and right-aligning the rest of the trailing NaN + // payload field. + absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits; + absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits); + absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); + } else if (aAbs != 0) { + // a is denormal. + // renormalize the significand and clear the leading bit, then insert + // the correct adjusted exponent in the destination type. + const scale: i32 = @clz(aAbs) - @clz(srcMinNormal); + absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); + absResult ^= dstMinNormal; + const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1; + absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits); + } else { + // a is zero. + absResult = 0; + } + + // Apply the signbit to (dst_t)abs(a). + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits); + return @bitCast(dst_t, result); +} + +test "import extendXfYf2" { + _ = @import("extendXfYf2_test.zig"); +} diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig new file mode 100644 index 0000000000..84fb410fbb --- /dev/null +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -0,0 +1,108 @@ +const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; +const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; +const assert = @import("std").debug.assert; + +fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { + const x = __extenddftf2(a); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extenddftf2 test failure"); +} + +fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void { + const x = __extendsftf2(a); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extendsftf2 test failure"); +} + +test "extenddftf2" { + // qNaN + test__extenddftf2(makeQNaN64(), 0x7fff800000000000, 0x0); + + // NaN + test__extenddftf2(makeNaN64(0x7100000000000), 0x7fff710000000000, 0x0); + + // inf + test__extenddftf2(makeInf64(), 0x7fff000000000000, 0x0); + + // zero + test__extenddftf2(0.0, 0x0, 0x0); + + test__extenddftf2(0x1.23456789abcdefp+5, 0x400423456789abcd, 0xf000000000000000); + + test__extenddftf2(0x1.edcba987654321fp-9, 0x3ff6edcba9876543, 0x2000000000000000); + + test__extenddftf2(0x1.23456789abcdefp+45, 0x402c23456789abcd, 0xf000000000000000); + + test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000); +} + +test "extendsftf2" { + // qNaN + test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0); + // NaN + test__extendsftf2(makeNaN32(0x410000), 0x7fff820000000000, 0x0); + // inf + test__extendsftf2(makeInf32(), 0x7fff000000000000, 0x0); + // zero + test__extendsftf2(0.0, 0x0, 0x0); + test__extendsftf2(0x1.23456p+5, 0x4004234560000000, 0x0); + test__extendsftf2(0x1.edcbap-9, 0x3ff6edcba0000000, 0x0); + test__extendsftf2(0x1.23456p+45, 0x402c234560000000, 0x0); + test__extendsftf2(0x1.edcbap-45, 0x3fd2edcba0000000, 0x0); +} + +fn makeQNaN64() f64 { + return @bitCast(f64, u64(0x7ff8000000000000)); +} + +fn makeInf64() f64 { + return @bitCast(f64, u64(0x7ff0000000000000)); +} + +fn makeNaN64(rand: u64) f64 { + return @bitCast(f64, 0x7ff0000000000000 | (rand & 0xfffffffffffff)); +} + +fn makeQNaN32() f32 { + return @bitCast(f32, u32(0x7fc00000)); +} + +fn makeNaN32(rand: u32) f32 { + return @bitCast(f32, 0x7f800000 | (rand & 0x7fffff)); +} + +fn makeInf32() f32 { + return @bitCast(f32, u32(0x7f800000)); +} diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 6ad7768cb2..c96e1587f8 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -20,6 +20,8 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); + @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); + @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); diff --git a/test/behavior.zig b/test/behavior.zig index b494c623e2..3a2f706ad4 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -59,4 +59,5 @@ comptime { _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); _ = @import("cases/while.zig"); + _ = @import("cases/widening.zig"); } diff --git a/test/cases/widening.zig b/test/cases/widening.zig new file mode 100644 index 0000000000..18c12806d3 --- /dev/null +++ b/test/cases/widening.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; + +test "integer widening" { + var a: u8 = 250; + var b: u16 = a; + var c: u32 = b; + var d: u64 = c; + var e: u64 = d; + var f: u128 = e; + assert(f == a); +} + +test "implicit unsigned integer to signed integer" { + var a: u8 = 250; + var b: i16 = a; + assert(b == 250); +} + +test "float widening" { + var a: f32 = 12.34; + var b: f64 = a; + var c: f128 = b; + assert(c == a); +} -- cgit v1.2.3 From 459d72f8736ebd8372b9050c17e5f3bc00092573 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jun 2018 17:41:49 -0400 Subject: fix compiler crash for invalid enum closes #1079 closes #1147 --- src/analyze.cpp | 5 +++-- test/compile_errors.zig | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 479abef16a..5160a19e81 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2318,8 +2318,9 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) { return; if (enum_type->data.enumeration.zero_bits_loop_flag) { - enum_type->data.enumeration.zero_bits_known = true; - enum_type->data.enumeration.zero_bits_loop_flag = false; + add_node_error(g, enum_type->data.enumeration.decl_node, + buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name))); + enum_type->data.enumeration.is_invalid = true; return; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 17896f9ab9..2247f0af96 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "enum field value references enum", + \\pub const Foo = extern enum { + \\ A = Foo.B, + \\ C = D, + \\}; + \\export fn entry() void { + \\ var s: Foo = Foo.E; + \\} + , + ".tmp_source.zig:1:17: error: 'Foo' depends on itself", + ); + cases.add( "@floatToInt comptime safety", \\comptime { -- cgit v1.2.3 From 8e714289cac55fd6f793cf21cea5fa1930edb985 Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Sun, 24 Jun 2018 20:27:18 -0400 Subject: Fix os_path_join for case where dirname is empty --- src/os.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/os.cpp b/src/os.cpp index b7d2fd1de0..d52295950d 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -225,6 +225,11 @@ void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname) { } void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { + if (buf_len(dirname) == 0) { + buf_init_from_buf(out_full_path, basename); + return; + } + buf_init_from_buf(out_full_path, dirname); uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1); if (!os_is_sep(c)) -- cgit v1.2.3 From af95e1557214df4a1a34a712efc2f8dafb502c82 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Jun 2018 15:10:11 -0400 Subject: rename get_maybe_type to get_optional_type --- src/all_types.hpp | 2 +- src/analyze.cpp | 12 ++++++------ src/analyze.hpp | 2 +- src/ir.cpp | 38 +++++++++++++++++++------------------- 4 files changed, 27 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 12e054cbeb..019dcb182e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1233,7 +1233,7 @@ struct TypeTableEntry { // use these fields to make sure we don't duplicate type table entries for the same type TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const] - TypeTableEntry *maybe_parent; + TypeTableEntry *optional_parent; TypeTableEntry *promise_parent; TypeTableEntry *promise_frame_parent; // If we generate a constant name value for this type, we memoize it here. diff --git a/src/analyze.cpp b/src/analyze.cpp index 5160a19e81..c018ee4e92 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -482,7 +482,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) return return_type->promise_frame_parent; } - TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise); + TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise); TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); ZigList field_names = {}; @@ -513,9 +513,9 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) return entry; } -TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { - if (child_type->maybe_parent) { - TypeTableEntry *entry = child_type->maybe_parent; +TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type) { + if (child_type->optional_parent) { + TypeTableEntry *entry = child_type->optional_parent; return entry; } else { ensure_complete_type(g, child_type); @@ -592,7 +592,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { entry->data.maybe.child_type = child_type; - child_type->maybe_parent = entry; + child_type->optional_parent = entry; return entry; } } @@ -2996,7 +2996,7 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) { return wrong_panic_prototype(g, proto_node, fn_type); } - TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); + TypeTableEntry *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g)); if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { return wrong_panic_prototype(g, proto_node, fn_type); } diff --git a/src/analyze.hpp b/src/analyze.hpp index 88e06b2390..c2730197e2 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -24,7 +24,7 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); -TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type); +TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type); TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size); TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type); TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, diff --git a/src/ir.cpp b/src/ir.cpp index c6078e755d..1930bbb248 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3044,7 +3044,7 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, - get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig IrInstruction *replacement_value = irb->exec->coro_handle; IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node, @@ -6654,7 +6654,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, - get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node, promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr, AtomicRmwOp_xchg, AtomicOrderSeqCst); @@ -6988,7 +6988,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, - get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); + get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); @@ -8762,7 +8762,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (prev_inst->value.type->id == TypeTableEntryIdOptional) { return prev_inst->value.type; } else { - return get_maybe_type(ira->codegen, prev_inst->value.type); + return get_optional_type(ira->codegen, prev_inst->value.type); } } else { return prev_inst->value.type; @@ -12127,7 +12127,7 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, { if (instruction->optional == IrInstructionErrorReturnTrace::Null) { TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); - TypeTableEntry *optional_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); + TypeTableEntry *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); assert(get_codegen_ptr_type(optional_type) != nullptr); @@ -13105,7 +13105,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdPromise: { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); - out_val->data.x_type = get_maybe_type(ira->codegen, type_entry); + out_val->data.x_type = get_optional_type(ira->codegen, type_entry); return ira->codegen->builtin_types.entry_type; } case TypeTableEntryIdUnreachable: @@ -16326,7 +16326,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop true, false, PtrLenUnknown, get_abi_alignment(ira->codegen, ira->codegen->builtin_types.entry_u8), 0, 0); - fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); + fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { fn_def_fields[6].data.x_optional = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); @@ -16609,7 +16609,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // child: ?type ensure_field_index(result->type, "child", 0); fields[0].special = ConstValSpecialStatic; - fields[0].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.promise.result_type == nullptr) fields[0].data.x_optional = nullptr; @@ -16763,7 +16763,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // tag_type: ?type ensure_field_index(result->type, "tag_type", 1); fields[1].special = ConstValSpecialStatic; - fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); AstNode *union_decl_node = type_entry->data.unionation.decl_node; if (union_decl_node->data.container_decl.auto_enum || @@ -16803,7 +16803,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type); + inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type); if (fields[1].data.x_optional == nullptr) { inner_fields[1].data.x_optional = nullptr; @@ -16874,7 +16874,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t ConstExprValue *inner_fields = create_const_vals(3); inner_fields[1].special = ConstValSpecialStatic; - inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize); + inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_usize); if (!type_has_bits(struct_field->type_entry)) { inner_fields[1].data.x_optional = nullptr; @@ -16934,7 +16934,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // return_type: ?type ensure_field_index(result->type, "return_type", 3); fields[3].special = ConstValSpecialStatic; - fields[3].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.return_type == nullptr) fields[3].data.x_optional = nullptr; else { @@ -16947,7 +16947,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t // async_allocator_type: type ensure_field_index(result->type, "async_allocator_type", 4); fields[4].special = ConstValSpecialStatic; - fields[4].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[4].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr) fields[4].data.x_optional = nullptr; else { @@ -16990,7 +16990,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t inner_fields[1].type = ira->codegen->builtin_types.entry_bool; inner_fields[1].data.x_bool = fn_param_info->is_noalias; inner_fields[2].special = ConstValSpecialStatic; - inner_fields[2].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_type); + inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); if (arg_is_generic) inner_fields[2].data.x_optional = nullptr; @@ -17342,7 +17342,7 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, operand_type, success_order, failure_order); - result->value.type = get_maybe_type(ira->codegen, operand_type); + result->value.type = get_optional_type(ira->codegen, operand_type); ir_link_new_instruction(result, &instruction->base); ir_add_alloca(ira, result, result->value.type); return result->value.type; @@ -19013,7 +19013,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 old_align_bytes = ptr_type->data.pointer.alignment; TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); - result_type = get_maybe_type(ira->codegen, better_ptr_type); + result_type = get_optional_type(ira->codegen, better_ptr_type); } else if (target_type->id == TypeTableEntryIdOptional && target_type->data.maybe.child_type->id == TypeTableEntryIdFn) { @@ -19021,7 +19021,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 old_align_bytes = fn_type_id.alignment; fn_type_id.alignment = align_bytes; TypeTableEntry *fn_type = get_fn_type(ira->codegen, &fn_type_id); - result_type = get_maybe_type(ira->codegen, fn_type); + result_type = get_optional_type(ira->codegen, fn_type); } else if (is_slice(target_type)) { TypeTableEntry *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry; old_align_bytes = slice_ptr_type->data.pointer.alignment; @@ -19782,7 +19782,7 @@ static TypeTableEntry *ir_analyze_instruction_coro_free(IrAnalyze *ira, IrInstru instruction->base.source_node, coro_id, coro_handle); ir_link_new_instruction(result, &instruction->base); TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); - result->value.type = get_maybe_type(ira->codegen, ptr_type); + result->value.type = get_optional_type(ira->codegen, ptr_type); return result->value.type; } @@ -19850,7 +19850,7 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, instruction->base.source_node, alloc_fn, coro_size); ir_link_new_instruction(result, &instruction->base); TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false); - result->value.type = get_maybe_type(ira->codegen, u8_ptr_type); + result->value.type = get_optional_type(ira->codegen, u8_ptr_type); return result->value.type; } -- cgit v1.2.3 From 11ca38a4e9c637bf6ff635f4f62634edaf89f853 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Jun 2018 15:27:41 -0400 Subject: fix crash for optional pointer to empty struct closes #1153 --- src/ir.cpp | 3 ++- test/behavior.zig | 1 + test/cases/optional.zig | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/cases/optional.zig (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 1930bbb248..76178f2437 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7985,9 +7985,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry // * and [*] can do a const-cast-only to ?* and ?[*], respectively // but not if there is a mutable parent pointer + // and not if the pointer is zero bits if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional && wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer) + actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type)) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable); diff --git a/test/behavior.zig b/test/behavior.zig index 3a2f706ad4..3766ed4305 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -35,6 +35,7 @@ comptime { _ = @import("cases/math.zig"); _ = @import("cases/merge_error_sets.zig"); _ = @import("cases/misc.zig"); + _ = @import("cases/optional.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); diff --git a/test/cases/optional.zig b/test/cases/optional.zig new file mode 100644 index 0000000000..0129252dab --- /dev/null +++ b/test/cases/optional.zig @@ -0,0 +1,9 @@ +const assert = @import("std").debug.assert; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + assert(o != null); +} -- cgit v1.2.3 From 1f45075a0e1d86fa110011f6cedbef61a9f6f056 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: dry floating-point type definitions --- src/codegen.cpp | 57 ++++++++++++++------------------------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index c2406f0838..abec5a8ec7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6177,58 +6177,29 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_usize = entry; } } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMFloatType(); - buf_init_from_str(&entry->name, "f32"); - entry->data.floating.bit_count = 32; - - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - debug_size_in_bits, - ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_f32 = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMDoubleType(); - buf_init_from_str(&entry->name, "f64"); - entry->data.floating.bit_count = 64; - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - debug_size_in_bits, - ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_f64 = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { + auto add_fp_entry = [] (CodeGen *g, + const char *name, + uint32_t bit_count, + LLVMTypeRef type_ref, + TypeTableEntry **field) { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMFP128Type(); - buf_init_from_str(&entry->name, "f128"); - entry->data.floating.bit_count = 128; + entry->type_ref = type_ref; + buf_init_from_str(&entry->name, name); + entry->data.floating.bit_count = bit_count; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_f128 = entry; + *field = entry; g->primitive_type_table.put(&entry->name, entry); - } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); - entry->type_ref = LLVMX86FP80Type(); - buf_init_from_str(&entry->name, "c_longdouble"); - entry->data.floating.bit_count = 80; + }; + add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); + add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); + add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); + add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble); - uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); - entry->di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - debug_size_in_bits, - ZigLLVMEncoding_DW_ATE_float()); - g->builtin_types.entry_c_longdouble = entry; - g->primitive_type_table.put(&entry->name, entry); - } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); entry->type_ref = LLVMVoidType(); -- cgit v1.2.3 From fd75e73ee9818f12fd81d8fdb3cb949c492d664a Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: add f16 type Add support for half-precision floating point operations. Introduce `__extendhfsf2` and `__truncsfhf2` in std/special/compiler_rt. Add `__gnu_h2f_ieee` and `__gnu_f2h_ieee` as aliases that are used in Windows builds. The logic in std/special/compiler_rt/extendXfYf2.zig has been reworked and can now operate on 16 bits floating point types. `extendXfYf2()` and `truncXfYf2()` are marked `inline` to work around a not entirely understood stack alignment issue on Windows when calling the f16 versions of the builtins. closes #1122 --- CMakeLists.txt | 16 +++ src/all_types.hpp | 2 + src/analyze.cpp | 15 +++ src/bigfloat.cpp | 8 ++ src/bigfloat.hpp | 2 + src/codegen.cpp | 4 + src/ir.cpp | 151 ++++++++++++++++++++++++++- src/util.hpp | 19 ++++ std/special/compiler_rt/extendXfYf2.zig | 56 +++++----- std/special/compiler_rt/extendXfYf2_test.zig | 46 ++++++++ std/special/compiler_rt/index.zig | 5 + std/special/compiler_rt/truncXfYf2.zig | 111 ++++++++++++++++++++ std/special/compiler_rt/truncXfYf2_test.zig | 64 ++++++++++++ test/cases/cast.zig | 28 ++++- test/cases/math.zig | 12 ++- test/cases/misc.zig | 1 + 16 files changed, 505 insertions(+), 35 deletions(-) create mode 100644 std/special/compiler_rt/truncXfYf2.zig create mode 100644 std/special/compiler_rt/truncXfYf2_test.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 99de2328d2..789da4a8a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,12 +261,15 @@ endif() set(EMBEDDED_SOFTFLOAT_SOURCES "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_add.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_div.c" @@ -293,8 +296,20 @@ set(EMBEDDED_SOFTFLOAT_SOURCES "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_add.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_div.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_eq.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_lt.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mul.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_rem.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_roundToInt.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sqrt.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sub.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f64.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_f128M.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f128M.c" + "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f16.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_add256M.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addCarryM.c" "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addComplCarryM.c" @@ -572,6 +587,7 @@ set(ZIG_STD_FILES "special/compiler_rt/floatuntidf.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/index.zig" + "special/compiler_rt/truncXfYf2.zig" "special/compiler_rt/udivmod.zig" "special/compiler_rt/udivmoddi4.zig" "special/compiler_rt/udivmodti4.zig" diff --git a/src/all_types.hpp b/src/all_types.hpp index 019dcb182e..5d449491c8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -258,6 +258,7 @@ struct ConstExprValue { // populated if special == ConstValSpecialStatic BigInt x_bigint; BigFloat x_bigfloat; + float16_t x_f16; float x_f32; double x_f64; float128_t x_f128; @@ -1598,6 +1599,7 @@ struct CodeGen { TypeTableEntry *entry_i128; TypeTableEntry *entry_isize; TypeTableEntry *entry_usize; + TypeTableEntry *entry_f16; TypeTableEntry *entry_f32; TypeTableEntry *entry_f64; TypeTableEntry *entry_f128; diff --git a/src/analyze.cpp b/src/analyze.cpp index c018ee4e92..25cc1c79d0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4668,6 +4668,13 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { } case TypeTableEntryIdFloat: switch (const_val->type->data.floating.bit_count) { + case 16: + { + uint16_t result; + static_assert(sizeof(result) == sizeof(const_val->data.x_f16), ""); + memcpy(&result, &const_val->data.x_f16, sizeof(result)); + return result * 65537u; + } case 32: { uint32_t result; @@ -5128,6 +5135,9 @@ void init_const_float(ConstExprValue *const_val, TypeTableEntry *type, double va bigfloat_init_64(&const_val->data.x_bigfloat, value); } else if (type->id == TypeTableEntryIdFloat) { switch (type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = zig_double_to_f16(value); + break; case 32: const_val->data.x_f32 = value; break; @@ -5441,6 +5451,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdFloat: assert(a->type->data.floating.bit_count == b->type->data.floating.bit_count); switch (a->type->data.floating.bit_count) { + case 16: + return f16_eq(a->data.x_f16, b->data.x_f16); case 32: return a->data.x_f32 == b->data.x_f32; case 64: @@ -5614,6 +5626,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; case TypeTableEntryIdFloat: switch (type_entry->data.floating.bit_count) { + case 16: + buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); + return; case 32: buf_appendf(buf, "%f", const_val->data.x_f32); return; diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp index dcb6db61db..cc442fa3b7 100644 --- a/src/bigfloat.cpp +++ b/src/bigfloat.cpp @@ -18,6 +18,10 @@ void bigfloat_init_128(BigFloat *dest, float128_t x) { dest->value = x; } +void bigfloat_init_16(BigFloat *dest, float16_t x) { + f16_to_f128M(x, &dest->value); +} + void bigfloat_init_32(BigFloat *dest, float x) { float32_t f32_val; memcpy(&f32_val, &x, sizeof(float)); @@ -146,6 +150,10 @@ Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) { } } +float16_t bigfloat_to_f16(const BigFloat *bigfloat) { + return f128M_to_f16(&bigfloat->value); +} + float bigfloat_to_f32(const BigFloat *bigfloat) { float32_t f32_value = f128M_to_f32(&bigfloat->value); float result; diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp index e212c30c87..c6ae567945 100644 --- a/src/bigfloat.hpp +++ b/src/bigfloat.hpp @@ -22,6 +22,7 @@ struct BigFloat { struct Buf; +void bigfloat_init_16(BigFloat *dest, float16_t x); void bigfloat_init_32(BigFloat *dest, float x); void bigfloat_init_64(BigFloat *dest, double x); void bigfloat_init_128(BigFloat *dest, float128_t x); @@ -29,6 +30,7 @@ void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x); void bigfloat_init_bigint(BigFloat *dest, const BigInt *op); int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len); +float16_t bigfloat_to_f16(const BigFloat *bigfloat); float bigfloat_to_f32(const BigFloat *bigfloat); double bigfloat_to_f64(const BigFloat *bigfloat); float128_t bigfloat_to_f128(const BigFloat *bigfloat); diff --git a/src/codegen.cpp b/src/codegen.cpp index abec5a8ec7..4419f4fc84 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -17,6 +17,7 @@ #include "os.hpp" #include "translate_c.hpp" #include "target.hpp" +#include "util.hpp" #include "zig_llvm.h" #include @@ -5211,6 +5212,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c const_val->data.x_err_set->value, false); case TypeTableEntryIdFloat: switch (type_entry->data.floating.bit_count) { + case 16: + return LLVMConstReal(type_entry->type_ref, zig_f16_to_double(const_val->data.x_f16)); case 32: return LLVMConstReal(type_entry->type_ref, const_val->data.x_f32); case 64: @@ -6195,6 +6198,7 @@ static void define_builtin_types(CodeGen *g) { *field = entry; g->primitive_type_table.put(&entry->name, entry); }; + add_fp_entry(g, "f16", 16, LLVMHalfType(), &g->builtin_types.entry_f16); add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); diff --git a/src/ir.cpp b/src/ir.cpp index 76178f2437..694f912145 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11,9 +11,10 @@ #include "ir.hpp" #include "ir_print.hpp" #include "os.hpp" -#include "translate_c.hpp" #include "range_set.hpp" #include "softfloat.hpp" +#include "translate_c.hpp" +#include "util.hpp" struct IrExecContext { ConstExprValue *mem_slot_list; @@ -7238,6 +7239,11 @@ static bool float_has_fraction(ConstExprValue *const_val) { return bigfloat_has_fraction(&const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { + case 16: + { + float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); + return !f16_eq(floored, const_val->data.x_f16); + } case 32: return floorf(const_val->data.x_f32) != const_val->data.x_f32; case 64: @@ -7261,6 +7267,9 @@ static void float_append_buf(Buf *buf, ConstExprValue *const_val) { bigfloat_append_buf(buf, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { + case 16: + buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); + break; case 32: buf_appendf(buf, "%f", const_val->data.x_f32); break; @@ -7296,6 +7305,17 @@ static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) { bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); } else if (const_val->type->id == TypeTableEntryIdFloat) { switch (const_val->type->data.floating.bit_count) { + case 16: + { + double x = zig_f16_to_double(const_val->data.x_f16); + if (x >= 0) { + bigint_init_unsigned(bigint, (uint64_t)x); + } else { + bigint_init_unsigned(bigint, (uint64_t)-x); + bigint->is_negative = true; + } + break; + } case 32: if (const_val->data.x_f32 >= 0) { bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); @@ -7332,6 +7352,9 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); + break; case 32: dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); break; @@ -7349,11 +7372,39 @@ static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) { } } +static void float_init_f16(ConstExprValue *dest_val, float16_t x) { + if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { + bigfloat_init_16(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == TypeTableEntryIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = x; + break; + case 32: + dest_val->data.x_f32 = zig_f16_to_double(x); + break; + case 64: + dest_val->data.x_f64 = zig_f16_to_double(x); + break; + case 128: + f16_to_f128M(x, &dest_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + static void float_init_f32(ConstExprValue *dest_val, float x) { if (dest_val->type->id == TypeTableEntryIdComptimeFloat) { bigfloat_init_32(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = zig_double_to_f16(x); + break; case 32: dest_val->data.x_f32 = x; break; @@ -7380,6 +7431,9 @@ static void float_init_f64(ConstExprValue *dest_val, double x) { bigfloat_init_64(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = zig_double_to_f16(x); + break; case 32: dest_val->data.x_f32 = x; break; @@ -7406,6 +7460,9 @@ static void float_init_f128(ConstExprValue *dest_val, float128_t x) { bigfloat_init_128(&dest_val->data.x_bigfloat, x); } else if (dest_val->type->id == TypeTableEntryIdFloat) { switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = f128M_to_f16(&x); + break; case 32: { float32_t f32_val = f128M_to_f32(&x); @@ -7436,6 +7493,9 @@ static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); } else if (src_val->type->id == TypeTableEntryIdFloat) { switch (src_val->type->data.floating.bit_count) { + case 16: + float_init_f16(dest_val, src_val->data.x_f16); + break; case 32: float_init_f32(dest_val, src_val->data.x_f32); break; @@ -7459,6 +7519,14 @@ static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) { return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { + return CmpLT; + } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } case 32: if (op1->data.x_f32 > op2->data.x_f32) { return CmpGT; @@ -7496,6 +7564,17 @@ static Cmp float_cmp_zero(ConstExprValue *op) { return bigfloat_cmp_zero(&op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { + case 16: + { + const float16_t zero = zig_double_to_f16(0); + if (f16_lt(op->data.x_f16, zero)) { + return CmpLT; + } else if (f16_lt(zero, op->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } + } case 32: if (op->data.x_f32 < 0.0) { return CmpLT; @@ -7537,6 +7616,9 @@ static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; return; @@ -7561,6 +7643,9 @@ static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; return; @@ -7585,6 +7670,9 @@ static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; return; @@ -7609,6 +7697,9 @@ static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; return; @@ -7633,6 +7724,19 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + { + double a = zig_f16_to_double(op1->data.x_f16); + double b = zig_f16_to_double(op2->data.x_f16); + double c = a / b; + if (c >= 0.0) { + c = floor(c); + } else { + c = ceil(c); + } + out_val->data.x_f16 = zig_double_to_f16(c); + return; + } case 32: out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; if (out_val->data.x_f32 >= 0.0) { @@ -7668,6 +7772,10 @@ static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstE bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); + return; case 32: out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); return; @@ -7693,6 +7801,9 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); return; @@ -7710,6 +7821,16 @@ static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal } } +// c = a - b * trunc(a / b) +static float16_t zig_f16_mod(float16_t a, float16_t b) { + float16_t c; + c = f16_div(a, b); + c = f16_roundToInt(c, softfloat_round_min, true); + c = f16_mul(b, c); + c = f16_sub(a, c); + return c; +} + // c = a - b * trunc(a / b) static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { f128M_div(a, b, c); @@ -7725,6 +7846,9 @@ static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprVal bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); + return; case 32: out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); return; @@ -7748,6 +7872,12 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); } else if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { + case 16: + { + const float16_t zero = zig_double_to_f16(0); + out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); + return; + } case 32: out_val->data.x_f32 = -op->data.x_f32; return; @@ -7770,6 +7900,9 @@ static void float_negate(ConstExprValue *out_val, ConstExprValue *op) { void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { if (op->type->id == TypeTableEntryIdFloat) { switch (op->type->data.floating.bit_count) { + case 16: + memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian + return; case 32: memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian return; @@ -7790,6 +7923,9 @@ void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) { void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) { if (val->type->id == TypeTableEntryIdFloat) { switch (val->type->data.floating.bit_count) { + case 16: + memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian + return; case 32: memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian return; @@ -8817,6 +8953,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ if (other_val->type->id == TypeTableEntryIdComptimeFloat) { assert(new_type->id == TypeTableEntryIdFloat); switch (new_type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); + break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); break; @@ -8847,6 +8986,9 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ BigFloat bigfloat; bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); switch (new_type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = bigfloat_to_f16(&bigfloat); + break; case 32: const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); break; @@ -20104,6 +20246,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); } else if (float_type->id == TypeTableEntryIdFloat) { switch (float_type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_sqrt(val->data.x_f16); + break; case 32: out_val->data.x_f32 = sqrtf(val->data.x_f32); break; @@ -20124,7 +20269,9 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction } assert(float_type->id == TypeTableEntryIdFloat); - if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) { + if (float_type->data.floating.bit_count != 16 && + float_type->data.floating.bit_count != 32 && + float_type->data.floating.bit_count != 64) { ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); return ira->codegen->builtin_types.entry_invalid; } diff --git a/src/util.hpp b/src/util.hpp index 52baab7ace..b0402137bd 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -31,6 +31,8 @@ #endif +#include "softfloat.hpp" + #define BREAKPOINT __asm("int $0x03") ATTRIBUTE_COLD @@ -165,4 +167,21 @@ static inline uint8_t log2_u64(uint64_t x) { return (63 - clzll(x)); } +static inline float16_t zig_double_to_f16(double x) { + float64_t y; + static_assert(sizeof(x) == sizeof(y), ""); + memcpy(&y, &x, sizeof(x)); + return f64_to_f16(y); +} + + +// Return value is safe to coerce to float even when |x| is NaN or Infinity. +static inline double zig_f16_to_double(float16_t x) { + float64_t y = f16_to_f64(x); + double z; + static_assert(sizeof(y) == sizeof(z), ""); + memcpy(&z, &y, sizeof(y)); + return z; +} + #endif diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig index 6fa8cf4654..099e27b74a 100644 --- a/std/special/compiler_rt/extendXfYf2.zig +++ b/std/special/compiler_rt/extendXfYf2.zig @@ -10,9 +10,13 @@ pub extern fn __extendsftf2(a: f32) f128 { return extendXfYf2(f128, f32, a); } +pub extern fn __extendhfsf2(a: u16) f32 { + return extendXfYf2(f32, f16, @bitCast(f16, a)); +} + const CHAR_BIT = 8; -pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { +inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); const srcSigBits = std.math.floatMantissaBits(src_t); @@ -22,22 +26,22 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { // Various constants whose values follow from the type parameters. // Any reasonable optimizer will fold and propagate all of these. - const srcBits: i32 = @sizeOf(src_t) * CHAR_BIT; - const srcExpBits: i32 = srcBits - srcSigBits - 1; - const srcInfExp: i32 = (1 << srcExpBits) - 1; - const srcExpBias: i32 = srcInfExp >> 1; + const srcBits = @sizeOf(src_t) * CHAR_BIT; + const srcExpBits = srcBits - srcSigBits - 1; + const srcInfExp = (1 << srcExpBits) - 1; + const srcExpBias = srcInfExp >> 1; - const srcMinNormal: src_rep_t = src_rep_t(1) << srcSigBits; - const srcInfinity: src_rep_t = src_rep_t(@bitCast(u32, srcInfExp)) << srcSigBits; - const srcSignMask: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits +% srcExpBits); - const srcAbsMask: src_rep_t = srcSignMask -% 1; - const srcQNaN: src_rep_t = src_rep_t(1) << @intCast(SrcShift, srcSigBits -% 1); - const srcNaNCode: src_rep_t = srcQNaN -% 1; + const srcMinNormal = 1 << srcSigBits; + const srcInfinity = srcInfExp << srcSigBits; + const srcSignMask = 1 << (srcSigBits + srcExpBits); + const srcAbsMask = srcSignMask - 1; + const srcQNaN = 1 << (srcSigBits - 1); + const srcNaNCode = srcQNaN - 1; - const dstBits: i32 = @sizeOf(dst_t) * CHAR_BIT; - const dstExpBits: i32 = dstBits - dstSigBits - 1; - const dstInfExp: i32 = (1 << dstExpBits) - 1; - const dstExpBias: i32 = dstInfExp >> 1; + const dstBits = @sizeOf(dst_t) * CHAR_BIT; + const dstExpBits = dstBits - dstSigBits - 1; + const dstInfExp = (1 << dstExpBits) - 1; + const dstExpBias = dstInfExp >> 1; const dstMinNormal: dst_rep_t = dst_rep_t(1) << dstSigBits; @@ -47,38 +51,36 @@ pub fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { const sign: src_rep_t = aRep & srcSignMask; var absResult: dst_rep_t = undefined; - // If @sizeOf(src_rep_t) < @sizeOf(int), the subtraction result is promoted - // to (signed) int. To avoid that, explicitly cast to src_rep_t. - if ((src_rep_t)(aAbs -% srcMinNormal) < srcInfinity -% srcMinNormal) { + if (aAbs -% srcMinNormal < srcInfinity - srcMinNormal) { // a is a normal number. // Extend to the destination type by shifting the significand and // exponent into the proper position and rebiasing the exponent. - absResult = dst_rep_t(aAbs) << (dstSigBits -% srcSigBits); - absResult += dst_rep_t(@bitCast(u32, dstExpBias -% srcExpBias)) << dstSigBits; + absResult = dst_rep_t(aAbs) << (dstSigBits - srcSigBits); + absResult += (dstExpBias - srcExpBias) << dstSigBits; } else if (aAbs >= srcInfinity) { // a is NaN or infinity. // Conjure the result by beginning with infinity, then setting the qNaN // bit (if needed) and right-aligning the rest of the trailing NaN // payload field. - absResult = dst_rep_t(@bitCast(u32, dstInfExp)) << dstSigBits; - absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits); - absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); + absResult = dstInfExp << dstSigBits; + absResult |= dst_rep_t(aAbs & srcQNaN) << (dstSigBits - srcSigBits); + absResult |= dst_rep_t(aAbs & srcNaNCode) << (dstSigBits - srcSigBits); } else if (aAbs != 0) { // a is denormal. // renormalize the significand and clear the leading bit, then insert // the correct adjusted exponent in the destination type. - const scale: i32 = @clz(aAbs) - @clz(srcMinNormal); + const scale: u32 = @clz(aAbs) - @clz(src_rep_t(srcMinNormal)); absResult = dst_rep_t(aAbs) << @intCast(DstShift, dstSigBits - srcSigBits + scale); absResult ^= dstMinNormal; - const resultExponent: i32 = dstExpBias - srcExpBias - scale + 1; - absResult |= dst_rep_t(@bitCast(u32, resultExponent)) << @intCast(DstShift, dstSigBits); + const resultExponent: u32 = dstExpBias - srcExpBias - scale + 1; + absResult |= @intCast(dst_rep_t, resultExponent) << dstSigBits; } else { // a is zero. absResult = 0; } // Apply the signbit to (dst_t)abs(a). - const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << @intCast(DstShift, dstBits - srcBits); + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | dst_rep_t(sign) << (dstBits - srcBits); return @bitCast(dst_t, result); } diff --git a/std/special/compiler_rt/extendXfYf2_test.zig b/std/special/compiler_rt/extendXfYf2_test.zig index 84fb410fbb..0168de12a5 100644 --- a/std/special/compiler_rt/extendXfYf2_test.zig +++ b/std/special/compiler_rt/extendXfYf2_test.zig @@ -1,4 +1,5 @@ const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; +const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; const assert = @import("std").debug.assert; @@ -24,6 +25,22 @@ fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { @panic("__extenddftf2 test failure"); } +fn test__extendhfsf2(a: u16, expected: u32) void { + const x = __extendhfsf2(a); + const rep = @bitCast(u32, x); + + if (rep == expected) { + if (rep & 0x7fffffff > 0x7f800000) { + return; // NaN is always unequal. + } + if (x == @bitCast(f32, expected)) { + return; + } + } + + @panic("__extendhfsf2 test failure"); +} + fn test__extendsftf2(a: f32, expectedHi: u64, expectedLo: u64) void { const x = __extendsftf2(a); @@ -68,6 +85,35 @@ test "extenddftf2" { test__extenddftf2(0x1.edcba987654321fp-45, 0x3fd2edcba9876543, 0x2000000000000000); } +test "extendhfsf2" { + test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN + test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN + + test__extendhfsf2(0, 0); // 0 + test__extendhfsf2(0x8000, 0x80000000); // -0 + + test__extendhfsf2(0x7c00, 0x7f800000); // inf + test__extendhfsf2(0xfc00, 0xff800000); // -inf + + test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24 + test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24 + + test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24 + test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24 + + test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14 + test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14 + + test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504 + test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504 + + test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10 + test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10 + + test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3 + test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3 +} + test "extendsftf2" { // qNaN test__extendsftf2(makeQNaN32(), 0x7fff800000000000, 0x0); diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index c96e1587f8..fda8d9d8af 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -15,6 +15,8 @@ comptime { @export("__lttf2", @import("comparetf2.zig").__letf2, linkage); @export("__netf2", @import("comparetf2.zig").__letf2, linkage); @export("__gttf2", @import("comparetf2.zig").__getf2, linkage); + @export("__gnu_h2f_ieee", @import("extendXfYf2.zig").__extendhfsf2, linkage); + @export("__gnu_f2h_ieee", @import("truncXfYf2.zig").__truncsfhf2, linkage); } @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); @@ -22,6 +24,9 @@ comptime { @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); + @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); + + @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig new file mode 100644 index 0000000000..f08c6ae34f --- /dev/null +++ b/std/special/compiler_rt/truncXfYf2.zig @@ -0,0 +1,111 @@ +const std = @import("std"); + +pub extern fn __truncsfhf2(a: f32) u16 { + return @bitCast(u16, truncXfYf2(f16, f32, a)); +} + +const CHAR_BIT = 8; + +inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { + const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits); + const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits); + const srcSigBits = std.math.floatMantissaBits(src_t); + const dstSigBits = std.math.floatMantissaBits(dst_t); + const SrcShift = std.math.Log2Int(src_rep_t); + const DstShift = std.math.Log2Int(dst_rep_t); + + // Various constants whose values follow from the type parameters. + // Any reasonable optimizer will fold and propagate all of these. + const srcBits = @sizeOf(src_t) * CHAR_BIT; + const srcExpBits = srcBits - srcSigBits - 1; + const srcInfExp = (1 << srcExpBits) - 1; + const srcExpBias = srcInfExp >> 1; + + const srcMinNormal = 1 << srcSigBits; + const srcSignificandMask = srcMinNormal - 1; + const srcInfinity = srcInfExp << srcSigBits; + const srcSignMask = 1 << (srcSigBits + srcExpBits); + const srcAbsMask = srcSignMask - 1; + const roundMask = (1 << (srcSigBits - dstSigBits)) - 1; + const halfway = 1 << (srcSigBits - dstSigBits - 1); + const srcQNaN = 1 << (srcSigBits - 1); + const srcNaNCode = srcQNaN - 1; + + const dstBits = @sizeOf(dst_t) * CHAR_BIT; + const dstExpBits = dstBits - dstSigBits - 1; + const dstInfExp = (1 << dstExpBits) - 1; + const dstExpBias = dstInfExp >> 1; + + const underflowExponent = srcExpBias + 1 - dstExpBias; + const overflowExponent = srcExpBias + dstInfExp - dstExpBias; + const underflow = underflowExponent << srcSigBits; + const overflow = overflowExponent << srcSigBits; + + const dstQNaN = 1 << (dstSigBits - 1); + const dstNaNCode = dstQNaN - 1; + + // Break a into a sign and representation of the absolute value + const aRep: src_rep_t = @bitCast(src_rep_t, a); + const aAbs: src_rep_t = aRep & srcAbsMask; + const sign: src_rep_t = aRep & srcSignMask; + var absResult: dst_rep_t = undefined; + + if (aAbs -% underflow < aAbs -% overflow) { + // The exponent of a is within the range of normal numbers in the + // destination format. We can convert by simply right-shifting with + // rounding and adjusting the exponent. + absResult = @truncate(dst_rep_t, aAbs >> (srcSigBits - dstSigBits)); + absResult -%= dst_rep_t(srcExpBias - dstExpBias) << dstSigBits; + + const roundBits: src_rep_t = aAbs & roundMask; + if (roundBits > halfway) { + // Round to nearest + absResult += 1; + } else if (roundBits == halfway) { + // Ties to even + absResult += absResult & 1; + } + } else if (aAbs > srcInfinity) { + // a is NaN. + // Conjure the result by beginning with infinity, setting the qNaN + // bit and inserting the (truncated) trailing NaN field. + absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits; + absResult |= dstQNaN; + absResult |= @intCast(dst_rep_t, ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode); + } else if (aAbs >= overflow) { + // a overflows to infinity. + absResult = @intCast(dst_rep_t, dstInfExp) << dstSigBits; + } else { + // a underflows on conversion to the destination type or is an exact + // zero. The result may be a denormal or zero. Extract the exponent + // to get the shift amount for the denormalization. + const aExp: u32 = aAbs >> srcSigBits; + const shift: u32 = srcExpBias - dstExpBias - aExp + 1; + + const significand: src_rep_t = (aRep & srcSignificandMask) | srcMinNormal; + + // Right shift by the denormalization amount with sticky. + if (shift > srcSigBits) { + absResult = 0; + } else { + const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); + const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; + absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); + const roundBits: src_rep_t = denormalizedSignificand & roundMask; + if (roundBits > halfway) { + // Round to nearest + absResult += 1; + } else if (roundBits == halfway) { + // Ties to even + absResult += absResult & 1; + } + } + } + + const result: dst_rep_t align(@alignOf(dst_t)) = absResult | @truncate(dst_rep_t, sign >> @intCast(SrcShift, srcBits - dstBits)); + return @bitCast(dst_t, result); +} + +test "import truncXfYf2" { + _ = @import("truncXfYf2_test.zig"); +} diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig new file mode 100644 index 0000000000..e4dae7b5b0 --- /dev/null +++ b/std/special/compiler_rt/truncXfYf2_test.zig @@ -0,0 +1,64 @@ +const __truncsfhf2 = @import("truncXfYf2.zig").__truncsfhf2; + +fn test__truncsfhf2(a: u32, expected: u16) void { + const actual = __truncsfhf2(@bitCast(f32, a)); + + if (actual == expected) { + return; + } + + @panic("__truncsfhf2 test failure"); +} + +test "truncsfhf2" { + test__truncsfhf2(0x7fc00000, 0x7e00); // qNaN + test__truncsfhf2(0x7fe00000, 0x7f00); // sNaN + + test__truncsfhf2(0, 0); // 0 + test__truncsfhf2(0x80000000, 0x8000); // -0 + + test__truncsfhf2(0x7f800000, 0x7c00); // inf + test__truncsfhf2(0xff800000, 0xfc00); // -inf + + test__truncsfhf2(0x477ff000, 0x7c00); // 65520 -> inf + test__truncsfhf2(0xc77ff000, 0xfc00); // -65520 -> -inf + + test__truncsfhf2(0x71cc3892, 0x7c00); // 0x1.987124876876324p+100 -> inf + test__truncsfhf2(0xf1cc3892, 0xfc00); // -0x1.987124876876324p+100 -> -inf + + test__truncsfhf2(0x38800000, 0x0400); // normal (min), 2**-14 + test__truncsfhf2(0xb8800000, 0x8400); // normal (min), -2**-14 + + test__truncsfhf2(0x477fe000, 0x7bff); // normal (max), 65504 + test__truncsfhf2(0xc77fe000, 0xfbff); // normal (max), -65504 + + test__truncsfhf2(0x477fe100, 0x7bff); // normal, 65505 -> 65504 + test__truncsfhf2(0xc77fe100, 0xfbff); // normal, -65505 -> -65504 + + test__truncsfhf2(0x477fef00, 0x7bff); // normal, 65519 -> 65504 + test__truncsfhf2(0xc77fef00, 0xfbff); // normal, -65519 -> -65504 + + test__truncsfhf2(0x3f802000, 0x3c01); // normal, 1 + 2**-10 + test__truncsfhf2(0xbf802000, 0xbc01); // normal, -1 - 2**-10 + + test__truncsfhf2(0x3eaaa000, 0x3555); // normal, approx. 1/3 + test__truncsfhf2(0xbeaaa000, 0xb555); // normal, approx. -1/3 + + test__truncsfhf2(0x40490fdb, 0x4248); // normal, 3.1415926535 + test__truncsfhf2(0xc0490fdb, 0xc248); // normal, -3.1415926535 + + test__truncsfhf2(0x45cc3892, 0x6e62); // normal, 0x1.987124876876324p+12 + + test__truncsfhf2(0x3f800000, 0x3c00); // normal, 1 + test__truncsfhf2(0x38800000, 0x0400); // normal, 0x1.0p-14 + + test__truncsfhf2(0x33800000, 0x0001); // denormal (min), 2**-24 + test__truncsfhf2(0xb3800000, 0x8001); // denormal (min), -2**-24 + + test__truncsfhf2(0x387fc000, 0x03ff); // denormal (max), 2**-14 - 2**-24 + test__truncsfhf2(0xb87fc000, 0x83ff); // denormal (max), -2**-14 + 2**-24 + + test__truncsfhf2(0x35800000, 0x0010); // denormal, 0x1.0p-20 + test__truncsfhf2(0x33280000, 0x0001); // denormal, 0x1.5p-25 -> 0x1.0p-24 + test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero +} diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 4209d87c1a..5688d90e11 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -350,13 +350,16 @@ fn testFloatToInts() void { assert(x == 10000); const y = @floatToInt(i32, f32(1e4)); assert(y == 10000); - expectFloatToInt(u8, 255.1, 255); - expectFloatToInt(i8, 127.2, 127); - expectFloatToInt(i8, -128.2, -128); + expectFloatToInt(f16, 255.1, u8, 255); + expectFloatToInt(f16, 127.2, i8, 127); + expectFloatToInt(f16, -128.2, i8, -128); + expectFloatToInt(f32, 255.1, u8, 255); + expectFloatToInt(f32, 127.2, i8, 127); + expectFloatToInt(f32, -128.2, i8, -128); } -fn expectFloatToInt(comptime T: type, f: f32, i: T) void { - assert(@floatToInt(T, f) == i); +fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { + assert(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -418,6 +421,16 @@ test "@intCast comptime_int" { } test "@floatCast comptime_int and comptime_float" { + { + const result = @floatCast(f16, 1234); + assert(@typeOf(result) == f16); + assert(result == 1234.0); + } + { + const result = @floatCast(f16, 1234.0); + assert(@typeOf(result) == f16); + assert(result == 1234.0); + } { const result = @floatCast(f32, 1234); assert(@typeOf(result) == f32); @@ -431,6 +444,11 @@ test "@floatCast comptime_int and comptime_float" { } test "comptime_int @intToFloat" { + { + const result = @intToFloat(f16, 1234); + assert(@typeOf(result) == f16); + assert(result == 1234.0); + } { const result = @intToFloat(f32, 1234); assert(@typeOf(result) == f32); diff --git a/test/cases/math.zig b/test/cases/math.zig index 08388d3df8..1807e5a1b0 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -6,15 +6,20 @@ test "division" { } fn testDivision() void { assert(div(u32, 13, 3) == 4); + assert(div(f16, 1.0, 2.0) == 0.5); assert(div(f32, 1.0, 2.0) == 0.5); assert(divExact(u32, 55, 11) == 5); assert(divExact(i32, -55, 11) == -5); + assert(divExact(f16, 55.0, 11.0) == 5.0); + assert(divExact(f16, -55.0, 11.0) == -5.0); assert(divExact(f32, 55.0, 11.0) == 5.0); assert(divExact(f32, -55.0, 11.0) == -5.0); assert(divFloor(i32, 5, 3) == 1); assert(divFloor(i32, -5, 3) == -2); + assert(divFloor(f16, 5.0, 3.0) == 1.0); + assert(divFloor(f16, -5.0, 3.0) == -2.0); assert(divFloor(f32, 5.0, 3.0) == 1.0); assert(divFloor(f32, -5.0, 3.0) == -2.0); assert(divFloor(i32, -0x80000000, -2) == 0x40000000); @@ -24,6 +29,8 @@ fn testDivision() void { assert(divTrunc(i32, 5, 3) == 1); assert(divTrunc(i32, -5, 3) == -1); + assert(divTrunc(f16, 5.0, 3.0) == 1.0); + assert(divTrunc(f16, -5.0, 3.0) == -1.0); assert(divTrunc(f32, 5.0, 3.0) == 1.0); assert(divTrunc(f32, -5.0, 3.0) == -1.0); @@ -435,10 +442,11 @@ test "comptime float rem int" { } test "remainder division" { + comptime remdiv(f16); comptime remdiv(f32); comptime remdiv(f64); comptime remdiv(f128); - remdiv(f32); + remdiv(f16); remdiv(f64); remdiv(f128); } @@ -453,6 +461,8 @@ test "@sqrt" { comptime testSqrt(f64, 12.0); testSqrt(f32, 13.0); comptime testSqrt(f32, 13.0); + testSqrt(f16, 13.0); + comptime testSqrt(f16, 13.0); const x = 14.0; const y = x * x; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index d539f79a57..0f181a7b4e 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -53,6 +53,7 @@ test "@IntType builtin" { } test "floating point primitive bit counts" { + assert(f16.bit_count == 16); assert(f32.bit_count == 32); assert(f64.bit_count == 64); } -- cgit v1.2.3 From 440c1d52b4053c40c58c05116c8f6d9da8e35eed Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jun 2018 16:20:04 +0200 Subject: simplify comptime floating-point @divTrunc Replace a conditional ceil/floor call with an unconditional trunc call. --- src/ir.cpp | 29 +++++------------------------ test/cases/math.zig | 2 ++ 2 files changed, 7 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 694f912145..6e424980f8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7725,33 +7725,14 @@ static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstE } else if (op1->type->id == TypeTableEntryIdFloat) { switch (op1->type->data.floating.bit_count) { case 16: - { - double a = zig_f16_to_double(op1->data.x_f16); - double b = zig_f16_to_double(op2->data.x_f16); - double c = a / b; - if (c >= 0.0) { - c = floor(c); - } else { - c = ceil(c); - } - out_val->data.x_f16 = zig_double_to_f16(c); - return; - } + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false); + return; case 32: - out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; - if (out_val->data.x_f32 >= 0.0) { - out_val->data.x_f32 = floorf(out_val->data.x_f32); - } else { - out_val->data.x_f32 = ceilf(out_val->data.x_f32); - } + out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32); return; case 64: - out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; - if (out_val->data.x_f64 >= 0.0) { - out_val->data.x_f64 = floor(out_val->data.x_f64); - } else { - out_val->data.x_f64 = ceil(out_val->data.x_f64); - } + out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64); return; case 128: f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); diff --git a/test/cases/math.zig b/test/cases/math.zig index 1807e5a1b0..5931c5de31 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -33,6 +33,8 @@ fn testDivision() void { assert(divTrunc(f16, -5.0, 3.0) == -1.0); assert(divTrunc(f32, 5.0, 3.0) == 1.0); assert(divTrunc(f32, -5.0, 3.0) == -1.0); + assert(divTrunc(f64, 5.0, 3.0) == 1.0); + assert(divTrunc(f64, -5.0, 3.0) == -1.0); comptime { assert( -- cgit v1.2.3 From 19961c50e4db10fc4ada428928a7f5d1a2966da6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Jun 2018 13:15:55 -0400 Subject: fix comptime @tagName crashing sometimes closes #1118 --- src/analyze.cpp | 1 + src/ir.cpp | 3 +++ test/cases/eval.zig | 5 +++++ test/cases/widening.zig | 9 +++++---- 4 files changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 25cc1c79d0..d5e69de1eb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3728,6 +3728,7 @@ TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt } TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag) { + assert(enum_type->data.enumeration.zero_bits_known); for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *field = &enum_type->data.enumeration.fields[i]; if (bigint_cmp(&field->value, tag) == CmpEQ) { diff --git a/src/ir.cpp b/src/ir.cpp index 6e424980f8..9ba01d1411 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16053,6 +16053,9 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn assert(target->value.type->id == TypeTableEntryIdEnum); if (instr_is_comptime(target)) { + type_ensure_zero_bits_known(ira->codegen, target->value.type); + if (type_is_invalid(target->value.type)) + return ira->codegen->builtin_types.entry_invalid; TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint); ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 756ffe339a..83d2e80176 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -637,3 +637,8 @@ test "call method with comptime pass-by-non-copying-value self parameter" { var b = s.b(); assert(b == 2); } + +test "@tagName of @typeId" { + const str = @tagName(@typeId(u8)); + assert(std.mem.eql(u8, str, "Int")); +} diff --git a/test/cases/widening.zig b/test/cases/widening.zig index 18c12806d3..cf6ab4ca0f 100644 --- a/test/cases/widening.zig +++ b/test/cases/widening.zig @@ -19,8 +19,9 @@ test "implicit unsigned integer to signed integer" { } test "float widening" { - var a: f32 = 12.34; - var b: f64 = a; - var c: f128 = b; - assert(c == a); + var a: f16 = 12.34; + var b: f32 = a; + var c: f64 = b; + var d: f128 = c; + assert(d == a); } -- cgit v1.2.3 From 2fa588e81d60cfe319446bd0483c6bf296f40c40 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Jun 2018 18:45:21 -0400 Subject: fix coroutine accessing freed memory closes #1164 --- src/analyze.cpp | 2 +- src/ir.cpp | 17 ++++++++++++++--- test/cases/coroutines.zig | 41 ++++++++++++++++++++++++++++++++--------- 3 files changed, 47 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index d5e69de1eb..3c81d9ff9a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT return; } case ConstPtrSpecialHardCodedAddr: - buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name), + buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name), const_val->data.x_ptr.data.hard_coded_addr.addr); return; case ConstPtrSpecialDiscard: diff --git a/src/ir.cpp b/src/ir.cpp index 9ba01d1411..98ed53d839 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr); ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr); } + // Before we destroy the coroutine frame, we need to load the target promise into + // a register or local variable which does not get spilled into the frame, + // otherwise llvm tries to access memory inside the destroyed frame. + IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + irb->exec->await_handle_var_ptr, false); + IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block); @@ -7126,6 +7132,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec incoming_values[1] = const_bool_true; IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); + IrBasicBlock **merge_incoming_blocks = allocate(2); + IrInstruction **merge_incoming_values = allocate(2); + merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block; + merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node); + merge_incoming_blocks[1] = irb->exec->coro_normal_final; + merge_incoming_values[1] = await_handle_in_block; + IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values); + Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node, ImplicitAllocatorIdLocalVar); @@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, resume_block); - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, - irb->exec->await_handle_var_ptr, false); - IrInstruction *awaiter_handle = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_coro_resume(irb, scope, node, awaiter_handle); ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false); } diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 4d2aa54a69..b3899b306b 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -5,7 +5,10 @@ const assert = std.debug.assert; var x: i32 = 1; test "create a coroutine and cancel it" { - const p = try async simpleAsyncFn(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> simpleAsyncFn(); comptime assert(@typeOf(p) == promise->void); cancel p; assert(x == 2); @@ -17,8 +20,11 @@ async fn simpleAsyncFn() void { } test "coroutine suspend, resume, cancel" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + seq('a'); - const p = try async testAsyncSeq(); + const p = try async<&da.allocator> testAsyncSeq(); seq('c'); resume p; seq('f'); @@ -43,7 +49,10 @@ fn seq(c: u8) void { } test "coroutine suspend with block" { - const p = try async testSuspendBlock(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const p = try async<&da.allocator> testSuspendBlock(); std.debug.assert(!result); resume a_promise; std.debug.assert(result); @@ -64,8 +73,11 @@ var await_a_promise: promise = undefined; var await_final_result: i32 = 0; test "coroutine await" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + await_seq('a'); - const p = async await_amain() catch unreachable; + const p = async<&da.allocator> await_amain() catch unreachable; await_seq('f'); resume await_a_promise; await_seq('i'); @@ -100,8 +112,11 @@ fn await_seq(c: u8) void { var early_final_result: i32 = 0; test "coroutine await early return" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + early_seq('a'); - const p = async early_amain() catch unreachable; + const p = async<&da.allocator> early_amain() catch unreachable; early_seq('f'); assert(early_final_result == 1234); assert(std.mem.eql(u8, early_points, "abcdef")); @@ -146,7 +161,9 @@ test "async function with dot syntax" { suspend; } }; - const p = try async S.foo(); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = try async<&da.allocator> S.foo(); cancel p; assert(S.y == 2); } @@ -157,7 +174,9 @@ test "async fn pointer in a struct field" { bar: async<*std.mem.Allocator> fn (*i32) void, }; var foo = Foo{ .bar = simpleAsyncFn2 }; - const p = (async foo.bar(&data)) catch unreachable; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; assert(data == 2); cancel p; assert(data == 4); @@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { } test "async fn with inferred error set" { - const p = (async failing()) catch unreachable; + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p = (async<&da.allocator> failing()) catch unreachable; resume p; cancel p; } @@ -181,7 +202,9 @@ async fn failing() !void { test "error return trace across suspend points - early return" { const p = nonFailing(); resume p; - const p2 = try async printTrace(p); + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + const p2 = try async<&da.allocator> printTrace(p); cancel p2; } -- cgit v1.2.3 From 4a35d7eeebec3f345e2482bc189f07c19dcf6f8b Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 28 Jun 2018 20:12:03 +1200 Subject: Correct hex-float parsing Unblocks #495. --- src/tokenizer.cpp | 11 +++++++++-- test/cases/math.zig | 8 ++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 2950b4eb49..f7f41af8a6 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -357,12 +357,19 @@ static void end_float_token(Tokenize *t) { // Mask the sign bit to 0 since always non-negative lex const uint64_t exp_mask = 0xffffull << exp_shift; - if (shift >= 64) { + // must be special-cased to avoid undefined behavior on shift == 64 + if (shift == 128) { + f_bits.repr[0] = 0; + f_bits.repr[1] = sig_bits[0]; + } else if (shift == 0) { + f_bits.repr[0] = sig_bits[0]; + f_bits.repr[1] = sig_bits[1]; + } else if (shift >= 64) { f_bits.repr[0] = 0; f_bits.repr[1] = sig_bits[0] << (shift - 64); } else { f_bits.repr[0] = sig_bits[0] << shift; - f_bits.repr[1] = ((sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift))); + f_bits.repr[1] = (sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift)); } f_bits.repr[1] &= ~exp_mask; diff --git a/test/cases/math.zig b/test/cases/math.zig index 5931c5de31..195ada15dd 100644 --- a/test/cases/math.zig +++ b/test/cases/math.zig @@ -296,6 +296,14 @@ test "quad hex float literal parsing in range" { const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; } +test "quad hex float literal parsing accurate" { + const a: f128 = 0x1.1111222233334444555566667777p+0; + + // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. + const expected: u128 = 0x3fff1111222233334444555566667777; + assert(@bitCast(u128, a) == expected); +} + test "hex float literal within range" { const a = 0x1.0p16383; const b = 0x0.1p16387; -- cgit v1.2.3 From 3ec38b249446d1a51391e263fbb8303af52e6751 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Thu, 28 Jun 2018 10:34:37 +0200 Subject: Implement const_values_equal for array type * This allows arrays to be passed by value at comptime --- src/analyze.cpp | 15 +++++++++++++-- test/cases/array.zig | 8 ++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 5160a19e81..e9b74a9c26 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5458,8 +5458,19 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdPointer: case TypeTableEntryIdFn: return const_values_equal_ptr(a, b); - case TypeTableEntryIdArray: - zig_panic("TODO"); + case TypeTableEntryIdArray: { + assert(a->type->data.array.len == b->type->data.array.len); + size_t len = a->type->data.array.len; + ConstExprValue *a_elems = a->data.x_array.s_none.elements; + ConstExprValue *b_elems = b->data.x_array.s_none.elements; + + for (size_t i = 0; i < len; ++i) { + if (!const_values_equal(&a_elems[i], &b_elems[i])) + return false; + } + + return true; + } case TypeTableEntryIdStruct: for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { ConstExprValue *field_a = &a->data.x_struct.fields[i]; diff --git a/test/cases/array.zig b/test/cases/array.zig index b481261b4f..b72491bcc0 100644 --- a/test/cases/array.zig +++ b/test/cases/array.zig @@ -152,3 +152,11 @@ fn testImplicitCastSingleItemPtr() void { slice[0] += 1; assert(byte == 101); } + +fn testArrayByValAtComptime(b: [2]u8) u8 { return b[0]; } + +test "comptime evalutating function that takes array by value" { + const arr = []u8{0,1}; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} -- cgit v1.2.3 From b1128b18d5395d85f1c483d8b35e33c57be80722 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 29 Jun 2018 08:41:16 +0200 Subject: Assert that array is not ConstArraySpecialUndef in const_values_equal --- src/analyze.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index e9b74a9c26..b3a302a1d4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5460,6 +5460,9 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return const_values_equal_ptr(a, b); case TypeTableEntryIdArray: { assert(a->type->data.array.len == b->type->data.array.len); + assert(a->data.x_array.special != ConstArraySpecialUndef); + assert(b->data.x_array.special != ConstArraySpecialUndef); + size_t len = a->type->data.array.len; ConstExprValue *a_elems = a->data.x_array.s_none.elements; ConstExprValue *b_elems = b->data.x_array.s_none.elements; -- cgit v1.2.3 From 4c3f27ce1ea17b5236a022971ebace73a02b7c2b Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 29 Jun 2018 10:21:43 +0200 Subject: ir_resolve_const now checks recursivly for undef values --- src/analyze.cpp | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ src/analyze.hpp | 1 + src/ir.cpp | 11 +++- test/compile_errors.zig | 15 ++++++ 4 files changed, 160 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index b3a302a1d4..068ea48c0a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5288,6 +5288,141 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_ return const_val; } +bool contains_comptime_undefined_value(ConstExprValue *value) { + assert(value->special != ConstValSpecialRuntime); + if (value->special == ConstValSpecialUndef) + return true; + + switch (value->type->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + + case TypeTableEntryIdPointer: { + ConstPtrValue *ptr = &value->data.x_ptr; + if (ptr->mut == ConstPtrMutRuntimeVar) + return false; + + switch (ptr->special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t index = ptr->data.base_array.elem_index; + ConstExprValue *arr = ptr->data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); + } + case ConstPtrSpecialBaseStruct: { + size_t index = ptr->data.base_struct.field_index; + ConstExprValue *str = ptr->data.base_struct.struct_val; + if (str->special == ConstValSpecialUndef) + return true; + + return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); + } + case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + return false; + } + } + case TypeTableEntryIdArray: { + ConstArrayValue *arr = &value->data.x_array; + if (arr->special == ConstArraySpecialUndef) + return true; + + for (size_t i = 0; i < value->type->data.array.len; ++i) { + if (contains_comptime_undefined_value(&arr->s_none.elements[i])) + return true; + } + return false; + } + case TypeTableEntryIdStruct: { + ConstStructValue *str = &value->data.x_struct; + if (value->type->data.structure.is_slice) { + ConstExprValue *len = &str->fields[slice_len_index]; + ConstExprValue *ptr = &str->fields[slice_ptr_index]; + if (len->special == ConstValSpecialUndef) + return true; + if (ptr->special == ConstValSpecialUndef) + return true; + + switch (ptr->data.x_ptr.special) { + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t offset = ptr->data.x_ptr.data.base_array.elem_index; + ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); + for (size_t i = 0; i < slice_len; ++i) { + if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) + return true; + } + + return false; + } + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialInvalid: + case ConstPtrSpecialFunction: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + } + } + + for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { + if (contains_comptime_undefined_value(&str->fields[i])) + return true; + } + return false; + } + case TypeTableEntryIdOptional: + if (value->data.x_optional == nullptr) + return false; + + return contains_comptime_undefined_value(value->data.x_optional); + case TypeTableEntryIdErrorUnion: + // TODO: Can error union error be undefined? + if (value->data.x_err_union.err != nullptr) + return false; + + return contains_comptime_undefined_value(value->data.x_err_union.payload); + case TypeTableEntryIdUnion: + return contains_comptime_undefined_value(value->data.x_union.payload); + + case TypeTableEntryIdArgTuple: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: + case TypeTableEntryIdUndefined: + case TypeTableEntryIdNull: + case TypeTableEntryIdErrorSet: + case TypeTableEntryIdEnum: + case TypeTableEntryIdFn: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdBoundFn: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdOpaque: + case TypeTableEntryIdPromise: + return false; + } + zig_unreachable(); +} void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { TypeTableEntry *wanted_type = const_val->type; diff --git a/src/analyze.hpp b/src/analyze.hpp index 88e06b2390..100f85d4d9 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -93,6 +93,7 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry); void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry); void complete_enum(CodeGen *g, TypeTableEntry *enum_type); bool ir_get_var_is_comptime(VariableTableEntry *var); +bool contains_comptime_undefined_value(ConstExprValue *value); bool const_values_equal(ConstExprValue *a, ConstExprValue *b); void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max); diff --git a/src/ir.cpp b/src/ir.cpp index c6078e755d..2cce4a5044 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9148,8 +9148,15 @@ enum UndefAllowed { static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { switch (value->value.special) { - case ConstValSpecialStatic: - return &value->value; + case ConstValSpecialStatic: { + ConstExprValue *res = &value->value; + if (undef_allowed == UndefBad && contains_comptime_undefined_value(res)) { + ir_add_error(ira, value, buf_sprintf("use of undefined value")); + return nullptr; + } + + return res; + } case ConstValSpecialRuntime: ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2247f0af96..8749f5b560 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4124,4 +4124,19 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); + + cases.add( + "Trying to pass undefined array to function taking comptime array by value", + \\fn a(comptime b: [2]u8) u8 { return b[0]; } + \\ + \\test "" { + \\ const arr: [2]u8 = undefined; + \\ _ = a(arr); + \\} + , + ".tmp_source.zig:5:11: error: use of undefined value", + ); + + + } -- cgit v1.2.3 From 58b1692182dc2f8da5b535f59e9a89cfab10a7b6 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 29 Jun 2018 11:34:38 +0200 Subject: contains_comptime_undefined_value should not follow pointers --- src/analyze.cpp | 72 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 71 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 068ea48c0a..4c200888d8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5296,41 +5296,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { switch (value->type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); - - case TypeTableEntryIdPointer: { - ConstPtrValue *ptr = &value->data.x_ptr; - if (ptr->mut == ConstPtrMutRuntimeVar) - return false; - - switch (ptr->special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t index = ptr->data.base_array.elem_index; - ConstExprValue *arr = ptr->data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); - } - case ConstPtrSpecialBaseStruct: { - size_t index = ptr->data.base_struct.field_index; - ConstExprValue *str = ptr->data.base_struct.struct_val; - if (str->special == ConstValSpecialUndef) - return true; - - return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); - } - case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - return false; - } - } case TypeTableEntryIdArray: { ConstArrayValue *arr = &value->data.x_array; if (arr->special == ConstArraySpecialUndef) @@ -5344,42 +5309,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { } case TypeTableEntryIdStruct: { ConstStructValue *str = &value->data.x_struct; - if (value->type->data.structure.is_slice) { - ConstExprValue *len = &str->fields[slice_len_index]; - ConstExprValue *ptr = &str->fields[slice_ptr_index]; - if (len->special == ConstValSpecialUndef) - return true; - if (ptr->special == ConstValSpecialUndef) - return true; - - switch (ptr->data.x_ptr.special) { - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t offset = ptr->data.x_ptr.data.base_array.elem_index; - ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); - for (size_t i = 0; i < slice_len; ++i) { - if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) - return true; - } - - return false; - } - case ConstPtrSpecialBaseStruct: - case ConstPtrSpecialInvalid: - case ConstPtrSpecialFunction: - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - } - } - for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { if (contains_comptime_undefined_value(&str->fields[i])) return true; @@ -5400,6 +5329,7 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { case TypeTableEntryIdUnion: return contains_comptime_undefined_value(value->data.x_union.payload); + case TypeTableEntryIdPointer: case TypeTableEntryIdArgTuple: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: -- cgit v1.2.3 From f1c56f7f225f2f3054abd8c9e6330a0f1e20b2a6 Mon Sep 17 00:00:00 2001 From: isaachier Date: Fri, 29 Jun 2018 14:52:25 -0400 Subject: Clarify reason implicit cast does not work for large RHS (#1168) * Clarify reason implicit cast does not work for large RHS --- src/ir.cpp | 20 ++++++++++++++++++++ test/compile_errors.zig | 12 ++++++++++++ 2 files changed, 32 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 98ed53d839..a450b6d14e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11432,6 +11432,26 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * } else { TypeTableEntry *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, op1->value.type->data.integral.bit_count - 1); + if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy && + op2->value.type->id == TypeTableEntryIdComptimeInt) { + if (!bigint_fits_in_bits(&op2->value.data.x_bigint, + shift_amt_type->data.integral.bit_count, + op2->value.data.x_bigint.is_negative)) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10); + ErrorMsg* msg = ir_add_error(ira, + &bin_op_instruction->base, + buf_sprintf("RHS of shift is too large for LHS type")); + add_error_note( + ira->codegen, + msg, + op2->source_node, + buf_sprintf("value %s cannot fit into type %s", + buf_ptr(val_buf), + buf_ptr(&shift_amt_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); if (casted_op2 == ira->codegen->invalid_instruction) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2247f0af96..cfe4a2ef5f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1677,6 +1677,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'", ); + cases.add( + "invalid shift amount error", + \\const x : u8 = 2; + \\fn f() u16 { + \\ return x << 8; + \\} + \\export fn entry() u16 { return f(); } + , + ".tmp_source.zig:3:14: error: RHS of shift is too large for LHS type", + ".tmp_source.zig:3:17: note: value 8 cannot fit into type u3", + ); + cases.add( "incompatible number literals", \\const x = 2 == 2.0; -- cgit v1.2.3 From 03f66825d6b6922da250cdb99c2996455541e0f9 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 29 Jun 2018 23:28:42 +0200 Subject: support --emit in 'test' command Support the `--emit` switch in `zig --emit asm test file.zig`. The command fails because no tests run (no executable is created) but it emits the requested file. That seems like a good tradeoff. --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index 0fe12bb0cb..a409778a78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -924,6 +924,8 @@ int main(int argc, char **argv) { codegen_print_timing_report(g, stdout); return EXIT_SUCCESS; } else if (cmd == CmdTest) { + codegen_set_emit_file_type(g, emit_file_type); + ZigTarget native; get_native_target(&native); -- cgit v1.2.3 From 616fe798c801baa5fa7238f5fc576a5090938999 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 17:35:05 +0200 Subject: Revert "contains_comptime_undefined_value should not follow pointers" This reverts commit 58b1692182dc2f8da5b535f59e9a89cfab10a7b6. --- src/analyze.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 4c200888d8..068ea48c0a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5296,6 +5296,41 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { switch (value->type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); + + case TypeTableEntryIdPointer: { + ConstPtrValue *ptr = &value->data.x_ptr; + if (ptr->mut == ConstPtrMutRuntimeVar) + return false; + + switch (ptr->special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t index = ptr->data.base_array.elem_index; + ConstExprValue *arr = ptr->data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); + } + case ConstPtrSpecialBaseStruct: { + size_t index = ptr->data.base_struct.field_index; + ConstExprValue *str = ptr->data.base_struct.struct_val; + if (str->special == ConstValSpecialUndef) + return true; + + return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); + } + case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + return false; + } + } case TypeTableEntryIdArray: { ConstArrayValue *arr = &value->data.x_array; if (arr->special == ConstArraySpecialUndef) @@ -5309,6 +5344,42 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { } case TypeTableEntryIdStruct: { ConstStructValue *str = &value->data.x_struct; + if (value->type->data.structure.is_slice) { + ConstExprValue *len = &str->fields[slice_len_index]; + ConstExprValue *ptr = &str->fields[slice_ptr_index]; + if (len->special == ConstValSpecialUndef) + return true; + if (ptr->special == ConstValSpecialUndef) + return true; + + switch (ptr->data.x_ptr.special) { + case ConstPtrSpecialRef: + return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); + case ConstPtrSpecialBaseArray: { + size_t offset = ptr->data.x_ptr.data.base_array.elem_index; + ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; + if (arr->special == ConstValSpecialUndef) + return true; + if (arr->data.x_array.special == ConstArraySpecialUndef) + return true; + + uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); + for (size_t i = 0; i < slice_len; ++i) { + if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) + return true; + } + + return false; + } + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialInvalid: + case ConstPtrSpecialFunction: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + } + } + for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { if (contains_comptime_undefined_value(&str->fields[i])) return true; @@ -5329,7 +5400,6 @@ bool contains_comptime_undefined_value(ConstExprValue *value) { case TypeTableEntryIdUnion: return contains_comptime_undefined_value(value->data.x_union.payload); - case TypeTableEntryIdPointer: case TypeTableEntryIdArgTuple: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: -- cgit v1.2.3 From 01bd5c46e177ae59f72197063c374e845eea3ff3 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 17:35:06 +0200 Subject: Revert "ir_resolve_const now checks recursivly for undef values" This reverts commit 4c3f27ce1ea17b5236a022971ebace73a02b7c2b. --- src/analyze.cpp | 135 ------------------------------------------------ src/analyze.hpp | 1 - src/ir.cpp | 11 +--- test/compile_errors.zig | 15 ------ 4 files changed, 2 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 068ea48c0a..b3a302a1d4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5288,141 +5288,6 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_ return const_val; } -bool contains_comptime_undefined_value(ConstExprValue *value) { - assert(value->special != ConstValSpecialRuntime); - if (value->special == ConstValSpecialUndef) - return true; - - switch (value->type->id) { - case TypeTableEntryIdInvalid: - zig_unreachable(); - - case TypeTableEntryIdPointer: { - ConstPtrValue *ptr = &value->data.x_ptr; - if (ptr->mut == ConstPtrMutRuntimeVar) - return false; - - switch (ptr->special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t index = ptr->data.base_array.elem_index; - ConstExprValue *arr = ptr->data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - return contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[index]); - } - case ConstPtrSpecialBaseStruct: { - size_t index = ptr->data.base_struct.field_index; - ConstExprValue *str = ptr->data.base_struct.struct_val; - if (str->special == ConstValSpecialUndef) - return true; - - return contains_comptime_undefined_value(&str->data.x_struct.fields[index]); - } - case ConstPtrSpecialFunction: // TODO: Can a fn ptr have an undefined value? - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - return false; - } - } - case TypeTableEntryIdArray: { - ConstArrayValue *arr = &value->data.x_array; - if (arr->special == ConstArraySpecialUndef) - return true; - - for (size_t i = 0; i < value->type->data.array.len; ++i) { - if (contains_comptime_undefined_value(&arr->s_none.elements[i])) - return true; - } - return false; - } - case TypeTableEntryIdStruct: { - ConstStructValue *str = &value->data.x_struct; - if (value->type->data.structure.is_slice) { - ConstExprValue *len = &str->fields[slice_len_index]; - ConstExprValue *ptr = &str->fields[slice_ptr_index]; - if (len->special == ConstValSpecialUndef) - return true; - if (ptr->special == ConstValSpecialUndef) - return true; - - switch (ptr->data.x_ptr.special) { - case ConstPtrSpecialRef: - return contains_comptime_undefined_value(ptr->data.x_ptr.data.ref.pointee); - case ConstPtrSpecialBaseArray: { - size_t offset = ptr->data.x_ptr.data.base_array.elem_index; - ConstExprValue *arr = ptr->data.x_ptr.data.base_array.array_val; - if (arr->special == ConstValSpecialUndef) - return true; - if (arr->data.x_array.special == ConstArraySpecialUndef) - return true; - - uint64_t slice_len = bigint_as_unsigned(&len->data.x_bigint); - for (size_t i = 0; i < slice_len; ++i) { - if (contains_comptime_undefined_value(&arr->data.x_array.s_none.elements[i + offset])) - return true; - } - - return false; - } - case ConstPtrSpecialBaseStruct: - case ConstPtrSpecialInvalid: - case ConstPtrSpecialFunction: - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - } - } - - for (size_t i = 0; i < value->type->data.structure.src_field_count; ++i) { - if (contains_comptime_undefined_value(&str->fields[i])) - return true; - } - return false; - } - case TypeTableEntryIdOptional: - if (value->data.x_optional == nullptr) - return false; - - return contains_comptime_undefined_value(value->data.x_optional); - case TypeTableEntryIdErrorUnion: - // TODO: Can error union error be undefined? - if (value->data.x_err_union.err != nullptr) - return false; - - return contains_comptime_undefined_value(value->data.x_err_union.payload); - case TypeTableEntryIdUnion: - return contains_comptime_undefined_value(value->data.x_union.payload); - - case TypeTableEntryIdArgTuple: - case TypeTableEntryIdVoid: - case TypeTableEntryIdBool: - case TypeTableEntryIdUnreachable: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdComptimeFloat: - case TypeTableEntryIdComptimeInt: - case TypeTableEntryIdUndefined: - case TypeTableEntryIdNull: - case TypeTableEntryIdErrorSet: - case TypeTableEntryIdEnum: - case TypeTableEntryIdFn: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBlock: - case TypeTableEntryIdBoundFn: - case TypeTableEntryIdMetaType: - case TypeTableEntryIdOpaque: - case TypeTableEntryIdPromise: - return false; - } - zig_unreachable(); -} void init_const_undefined(CodeGen *g, ConstExprValue *const_val) { TypeTableEntry *wanted_type = const_val->type; diff --git a/src/analyze.hpp b/src/analyze.hpp index 100f85d4d9..88e06b2390 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -93,7 +93,6 @@ void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry); void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry); void complete_enum(CodeGen *g, TypeTableEntry *enum_type); bool ir_get_var_is_comptime(VariableTableEntry *var); -bool contains_comptime_undefined_value(ConstExprValue *value); bool const_values_equal(ConstExprValue *a, ConstExprValue *b); void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); void eval_min_max_value_int(CodeGen *g, TypeTableEntry *int_type, BigInt *bigint, bool is_max); diff --git a/src/ir.cpp b/src/ir.cpp index 2cce4a5044..c6078e755d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9148,15 +9148,8 @@ enum UndefAllowed { static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) { switch (value->value.special) { - case ConstValSpecialStatic: { - ConstExprValue *res = &value->value; - if (undef_allowed == UndefBad && contains_comptime_undefined_value(res)) { - ir_add_error(ira, value, buf_sprintf("use of undefined value")); - return nullptr; - } - - return res; - } + case ConstValSpecialStatic: + return &value->value; case ConstValSpecialRuntime: ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 8749f5b560..2247f0af96 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4124,19 +4124,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); - - cases.add( - "Trying to pass undefined array to function taking comptime array by value", - \\fn a(comptime b: [2]u8) u8 { return b[0]; } - \\ - \\test "" { - \\ const arr: [2]u8 = undefined; - \\ _ = a(arr); - \\} - , - ".tmp_source.zig:5:11: error: use of undefined value", - ); - - - } -- cgit v1.2.3 From ecd5e60be9cab03449e0d40a770c5a0c5582198d Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 20:50:09 +0200 Subject: Expanded the list of operators that catch undefined values at comptime --- src/ir.cpp | 134 +++++++++++---- test/compile_errors.zig | 420 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 523 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index c6078e755d..0f1e632299 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10773,10 +10773,15 @@ static TypeTableEntry *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *op1_val = &casted_op1->value; - ConstExprValue *op2_val = &casted_op2->value; - if (op1_val->special != ConstValSpecialRuntime && op2_val->special != ConstValSpecialRuntime) { + if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; assert(casted_op1->value.type->id == TypeTableEntryIdBool); assert(casted_op2->value.type->id == TypeTableEntryIdBool); @@ -10926,9 +10931,14 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp } } - ConstExprValue *op1_val = &op1->value; - ConstExprValue *op2_val = &op2->value; - if (value_is_comptime(op1_val) && value_is_comptime(op2_val)) { + if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + bool answer; bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value; if (op_id == IrBinOpCmpEq) { @@ -11017,10 +11027,15 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *op1_val = &casted_op1->value; - ConstExprValue *op2_val = &casted_op2->value; bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); - if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) { + if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + bool answer; if (resolved_type->id == TypeTableEntryIdComptimeFloat || resolved_type->id == TypeTableEntryIdFloat) { Cmp cmp_result = float_cmp(op1_val, op2_val); @@ -11048,11 +11063,17 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if (resolved_type->id == TypeTableEntryIdInt && !resolved_type->data.integral.is_signed) { ConstExprValue *known_left_val; IrBinOp flipped_op_id; - if (value_is_comptime(op1_val)) { - known_left_val = op1_val; + if (instr_is_comptime(casted_op1)) { + known_left_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (known_left_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + flipped_op_id = op_id; - } else if (value_is_comptime(op2_val)) { - known_left_val = op2_val; + } else if (instr_is_comptime(casted_op2)) { + known_left_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (known_left_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (op_id == IrBinOpCmpLessThan) { flipped_op_id = IrBinOpCmpGreaterThan; } else if (op_id == IrBinOpCmpGreaterThan) { @@ -11304,8 +11325,14 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp * } if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = &op1->value; - ConstExprValue *op2_val = &casted_op2->value; + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base); ir_link_new_instruction(result_instruction, &bin_op_instruction->base); ConstExprValue *out_val = &result_instruction->value; @@ -11384,7 +11411,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (is_signed_div) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. op_id = IrBinOpDivTrunc; @@ -11392,8 +11427,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { BigInt trunc_result; BigInt floor_result; - bigint_div_trunc(&trunc_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); - bigint_div_floor(&floor_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) { ok = true; op_id = IrBinOpDivTrunc; @@ -11414,7 +11449,15 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (is_signed_div && (is_int || is_float)) { bool ok = false; if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (is_int) { + ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem @@ -11422,14 +11465,19 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { BigInt rem_result; BigInt mod_result; - bigint_rem(&rem_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); - bigint_mod(&mod_result, &op1->value.data.x_bigint, &op2->value.data.x_bigint); + bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; } } else { IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + if (float_cmp_zero(&casted_op2->value) == CmpEQ) { // the division by zero error will be caught later, but we don't // have a remainder function ambiguity problem @@ -11437,8 +11485,8 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp } else { ConstExprValue rem_result; ConstExprValue mod_result; - float_rem(&rem_result, &op1->value, &casted_op2->value); - float_mod(&mod_result, &op1->value, &casted_op2->value); + float_rem(&rem_result, op1_val, op2_val); + float_mod(&mod_result, op1_val, op2_val); ok = float_cmp(&rem_result, &mod_result) == CmpEQ; } } @@ -11496,8 +11544,13 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_invalid; if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { - ConstExprValue *op1_val = &casted_op1->value; - ConstExprValue *op2_val = &casted_op2->value; + ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *result_instruction = ir_get_const(ira, &bin_op_instruction->base); ir_link_new_instruction(result_instruction, &bin_op_instruction->base); ConstExprValue *out_val = &result_instruction->value; @@ -11672,9 +11725,16 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * out_val->data.x_ptr.data.base_array.array_val = out_array_val; out_val->data.x_ptr.data.base_array.elem_index = 0; } - out_array_val->data.x_array.s_none.elements = create_const_vals(new_len); + if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && + op2_array_val->data.x_array.special == ConstArraySpecialUndef) { + out_array_val->data.x_array.special = ConstArraySpecialUndef; + return result_type; + } + + out_array_val->data.x_array.s_none.elements = create_const_vals(new_len); expand_undef_array(ira->codegen, op1_array_val); + expand_undef_array(ira->codegen, op2_array_val); size_t next_index = 0; for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { @@ -11726,10 +11786,14 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp } ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + if (array_val->data.x_array.special == ConstArraySpecialUndef) { + out_val->data.x_array.special = ConstArraySpecialUndef; - out_val->data.x_array.s_none.elements = create_const_vals(new_array_len); + TypeTableEntry *child_type = array_type->data.array.child_type; + return get_array_type(ira->codegen, child_type, new_array_len); + } - expand_undef_array(ira->codegen, array_val); + out_val->data.x_array.s_none.elements = create_const_vals(new_array_len); uint64_t i = 0; for (uint64_t x = 0; x < mult_amt; x += 1) { @@ -13056,7 +13120,11 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp // one of the ptr instructions if (instr_is_comptime(value)) { - ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value); + ConstExprValue *comptime_value = ir_resolve_const(ira, value, UndefBad); + if (comptime_value == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *pointee = const_ptr_pointee(ira->codegen, comptime_value); if (pointee->type == child_type) { ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); copy_const_val(out_val, pointee, value->value.data.x_ptr.mut == ConstPtrMutComptimeConst); @@ -13173,7 +13241,7 @@ static TypeTableEntry *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *ins if (expr_type->id == TypeTableEntryIdInt) { if (instr_is_comptime(value)) { ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); - if (!target_const_val) + if (target_const_val == nullptr) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); @@ -17750,9 +17818,13 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc if (type_is_invalid(casted_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - if (casted_value->value.special != ConstValSpecialRuntime) { + if (instr_is_comptime(casted_value)) { + ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad); + if (value == nullptr) + return ira->codegen->builtin_types.entry_invalid; + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_bool = !casted_value->value.data.x_bool; + out_val->data.x_bool = !value->data.x_bool; return bool_type; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2247f0af96..2562424ee0 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1893,6 +1893,426 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:1:15: error: use of undefined value", ); + cases.add( + "div on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a / a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "div assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a /= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "mod on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a % a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "mod assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a %= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "add on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a + a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "add assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a += a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "add wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a +% a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "add wrap assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a +%= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "sub on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a - a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "sub assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a -= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "sub wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a -% a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "sub wrap assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a -%= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "mult on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a * a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "mult assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a *= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "mult wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a *% a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "mult wrap assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a *%= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "shift left on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a << 2; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "shift left assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a <<= 2; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "shift right on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a >> 2; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "shift left assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a >>= 2; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "bin and on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a & a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "bin and assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a &= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "bin or on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a | a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "bin or assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a |= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "bin xor on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a ^ a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "bin xor assign on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ a ^= a; + \\} + , + ".tmp_source.zig:3:5: error: use of undefined value", + ); + + cases.add( + "equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a == a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "not equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a != a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "greater than on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a > a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "greater than equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a >= a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "less than on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a < a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "less than equal on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = a <= a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "and on undefined value", + \\comptime { + \\ var a: bool = undefined; + \\ _ = a and a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "or on undefined value", + \\comptime { + \\ var a: bool = undefined; + \\ _ = a or a; + \\} + , + ".tmp_source.zig:3:9: error: use of undefined value", + ); + + cases.add( + "negate on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = -a; + \\} + , + ".tmp_source.zig:3:10: error: use of undefined value", + ); + + cases.add( + "negate wrap on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = -%a; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "bin not on undefined value", + \\comptime { + \\ var a: i64 = undefined; + \\ _ = ~a; + \\} + , + ".tmp_source.zig:3:10: error: use of undefined value", + ); + + cases.add( + "bool not on undefined value", + \\comptime { + \\ var a: bool = undefined; + \\ _ = !a; + \\} + , + ".tmp_source.zig:3:10: error: use of undefined value", + ); + + cases.add( + "orelse on undefined value", + \\comptime { + \\ var a: ?bool = undefined; + \\ _ = a orelse false; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "catch on undefined value", + \\comptime { + \\ var a: error!bool = undefined; + \\ _ = a catch |err| false; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "deref on undefined value", + \\comptime { + \\ var a: *u8 = undefined; + \\ _ = a.*; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + + cases.add( + "unwrap on undefined value", + \\comptime { + \\ var a: ?u8 = undefined; + \\ _ = a.?; + \\} + , + ".tmp_source.zig:3:11: error: use of undefined value", + ); + cases.add( "endless loop in function evaluation", \\const seventh_fib_number = fibbonaci(7); -- cgit v1.2.3 From 055e0fef4eaa2d8c6dcbd83180baa44d88be3b4d Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Sat, 30 Jun 2018 21:22:26 +0200 Subject: Avoid resolve_const in cmp when instr are not comptime --- src/ir.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 0f1e632299..ce2e333227 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11029,10 +11029,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { - ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->builtin_types.entry_invalid; -- cgit v1.2.3 From 2759c7951da050d825cf765c4b660f5562fb01a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 14:10:27 -0400 Subject: always link against compiler_rt.o even when linking libc sometimes libgcc is missing things we need, so we always link compiler_rt and rely on weak linkage to allow libgcc to override. --- src/link.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/link.cpp b/src/link.cpp index a4631b1daf..2d9a79585f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -325,10 +325,13 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { - Buf *builtin_o_path = build_o(g, "builtin"); - lj->args.append(buf_ptr(builtin_o_path)); + if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + if (g->libc_link_lib == nullptr) { + Buf *builtin_o_path = build_o(g, "builtin"); + lj->args.append(buf_ptr(builtin_o_path)); + } + // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } @@ -554,7 +557,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(builtin_o_path)); } - // msvc compiler_rt is missing some stuff, so we still build it and rely on LinkOnce + // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage Buf *compiler_rt_o_path = build_compiler_rt(g); lj->args.append(buf_ptr(compiler_rt_o_path)); } -- cgit v1.2.3 From 35463526cceb91243410bdab4d74e2d5b3c60f66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Jul 2018 15:49:49 -0400 Subject: add runtime safety for `@intToEnum`; add docs for runtime safety See #367 --- doc/langref.html.in | 225 ++++++++++++++++++++++++++++++++++++++++++------ src/codegen.cpp | 19 +++- test/runtime_safety.zig | 18 ++++ 3 files changed, 233 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 15e04459bd..1da4205b89 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6144,7 +6144,14 @@ fn assert(ok: bool) void { if (!ok) unreachable; // assertion failure } {#code_end#} -

      At runtime crashes with the message reached unreachable code and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + std.debug.assert(false); +} + {#code_end#} {#header_close#} {#header_open|Index out of Bounds#}

      At compile-time:

      @@ -6154,7 +6161,16 @@ comptime { const garbage = array[5]; } {#code_end#} -

      At runtime crashes with the message index out of bounds and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +pub fn main() void { + var x = foo("hello"); +} + +fn foo(x: []const u8) u8 { + return x[5]; +} + {#code_end#} {#header_close#} {#header_open|Cast Negative Number to Unsigned Integer#}

      At compile-time:

      @@ -6164,10 +6180,18 @@ comptime { const unsigned = @intCast(u32, value); } {#code_end#} -

      At runtime crashes with the message attempt to cast negative value to unsigned integer and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var value: i32 = -1; + var unsigned = @intCast(u32, value); + std.debug.warn("value: {}\n", unsigned); +} + {#code_end#}

      - If you are trying to obtain the maximum value of an unsigned integer, use @maxValue(T), - where T is the integer type, such as u32. + To obtain the maximum value of an unsigned integer, use {#link|@maxValue#}.

      {#header_close#} {#header_open|Cast Truncates Data#} @@ -6178,11 +6202,18 @@ comptime { const byte = @intCast(u8, spartan_count); } {#code_end#} -

      At runtime crashes with the message integer cast truncated bits and a stack trace.

      +

      At runtime:

      + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var spartan_count: u16 = 300; + const byte = @intCast(u8, spartan_count); + std.debug.warn("value: {}\n", byte); +} + {#code_end#}

      - If you are trying to truncate bits, use @truncate(T, value), - where T is the integer type, such as u32, and value - is the value you want to truncate. + To truncate bits, use {#link|@truncate#}.

      {#header_close#} {#header_open|Integer Overflow#} @@ -6194,9 +6225,9 @@ comptime {
    • - (negation)
    • * (multiplication)
    • / (division)
    • -
    • @divTrunc (division)
    • -
    • @divFloor (division)
    • -
    • @divExact (division)
    • +
    • {#link|@divTrunc#} (division)
    • +
    • {#link|@divFloor#} (division)
    • +
    • {#link|@divExact#} (division)

    Example with addition at compile-time:

    {#code_begin|test_err|operation caused overflow#} @@ -6205,7 +6236,16 @@ comptime { byte += 1; } {#code_end#} -

    At runtime crashes with the message integer overflow and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var byte: u8 = 255; + byte += 1; + std.debug.warn("value: {}\n", byte); +} + {#code_end#} {#header_close#} {#header_open|Standard Library Math Functions#}

    These functions provided by the standard library return possible errors.

    @@ -6240,13 +6280,13 @@ pub fn main() !void { occurred, as well as returning the overflowed bits:

      -
    • @addWithOverflow
    • -
    • @subWithOverflow
    • -
    • @mulWithOverflow
    • -
    • @shlWithOverflow
    • +
    • {#link|@addWithOverflow#}
    • +
    • {#link|@subWithOverflow#}
    • +
    • {#link|@mulWithOverflow#}
    • +
    • {#link|@shlWithOverflow#}

    - Example of @addWithOverflow: + Example of {#link|@addWithOverflow#}:

    {#code_begin|exe#} const warn = @import("std").debug.warn; @@ -6292,7 +6332,16 @@ comptime { const x = @shlExact(u8(0b01010101), 2); } {#code_end#} -

    At runtime crashes with the message left shift overflowed bits and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var x: u8 = 0b01010101; + var y = @shlExact(x, 2); + std.debug.warn("value: {}\n", y); +} + {#code_end#} {#header_close#} {#header_open|Exact Right Shift Overflow#}

    At compile-time:

    @@ -6301,7 +6350,16 @@ comptime { const x = @shrExact(u8(0b10101010), 2); } {#code_end#} -

    At runtime crashes with the message right shift overflowed bits and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var x: u8 = 0b10101010; + var y = @shrExact(x, 2); + std.debug.warn("value: {}\n", y); +} + {#code_end#} {#header_close#} {#header_open|Division by Zero#}

    At compile-time:

    @@ -6312,8 +6370,17 @@ comptime { const c = a / b; } {#code_end#} -

    At runtime crashes with the message division by zero and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); +pub fn main() void { + var a: u32 = 1; + var b: u32 = 0; + var c = a / b; + std.debug.warn("value: {}\n", c); +} + {#code_end#} {#header_close#} {#header_open|Remainder Division by Zero#}

    At compile-time:

    @@ -6324,14 +6391,57 @@ comptime { const c = a % b; } {#code_end#} -

    At runtime crashes with the message remainder division by zero and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); +pub fn main() void { + var a: u32 = 10; + var b: u32 = 0; + var c = a % b; + std.debug.warn("value: {}\n", c); +} + {#code_end#} {#header_close#} {#header_open|Exact Division Remainder#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|exact division had a remainder#} +comptime { + const a: u32 = 10; + const b: u32 = 3; + const c = @divExact(a, b); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var a: u32 = 10; + var b: u32 = 3; + var c = @divExact(a, b); + std.debug.warn("value: {}\n", c); +} + {#code_end#} {#header_close#} {#header_open|Slice Widen Remainder#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|unable to convert#} +comptime { + var bytes = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = @bytesToSlice(u32, bytes); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var bytes = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = @bytesToSlice(u32, bytes[0..]); + std.debug.warn("value: {}\n", slice[0]); +} + {#code_end#} {#header_close#} {#header_open|Attempt to Unwrap Null#}

    At compile-time:

    @@ -6341,7 +6451,16 @@ comptime { const number = optional_number.?; } {#code_end#} -

    At runtime crashes with the message attempt to unwrap null and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var optional_number: ?i32 = null; + var number = optional_number.?; + std.debug.warn("value: {}\n", number); +} + {#code_end#}

    One way to avoid this crash is to test for null instead of assuming non-null, with the if expression:

    {#code_begin|exe|test#} @@ -6356,6 +6475,7 @@ pub fn main() void { } } {#code_end#} + {#see_also|Optionals#} {#header_close#} {#header_open|Attempt to Unwrap Error#}

    At compile-time:

    @@ -6368,7 +6488,19 @@ fn getNumberOrFail() !i32 { return error.UnableToReturnNumber; } {#code_end#} -

    At runtime crashes with the message attempt to unwrap error: ErrorCode and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + const number = getNumberOrFail() catch unreachable; + std.debug.warn("value: {}\n", number); +} + +fn getNumberOrFail() !i32 { + return error.UnableToReturnNumber; +} + {#code_end#}

    One way to avoid this crash is to test for an error instead of assuming a successful result, with the if expression:

    {#code_begin|exe#} @@ -6388,6 +6520,7 @@ fn getNumberOrFail() !i32 { return error.UnableToReturnNumber; } {#code_end#} + {#see_also|Errors#} {#header_close#} {#header_open|Invalid Error Code#}

    At compile-time:

    @@ -6398,11 +6531,47 @@ comptime { const invalid_err = @intToError(number); } {#code_end#} -

    At runtime crashes with the message invalid error code and a stack trace.

    +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +pub fn main() void { + var err = error.AnError; + var number = @errorToInt(err) + 500; + var invalid_err = @intToError(number); + std.debug.warn("value: {}\n", number); +} + {#code_end#} {#header_close#} {#header_open|Invalid Enum Cast#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|has no tag matching integer value 3#} +const Foo = enum { + A, + B, + C, +}; +comptime { + const a: u2 = 3; + const b = @intToEnum(Foo, a); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const std = @import("std"); + +const Foo = enum { + A, + B, + C, +}; +pub fn main() void { + var a: u2 = 3; + var b = @intToEnum(Foo, a); + std.debug.warn("value: {}\n", @tagName(b)); +} + {#code_end#} {#header_close#} {#header_open|Invalid Error Set Cast#} diff --git a/src/codegen.cpp b/src/codegen.cpp index 4419f4fc84..9c37c174d6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2673,8 +2673,25 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, TypeTableEntry *tag_int_type = wanted_type->data.enumeration.tag_int_type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), + LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), instruction->target->value.type, tag_int_type, target_val); + + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); + LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); + size_t field_count = wanted_type->data.enumeration.src_field_count; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref, + &wanted_type->data.enumeration.fields[field_i].value); + LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); + } + LLVMPositionBuilderAtEnd(g->builder, bad_value_block); + gen_safety_crash(g, PanicMsgIdBadEnumValue); + + LLVMPositionBuilderAtEnd(g->builder, ok_value_block); + } + return tag_int_value; } static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index a7e8d6dc0e..3d58dfe748 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -1,6 +1,24 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompareOutputContext) void { + cases.addRuntimeSafety("@intToEnum - no matching tag value", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\const Foo = enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub fn main() void { + \\ baz(bar(3)); + \\} + \\fn bar(a: u2) Foo { + \\ return @intToEnum(Foo, a); + \\} + \\fn baz(a: Foo) void {} + ); + cases.addRuntimeSafety("@floatToInt cannot fit - negative to unsigned", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); -- cgit v1.2.3 From 291afcf75ab458e54a8ccd78dfd1531debfd2e40 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 Jul 2018 14:20:26 -0400 Subject: fix runtime libc detection depending on locale closes #1165 --- src/analyze.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 6f94deb9fd..ca582dfc4c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4417,22 +4417,14 @@ Buf *get_linux_libc_include_path(void) { } char *prev_newline = buf_ptr(out_stderr); ZigList search_paths = {}; - bool found_search_paths = false; for (;;) { char *newline = strchr(prev_newline, '\n'); if (newline == nullptr) { - zig_panic("unable to determine libc include path: bad output from C compiler command"); + break; } *newline = 0; - if (found_search_paths) { - if (strcmp(prev_newline, "End of search list.") == 0) { - break; - } + if (prev_newline[0] == ' ') { search_paths.append(prev_newline); - } else { - if (strcmp(prev_newline, "#include <...> search starts here:") == 0) { - found_search_paths = true; - } } prev_newline = newline + 1; } -- cgit v1.2.3 From 8c39cdc89f2ae7fc25c3856e7c4c6b4662ac8a80 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 Jul 2018 21:36:16 -0400 Subject: fix await on early return when return type is struct previously, await on an early return would try to access the destroyed coroutine frame; now it copies the result into a temporary variable before destroying the coroutine frame --- src/ir.cpp | 12 +++------ test/behavior.zig | 1 + test/cases/coroutine_await_struct.zig | 47 +++++++++++++++++++++++++++++++++++ test/cases/coroutines.zig | 4 +-- 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 test/cases/coroutine_await_struct.zig (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 5df5c1d676..b40c2dc36d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6674,7 +6674,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); + // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, + // because we're about to destroy the memory. So we store it into our result variable. IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); + ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result); ir_build_cancel(irb, parent_scope, node, target_inst); ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); @@ -6696,17 +6699,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); - IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - IrBasicBlock **incoming_blocks = allocate(2); - IrInstruction **incoming_values = allocate(2); - incoming_blocks[0] = resume_block; - incoming_values[0] = yes_suspend_result; - incoming_blocks[1] = no_suspend_block; - incoming_values[1] = no_suspend_result; - return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); + return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { diff --git a/test/behavior.zig b/test/behavior.zig index 3766ed4305..d47eb8fd6c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -18,6 +18,7 @@ comptime { _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); _ = @import("cases/coroutines.zig"); + _ = @import("cases/coroutine_await_struct.zig"); _ = @import("cases/defer.zig"); _ = @import("cases/enum.zig"); _ = @import("cases/enum_with_members.zig"); diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig new file mode 100644 index 0000000000..56c526092d --- /dev/null +++ b/test/cases/coroutine_await_struct.zig @@ -0,0 +1,47 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; + +const Foo = struct { + x: i32, +}; + +var await_a_promise: promise = undefined; +var await_final_result = Foo{ .x = 0 }; + +test "coroutine await struct" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + await_seq('a'); + const p = async<&da.allocator> await_amain() catch unreachable; + await_seq('f'); + resume await_a_promise; + await_seq('i'); + assert(await_final_result.x == 1234); + assert(std.mem.eql(u8, await_points, "abcdefghi")); +} +async fn await_amain() void { + await_seq('b'); + const p = async await_another() catch unreachable; + await_seq('e'); + await_final_result = await p; + await_seq('h'); +} +async fn await_another() Foo { + await_seq('c'); + suspend |p| { + await_seq('d'); + await_a_promise = p; + } + await_seq('g'); + return Foo{ .x = 1234 }; +} + +var await_points = []u8{0} ** "abcdefghi".len; +var await_seq_index: usize = 0; + +fn await_seq(c: u8) void { + await_points[await_seq_index] = c; + await_seq_index += 1; +} diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index b3899b306b..f7f2af62a6 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -116,14 +116,14 @@ test "coroutine await early return" { defer da.deinit(); early_seq('a'); - const p = async<&da.allocator> early_amain() catch unreachable; + const p = async<&da.allocator> early_amain() catch @panic("out of memory"); early_seq('f'); assert(early_final_result == 1234); assert(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); - const p = async early_another() catch unreachable; + const p = async early_another() catch @panic("out of memory"); early_seq('d'); early_final_result = await p; early_seq('e'); -- cgit v1.2.3 From 9395162a7c41689bcd1c0c48f9eabffc1485fc74 Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Mon, 2 Jul 2018 16:56:40 -0400 Subject: Debug enum issue --- src/ir.cpp | 1 + test/behavior.zig | 1 + test/cases/switch_usize_enum_prongs.zig | 11 +++++++++++ 3 files changed, 13 insertions(+) create mode 100644 test/cases/switch_usize_enum_prongs.zig (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index b40c2dc36d..c16f3c09b8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19149,6 +19149,7 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; + printf("%s\n", buf_ptr(&start_val->type->name)); assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, diff --git a/test/behavior.zig b/test/behavior.zig index d47eb8fd6c..803d4a5a08 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -52,6 +52,7 @@ comptime { _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); + _ = @import("cases/switch_usize_enum_prongs.zig"); _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); diff --git a/test/cases/switch_usize_enum_prongs.zig b/test/cases/switch_usize_enum_prongs.zig new file mode 100644 index 0000000000..b49615e887 --- /dev/null +++ b/test/cases/switch_usize_enum_prongs.zig @@ -0,0 +1,11 @@ +const E = enum(usize) { One, Two }; + +test "aoeou" { + foo(1); +} + +fn foo(x: usize) void { + switch (x) { + E.One => {}, + } +} -- cgit v1.2.3 From 9cff23dbf9ff3da716a1c4397f9411eba09f6cac Mon Sep 17 00:00:00 2001 From: Isaac Hier Date: Wed, 4 Jul 2018 13:27:10 -0400 Subject: Fix assertion crash on enum switch values --- src/ir.cpp | 7 ++++++- test/behavior.zig | 1 - test/cases/switch_usize_enum_prongs.zig | 11 ----------- test/compile_errors.zig | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 13 deletions(-) delete mode 100644 test/cases/switch_usize_enum_prongs.zig (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index c16f3c09b8..37d673bbd7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19149,9 +19149,14 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira if (!end_val) return ira->codegen->builtin_types.entry_invalid; - printf("%s\n", buf_ptr(&start_val->type->name)); + if (start_val->type->id == TypeTableEntryIdEnum) + return ira->codegen->builtin_types.entry_invalid; assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); + + if (end_val->type->id == TypeTableEntryIdEnum) + return ira->codegen->builtin_types.entry_invalid; assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); + AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { diff --git a/test/behavior.zig b/test/behavior.zig index 803d4a5a08..d47eb8fd6c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -52,7 +52,6 @@ comptime { _ = @import("cases/switch.zig"); _ = @import("cases/switch_prong_err_enum.zig"); _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/switch_usize_enum_prongs.zig"); _ = @import("cases/syntax.zig"); _ = @import("cases/this.zig"); _ = @import("cases/try.zig"); diff --git a/test/cases/switch_usize_enum_prongs.zig b/test/cases/switch_usize_enum_prongs.zig deleted file mode 100644 index b49615e887..0000000000 --- a/test/cases/switch_usize_enum_prongs.zig +++ /dev/null @@ -1,11 +0,0 @@ -const E = enum(usize) { One, Two }; - -test "aoeou" { - foo(1); -} - -fn foo(x: usize) void { - switch (x) { - E.One => {}, - } -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7291a48a8f..8bd5480395 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -358,6 +358,24 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:14: note: other value is here", ); + + cases.add( + "invalid cast from integral type to enum", + \\const E = enum(usize) { One, Two }; + \\ + \\export fn entry() void { + \\ foo(1); + \\} + \\ + \\fn foo(x: usize) void { + \\ switch (x) { + \\ E.One => {}, + \\ } + \\} + , + ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'" + ); + cases.add( "range operator in switch used on error set", \\export fn entry() void { -- cgit v1.2.3 From 1a5bd8888174ef2eb1881c1dd81d418b44625cc7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 12:03:07 -0400 Subject: alternate implementation of previous commit This strategy adds another field to the SwitchBr instruction, which is the result of the CheckSwitchProngs instruction. The type of the result is void, and is unused, except that the SwitchBr instruction will not perform analysis if the CheckSwitchProngs instruction did not pass analysis. This allows the CheckSwitchProngs instruction to do implicit casting for its type checking, while preventing duplicate compile error messages. --- src/all_types.hpp | 1 + src/ir.cpp | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 5d449491c8..4d97be468c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2193,6 +2193,7 @@ struct IrInstructionSwitchBr { size_t case_count; IrInstructionSwitchBrCase *cases; IrInstruction *is_comptime; + IrInstruction *switch_prongs_void; }; struct IrInstructionSwitchVar { diff --git a/src/ir.cpp b/src/ir.cpp index 37d673bbd7..204ebb332a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1719,7 +1719,8 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr } static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, - IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime) + IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, + IrInstruction *switch_prongs_void) { IrInstructionSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable; @@ -1729,10 +1730,12 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode * instruction->case_count = case_count; instruction->cases = cases; instruction->is_comptime = is_comptime; + instruction->switch_prongs_void = switch_prongs_void; ir_ref_instruction(target_value, irb->current_basic_block); if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); ir_ref_bb(else_block); + if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block); for (size_t i = 0; i < case_count; i += 1) { ir_ref_instruction(cases[i].value, irb->current_basic_block); @@ -1744,10 +1747,10 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode * static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, - IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime) + IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void) { IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node, - target_value, else_block, case_count, cases, is_comptime); + target_value, else_block, case_count, cases, is_comptime, switch_prongs_void); ir_link_new_instruction(new_instruction, old_instruction); return new_instruction; } @@ -6035,13 +6038,13 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * } - ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, + IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length, else_prong != nullptr); if (cases.length == 0) { ir_build_br(irb, scope, node, else_block, is_comptime); } else { - ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime); + ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void); } if (!else_prong) { @@ -6692,7 +6695,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1); cases[1].block = cleanup_block; ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false); + 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); @@ -6773,7 +6776,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); cases[1].block = cleanup_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, - 2, cases, const_bool_false)); + 2, cases, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); @@ -7078,7 +7081,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec cases[0].block = invalid_resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = irb->exec->coro_final_cleanup_block; - ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false); + ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block); ir_build_coro_end(irb, scope, node); @@ -15297,6 +15300,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, if (type_is_invalid(target_value->value.type)) return ir_unreach_error(ira); + if (switch_br_instruction->switch_prongs_void != nullptr) { + if (type_is_invalid(switch_br_instruction->switch_prongs_void->other->value.type)) { + return ir_unreach_error(ira); + } + } + + size_t case_count = switch_br_instruction->case_count; bool is_comptime; @@ -15387,7 +15397,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base); ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base, - target_value, new_else_block, case_count, cases, nullptr); + target_value, new_else_block, case_count, cases, nullptr, nullptr); return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } @@ -19136,27 +19146,27 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira IrInstruction *start_value = range->start->other; if (type_is_invalid(start_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); + if (type_is_invalid(casted_start_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; IrInstruction *end_value = range->end->other; if (type_is_invalid(end_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); + if (type_is_invalid(casted_end_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *start_val = ir_resolve_const(ira, start_value, UndefBad); + ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); if (!start_val) return ira->codegen->builtin_types.entry_invalid; - ConstExprValue *end_val = ir_resolve_const(ira, end_value, UndefBad); + ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); if (!end_val) return ira->codegen->builtin_types.entry_invalid; - if (start_val->type->id == TypeTableEntryIdEnum) - return ira->codegen->builtin_types.entry_invalid; assert(start_val->type->id == TypeTableEntryIdInt || start_val->type->id == TypeTableEntryIdComptimeInt); - - if (end_val->type->id == TypeTableEntryIdEnum) - return ira->codegen->builtin_types.entry_invalid; assert(end_val->type->id == TypeTableEntryIdInt || end_val->type->id == TypeTableEntryIdComptimeInt); - AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, start_value->source_node); if (prev_node != nullptr) { -- cgit v1.2.3 From 1cf7511dc9d449473748675a5e734e81ea7c85c2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 16:20:46 -0400 Subject: add compile error notes for where struct definitions are closes #1202 --- src/analyze.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/analyze.hpp | 1 + src/ir.cpp | 47 ++++++++++++++++++++++++++++++++++++----------- test/compile_errors.zig | 42 ++++++++++++++++++++++++++++++++++++------ 4 files changed, 110 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index ca582dfc4c..643a85634e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -212,6 +212,43 @@ static uint8_t bits_needed_for_unsigned(uint64_t x) { return (upper >= x) ? base : (base + 1); } +AstNode *type_decl_node(TypeTableEntry *type_entry) { + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + zig_unreachable(); + case TypeTableEntryIdStruct: + return type_entry->data.structure.decl_node; + case TypeTableEntryIdEnum: + return type_entry->data.enumeration.decl_node; + case TypeTableEntryIdUnion: + return type_entry->data.unionation.decl_node; + case TypeTableEntryIdOpaque: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdComptimeFloat: + case TypeTableEntryIdComptimeInt: + case TypeTableEntryIdUndefined: + case TypeTableEntryIdNull: + case TypeTableEntryIdOptional: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdErrorSet: + case TypeTableEntryIdFn: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdBoundFn: + case TypeTableEntryIdArgTuple: + case TypeTableEntryIdPromise: + return nullptr; + } + zig_unreachable(); +} + bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: diff --git a/src/analyze.hpp b/src/analyze.hpp index c2730197e2..5168509fe0 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -202,5 +202,6 @@ uint32_t get_coro_frame_align_bytes(CodeGen *g); bool fn_type_can_fail(FnTypeId *fn_type_id); bool type_can_fail(TypeTableEntry *type_entry); bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type); +AstNode *type_decl_node(TypeTableEntry *type_entry); #endif diff --git a/src/ir.cpp b/src/ir.cpp index 204ebb332a..3ad7c77645 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -82,6 +82,7 @@ struct ConstCastSliceMismatch; struct ConstCastErrUnionErrSetMismatch; struct ConstCastErrUnionPayloadMismatch; struct ConstCastErrSetMismatch; +struct ConstCastTypeMismatch; struct ConstCastOnly { ConstCastResultId id; @@ -92,6 +93,7 @@ struct ConstCastOnly { ConstCastOptionalMismatch *optional; ConstCastErrUnionPayloadMismatch *error_union_payload; ConstCastErrUnionErrSetMismatch *error_union_error_set; + ConstCastTypeMismatch *type_mismatch; ConstCastOnly *return_type; ConstCastOnly *async_allocator_type; ConstCastOnly *null_wrap_ptr_child; @@ -100,6 +102,11 @@ struct ConstCastOnly { } data; }; +struct ConstCastTypeMismatch { + TypeTableEntry *wanted_type; + TypeTableEntry *actual_type; +}; + struct ConstCastOptionalMismatch { ConstCastOnly child; TypeTableEntry *wanted_child; @@ -8128,15 +8135,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } // pointer const - if (wanted_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer && - (actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && - (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && - actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && - actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && - actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment) - { + if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const); if (child.id != ConstCastResultIdOk) { @@ -8145,8 +8144,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry result.data.pointer_mismatch->child = child; result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type; result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type; + return result; + } + if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) && + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) && + actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset && + actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count && + actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment) + { + return result; } - return result; } // slice const @@ -8341,6 +8349,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry } result.id = ConstCastResultIdType; + result.data.type_mismatch = allocate_nonzero(1); + result.data.type_mismatch->wanted_type = wanted_type; + result.data.type_mismatch->actual_type = actual_type; return result; } @@ -10154,6 +10165,21 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); break; } + case ConstCastResultIdType: { + AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type); + AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type); + if (wanted_decl_node != nullptr) { + add_error_note(ira->codegen, parent_msg, wanted_decl_node, + buf_sprintf("%s declared here", + buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); + } + if (actual_decl_node != nullptr) { + add_error_note(ira->codegen, parent_msg, actual_decl_node, + buf_sprintf("%s declared here", + buf_ptr(&cast_result->data.type_mismatch->actual_type->name))); + } + break; + } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO @@ -10163,7 +10189,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa case ConstCastResultIdFnGenericArgCount: // TODO case ConstCastResultIdFnArg: // TODO case ConstCastResultIdFnArgNoAlias: // TODO - case ConstCastResultIdType: // TODO case ConstCastResultIdUnresolvedInferredErrSet: // TODO case ConstCastResultIdAsyncAllocatorType: // TODO case ConstCastResultIdNullWrapPtr: // TODO diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 8bd5480395..d508c7c36c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,40 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addCase(x: { + const tc = cases.create( + "wrong same named struct", + \\const a = @import("a.zig"); + \\const b = @import("b.zig"); + \\ + \\export fn entry() void { + \\ var a1: a.Foo = undefined; + \\ bar(&a1); + \\} + \\ + \\fn bar(x: *b.Foo) void {} + , + ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'", + ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'", + "a.zig:1:17: note: Foo declared here", + "b.zig:1:17: note: Foo declared here", + ); + + tc.addSourceFile("a.zig", + \\pub const Foo = struct { + \\ x: i32, + \\}; + ); + + tc.addSourceFile("b.zig", + \\pub const Foo = struct { + \\ z: f64, + \\}; + ); + + break :x tc; + }); + cases.add( "enum field value references enum", \\pub const Foo = extern enum { @@ -358,9 +392,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:14: note: other value is here", ); - - cases.add( - "invalid cast from integral type to enum", + cases.add("invalid cast from integral type to enum", \\const E = enum(usize) { One, Two }; \\ \\export fn entry() void { @@ -372,9 +404,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ E.One => {}, \\ } \\} - , - ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'" - ); + , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'"); cases.add( "range operator in switch used on error set", -- cgit v1.2.3 From 4ad4cd26541258a84faf97e9fe07a69fadc57c66 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 6 Jul 2018 17:27:44 -0400 Subject: fix iterating over a void slice closes #1203 --- src/codegen.cpp | 24 ++++++++++++++++++------ test/cases/void.zig | 12 ++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 9c37c174d6..26ee106959 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2992,18 +2992,26 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); } else if (array_type->id == TypeTableEntryIdStruct) { assert(array_type->data.structure.is_slice); + if (!type_has_bits(instruction->base.value.type)) { + if (safety_check_on) { + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, array_ptr); + } + return nullptr; + } + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); if (safety_check_on) { - size_t len_index = array_type->data.structure.fields[1].gen_index; + size_t len_index = array_type->data.structure.fields[slice_len_index].gen_index; assert(len_index != SIZE_MAX); LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); } - size_t ptr_index = array_type->data.structure.fields[0].gen_index; + size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; assert(ptr_index != SIZE_MAX); LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, ""); @@ -3983,11 +3991,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); } - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (type_has_bits(array_type)) { + size_t gen_ptr_index = instruction->base.value.type->data.structure.fields[slice_ptr_index].gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + } - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); + size_t gen_len_index = instruction->base.value.type->data.structure.fields[slice_len_index].gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); gen_store_untyped(g, len_value, len_field_ptr, 0, false); diff --git a/test/cases/void.zig b/test/cases/void.zig index ef91690878..7121ac664b 100644 --- a/test/cases/void.zig +++ b/test/cases/void.zig @@ -16,3 +16,15 @@ test "compare void with void compile time known" { assert(foo.a == {}); } } + +test "iterate over a void slice" { + var j: usize = 0; + for (times(10)) |_, i| { + assert(i == j); + j += 1; + } +} + +fn times(n: usize) []const void { + return ([*]void)(undefined)[0..n]; +} -- cgit v1.2.3 From d8295c188946b0f07d62420c2f08c940f70b03ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 7 Jul 2018 00:25:32 -0400 Subject: add @popCount intrinsic --- doc/langref.html.in | 15 +++++++++-- src/all_types.hpp | 12 +++++++++ src/analyze.cpp | 4 +++ src/bigint.cpp | 31 ++++++++++++++++++++++ src/bigint.hpp | 2 ++ src/codegen.cpp | 21 ++++++++++++++- src/ir.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 9 +++++++ test/behavior.zig | 7 ++--- test/cases/popcount.zig | 24 +++++++++++++++++ test/compile_errors.zig | 18 +++++++++++++ 11 files changed, 205 insertions(+), 6 deletions(-) create mode 100644 test/cases/popcount.zig (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 5c1cc130ac..8eaffb64ad 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5013,7 +5013,7 @@ comptime {

    If x is zero, @clz returns T.bit_count.

    - + {#see_also|@ctz|@popCount#} {#header_close#} {#header_open|@cmpxchgStrong#}
    @cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T
    @@ -5149,6 +5149,7 @@ test "main" {

    If x is zero, @ctz returns T.bit_count.

    + {#see_also|@clz|@popCount#} {#header_close#} {#header_open|@divExact#}
    @divExact(numerator: T, denominator: T) T
    @@ -5631,6 +5632,16 @@ test "call foo" { {#see_also|Root Source File#} {#header_close#} + {#header_open|@popCount#} +
    @popCount(integer: var) var
    +

    Counts the number of bits set in an integer.

    +

    + If integer is known at {#link|comptime#}, the return type is comptime_int. + Otherwise, the return type is an unsigned integer with the minimum number + of bits that can represent the bit count of the integer type. +

    + {#see_also|@ctz|@clz#} + {#header_close#} {#header_open|@ptrCast#}
    @ptrCast(comptime DestType: type, value: var) DestType

    @@ -7337,7 +7348,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz popCount import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 4d97be468c..6dcf1894d8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1352,6 +1352,7 @@ enum BuiltinFnId { BuiltinFnIdCompileLog, BuiltinFnIdCtz, BuiltinFnIdClz, + BuiltinFnIdPopCount, BuiltinFnIdImport, BuiltinFnIdCImport, BuiltinFnIdErrName, @@ -1477,6 +1478,7 @@ bool type_id_eql(TypeId a, TypeId b); enum ZigLLVMFnId { ZigLLVMFnIdCtz, ZigLLVMFnIdClz, + ZigLLVMFnIdPopCount, ZigLLVMFnIdOverflowArithmetic, ZigLLVMFnIdFloor, ZigLLVMFnIdCeil, @@ -1499,6 +1501,9 @@ struct ZigLLVMFnKey { struct { uint32_t bit_count; } clz; + struct { + uint32_t bit_count; + } pop_count; struct { uint32_t bit_count; } floating; @@ -2050,6 +2055,7 @@ enum IrInstructionId { IrInstructionIdUnionTag, IrInstructionIdClz, IrInstructionIdCtz, + IrInstructionIdPopCount, IrInstructionIdImport, IrInstructionIdCImport, IrInstructionIdCInclude, @@ -2545,6 +2551,12 @@ struct IrInstructionClz { IrInstruction *value; }; +struct IrInstructionPopCount { + IrInstruction base; + + IrInstruction *value; +}; + struct IrInstructionUnionTag { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 643a85634e..9b60f7374a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5976,6 +5976,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934; case ZigLLVMFnIdClz: return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; + case ZigLLVMFnIdPopCount: + return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049; case ZigLLVMFnIdFloor: return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; case ZigLLVMFnIdCeil: @@ -5998,6 +6000,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { return a.data.ctz.bit_count == b.data.ctz.bit_count; case ZigLLVMFnIdClz: return a.data.clz.bit_count == b.data.clz.bit_count; + case ZigLLVMFnIdPopCount: + return a.data.pop_count.bit_count == b.data.pop_count.bit_count; case ZigLLVMFnIdFloor: case ZigLLVMFnIdCeil: case ZigLLVMFnIdSqrt: diff --git a/src/bigint.cpp b/src/bigint.cpp index bb227a7c3d..bf18b9a1bf 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1593,6 +1593,37 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) { } } +size_t bigint_popcount_unsigned(const BigInt *bi) { + assert(!bi->is_negative); + if (bi->digit_count == 0) + return 0; + + size_t count = 0; + size_t bit_count = bi->digit_count * 64; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(bi, i)) + count += 1; + } + return count; +} + +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) + return 0; + if (bi->digit_count == 0) + return 0; + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) + count += 1; + } + return count; +} + size_t bigint_ctz(const BigInt *bi, size_t bit_count) { if (bit_count == 0) return 0; diff --git a/src/bigint.hpp b/src/bigint.hpp index 9f044c8722..48b222a227 100644 --- a/src/bigint.hpp +++ b/src/bigint.hpp @@ -81,6 +81,8 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base); size_t bigint_ctz(const BigInt *bi, size_t bit_count); size_t bigint_clz(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_unsigned(const BigInt *bi); size_t bigint_bits_needed(const BigInt *op); diff --git a/src/codegen.cpp b/src/codegen.cpp index 26ee106959..54e2da7d61 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3426,14 +3426,22 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) { ZigLLVMFnKey key = {}; const char *fn_name; + uint32_t n_args; if (fn_id == BuiltinFnIdCtz) { fn_name = "cttz"; + n_args = 2; key.id = ZigLLVMFnIdCtz; key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; } else if (fn_id == BuiltinFnIdClz) { fn_name = "ctlz"; + n_args = 2; key.id = ZigLLVMFnIdClz; key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdPopCount) { + fn_name = "ctpop"; + n_args = 1; + key.id = ZigLLVMFnIdPopCount; + key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count; } else { zig_unreachable(); } @@ -3448,7 +3456,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui int_type->type_ref, LLVMInt1Type(), }; - LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false); + LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, n_args, false); LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type); assert(LLVMGetIntrinsicID(fn_val)); @@ -3481,6 +3489,14 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); } +static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, IrInstructionPopCount *instruction) { + TypeTableEntry *int_type = instruction->value->value.type; + LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount); + LLVMValueRef operand = ir_llvm_value(g, instruction->value); + LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int); +} + static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; @@ -4831,6 +4847,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction); + case IrInstructionIdPopCount: + return ir_render_pop_count(g, executable, (IrInstructionPopCount *)instruction); case IrInstructionIdSwitchBr: return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction); case IrInstructionIdPhi: @@ -6342,6 +6360,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1); create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 1); create_builtin_fn(g, BuiltinFnIdClz, "clz", 1); + create_builtin_fn(g, BuiltinFnIdPopCount, "popCount", 1); create_builtin_fn(g, BuiltinFnIdImport, "import", 1); create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); diff --git a/src/ir.cpp b/src/ir.cpp index 3ad7c77645..98b1bd85ad 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -427,6 +427,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionPopCount *) { + return IrInstructionIdPopCount; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) { return IrInstructionIdUnionTag; } @@ -1725,6 +1729,15 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr return new_instruction; } +static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { + IrInstructionPopCount *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void) @@ -3841,6 +3854,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, ctz, lval); } + case BuiltinFnIdPopCount: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, instr, lval); + } case BuiltinFnIdClz: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -15275,6 +15298,48 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC } } +static TypeTableEntry *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) { + IrInstruction *value = instruction->value->other; + if (type_is_invalid(value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (value->value.type->id != TypeTableEntryIdInt && value->value.type->id != TypeTableEntryIdComptimeInt) { + ir_add_error(ira, value, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name))); + 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; + if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) { + size_t result = bigint_popcount_unsigned(&val->data.x_bigint); + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, result); + return ira->codegen->builtin_types.entry_num_lit_int; + } + if (value->value.type->id == TypeTableEntryIdComptimeInt) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, &instruction->base, + buf_sprintf("@popCount on negative %s value %s", + buf_ptr(&value->value.type->name), buf_ptr(val_buf))); + return ira->codegen->builtin_types.entry_invalid; + } + size_t result = bigint_popcount_signed(&val->data.x_bigint, value->value.type->data.integral.bit_count); + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + bigint_init_unsigned(&out_val->data.x_bigint, result); + return ira->codegen->builtin_types.entry_num_lit_int; + } + + IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, value); + result->value.type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count); + ir_link_new_instruction(result, &instruction->base); + return result->value.type; +} + static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) { if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; @@ -20534,6 +20599,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction); + case IrInstructionIdPopCount: + return ir_analyze_instruction_pop_count(ira, (IrInstructionPopCount *)instruction); case IrInstructionIdSwitchBr: return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction); case IrInstructionIdSwitchTarget: @@ -20892,6 +20959,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdUnwrapOptional: case IrInstructionIdClz: case IrInstructionIdCtz: + case IrInstructionIdPopCount: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: case IrInstructionIdUnionTag: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5e5a71382c..780cf9e756 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -501,6 +501,12 @@ static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) { fprintf(irp->f, ")"); } +static void ir_print_pop_count(IrPrint *irp, IrInstructionPopCount *instruction) { + fprintf(irp->f, "@popCount("); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); +} + static void ir_print_switch_br(IrPrint *irp, IrInstructionSwitchBr *instruction) { fprintf(irp->f, "switch ("); ir_print_other_instruction(irp, instruction->target_value); @@ -1425,6 +1431,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); break; + case IrInstructionIdPopCount: + ir_print_pop_count(irp, (IrInstructionPopCount *)instruction); + break; case IrInstructionIdClz: ir_print_clz(irp, (IrInstructionClz *)instruction); break; diff --git a/test/behavior.zig b/test/behavior.zig index d47eb8fd6c..450dded56c 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -8,17 +8,17 @@ comptime { _ = @import("cases/atomics.zig"); _ = @import("cases/bitcast.zig"); _ = @import("cases/bool.zig"); + _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); _ = @import("cases/bugs/828.zig"); _ = @import("cases/bugs/920.zig"); - _ = @import("cases/bugs/1111.zig"); _ = @import("cases/byval_arg_var.zig"); _ = @import("cases/cast.zig"); _ = @import("cases/const_slice_child.zig"); - _ = @import("cases/coroutines.zig"); _ = @import("cases/coroutine_await_struct.zig"); + _ = @import("cases/coroutines.zig"); _ = @import("cases/defer.zig"); _ = @import("cases/enum.zig"); _ = @import("cases/enum_with_members.zig"); @@ -36,11 +36,12 @@ comptime { _ = @import("cases/math.zig"); _ = @import("cases/merge_error_sets.zig"); _ = @import("cases/misc.zig"); - _ = @import("cases/optional.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/new_stack_call.zig"); _ = @import("cases/null.zig"); + _ = @import("cases/optional.zig"); _ = @import("cases/pointers.zig"); + _ = @import("cases/popcount.zig"); _ = @import("cases/pub_enum/index.zig"); _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("cases/reflection.zig"); diff --git a/test/cases/popcount.zig b/test/cases/popcount.zig new file mode 100644 index 0000000000..7dc7f28c0e --- /dev/null +++ b/test/cases/popcount.zig @@ -0,0 +1,24 @@ +const assert = @import("std").debug.assert; + +test "@popCount" { + comptime testPopCount(); + testPopCount(); +} + +fn testPopCount() void { + { + var x: u32 = 0xaa; + assert(@popCount(x) == 4); + } + { + var x: u32 = 0xaaaaaaaa; + assert(@popCount(x) == 16); + } + { + var x: i16 = -1; + assert(@popCount(x) == 16); + } + comptime { + assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + } +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d508c7c36c..9071f0ad7e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,24 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@popCount - non-integer", + \\export fn entry(x: f32) u32 { + \\ return @popCount(x); + \\} + , + ".tmp_source.zig:2:22: error: expected integer type, found 'f32'", + ); + + cases.add( + "@popCount - negative comptime_int", + \\comptime { + \\ _ = @popCount(-1); + \\} + , + ".tmp_source.zig:2:9: error: @popCount on negative comptime_int value -1", + ); + cases.addCase(x: { const tc = cases.create( "wrong same named struct", -- cgit v1.2.3 From eb326e15530dd6dca4ccbe7dbfde7bf048de813e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 Jul 2018 15:09:02 -0400 Subject: M:N threading * add std.atomic.QueueMpsc.isEmpty * make std.debug.global_allocator thread-safe * std.event.Loop: now you have to choose between - initSingleThreaded - initMultiThreaded * std.event.Loop multiplexes coroutines onto kernel threads * Remove std.event.Loop.stop. Instead the event loop run() function returns once there are no pending coroutines. * fix crash in ir.cpp for calling methods under some conditions * small progress self-hosted compiler, analyzing top level declarations * Introduce std.event.Lock for synchronizing coroutines * introduce std.event.Locked(T) for data that only 1 coroutine should modify at once. * make the self hosted compiler use multi threaded event loop * make std.heap.DirectAllocator thread-safe See #174 TODO: * call sched_getaffinity instead of hard coding thread pool size 4 * support for Windows and MacOS * #1194 * #1197 --- src-self-hosted/main.zig | 5 +- src-self-hosted/module.zig | 257 ++++++++++++++++++-- src/ir.cpp | 2 +- std/atomic/queue_mpsc.zig | 17 ++ std/debug/index.zig | 7 +- std/event.zig | 580 +++++++++++++++++++++++++++++++++++++++------ std/heap.zig | 30 +-- std/mem.zig | 2 +- std/os/index.zig | 39 ++- std/os/linux/index.zig | 8 + 10 files changed, 833 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index d17fc94c82..fe94a4460a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -384,7 +384,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); - var loop = try event.Loop.init(allocator); + var loop: event.Loop = undefined; + try loop.initMultiThreaded(allocator); var module = try Module.create( &loop, @@ -493,8 +494,6 @@ async fn processBuildEvents(module: *Module, watch: bool) void { switch (build_event) { Module.Event.Ok => { std.debug.warn("Build succeeded\n"); - // for now we stop after 1 - module.loop.stop(); return; }, Module.Event.Error => |err| { diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index cf27c826c8..5ce1a7965a 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -2,6 +2,7 @@ const std = @import("std"); const os = std.os; const io = std.io; const mem = std.mem; +const Allocator = mem.Allocator; const Buffer = std.Buffer; const llvm = @import("llvm.zig"); const c = @import("c.zig"); @@ -13,6 +14,7 @@ const ArrayList = std.ArrayList; const errmsg = @import("errmsg.zig"); const ast = std.zig.ast; const event = std.event; +const assert = std.debug.assert; pub const Module = struct { loop: *event.Loop, @@ -81,6 +83,8 @@ pub const Module = struct { link_out_file: ?[]const u8, events: *event.Channel(Event), + exported_symbol_names: event.Locked(Decl.Table), + // TODO handle some of these earlier and report them in a way other than error codes pub const BuildError = error{ OutOfMemory, @@ -232,6 +236,7 @@ pub const Module = struct { .test_name_prefix = null, .emit_file_type = Emit.Binary, .link_out_file = null, + .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), }); } @@ -272,38 +277,91 @@ pub const Module = struct { return; }; await (async self.events.put(Event.Ok) catch unreachable); + // for now we stop after 1 + return; } } async fn addRootSrc(self: *Module) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); + // TODO async/await os.path.real const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; }; errdefer self.a().free(root_src_real_path); + // TODO async/await readFileAlloc() const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { try printError("unable to open '{}': {}", root_src_real_path, err); return err; }; errdefer self.a().free(source_code); - var tree = try std.zig.parse(self.a(), source_code); - defer tree.deinit(); - - //var it = tree.root_node.decls.iterator(); - //while (it.next()) |decl_ptr| { - // const decl = decl_ptr.*; - // switch (decl.id) { - // ast.Node.Comptime => @panic("TODO"), - // ast.Node.VarDecl => @panic("TODO"), - // ast.Node.UseDecl => @panic("TODO"), - // ast.Node.FnDef => @panic("TODO"), - // ast.Node.TestDecl => @panic("TODO"), - // else => unreachable, - // } - //} + var parsed_file = ParsedFile{ + .tree = try std.zig.parse(self.a(), source_code), + .realpath = root_src_real_path, + }; + errdefer parsed_file.tree.deinit(); + + const tree = &parsed_file.tree; + + // create empty struct for it + const decls = try Scope.Decls.create(self.a(), null); + errdefer decls.destroy(); + + var it = tree.root_node.decls.iterator(0); + while (it.next()) |decl_ptr| { + const decl = decl_ptr.*; + switch (decl.id) { + ast.Node.Id.Comptime => @panic("TODO"), + ast.Node.Id.VarDecl => @panic("TODO"), + ast.Node.Id.FnProto => { + const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); + + const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { + @panic("TODO add compile error"); + //try self.addCompileError( + // &parsed_file, + // fn_proto.fn_token, + // fn_proto.fn_token + 1, + // "missing function name", + //); + continue; + }; + + const fn_decl = try self.a().create(Decl.Fn{ + .base = Decl{ + .id = Decl.Id.Fn, + .name = name, + .visib = parseVisibToken(tree, fn_proto.visib_token), + .resolution = Decl.Resolution.Unresolved, + }, + .value = Decl.Fn.Val{ .Unresolved = {} }, + .fn_proto = fn_proto, + }); + errdefer self.a().destroy(fn_decl); + + // TODO make this parallel + try await try async self.addTopLevelDecl(tree, &fn_decl.base); + }, + ast.Node.Id.TestDecl => @panic("TODO"), + else => unreachable, + } + } + } + + async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void { + const is_export = decl.isExported(tree); + + { + const exported_symbol_names = await try async self.exported_symbol_names.acquire(); + defer exported_symbol_names.release(); + + if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { + @panic("TODO report compile error"); + } + } } pub fn link(self: *Module, out_file: ?[]const u8) !void { @@ -350,3 +408,172 @@ fn printError(comptime format: []const u8, args: ...) !void { const out_stream = &stderr_file_out_stream.stream; try out_stream.print(format, args); } + +fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib { + if (optional_token_index) |token_index| { + const token = tree.tokens.at(token_index); + assert(token.id == Token.Id.Keyword_pub); + return Visib.Pub; + } else { + return Visib.Private; + } +} + +pub const Scope = struct { + id: Id, + parent: ?*Scope, + + pub const Id = enum { + Decls, + Block, + }; + + pub const Decls = struct { + base: Scope, + table: Decl.Table, + + pub fn create(a: *Allocator, parent: ?*Scope) !*Decls { + const self = try a.create(Decls{ + .base = Scope{ + .id = Id.Decls, + .parent = parent, + }, + .table = undefined, + }); + errdefer a.destroy(self); + + self.table = Decl.Table.init(a); + errdefer self.table.deinit(); + + return self; + } + + pub fn destroy(self: *Decls) void { + self.table.deinit(); + self.table.allocator.destroy(self); + self.* = undefined; + } + }; + + pub const Block = struct { + base: Scope, + }; +}; + +pub const Visib = enum { + Private, + Pub, +}; + +pub const Decl = struct { + id: Id, + name: []const u8, + visib: Visib, + resolution: Resolution, + + pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); + + pub fn isExported(base: *const Decl, tree: *ast.Tree) bool { + switch (base.id) { + Id.Fn => { + const fn_decl = @fieldParentPtr(Fn, "base", base); + return fn_decl.isExported(tree); + }, + else => return false, + } + } + + pub const Resolution = enum { + Unresolved, + InProgress, + Invalid, + Ok, + }; + + pub const Id = enum { + Var, + Fn, + CompTime, + }; + + pub const Var = struct { + base: Decl, + }; + + pub const Fn = struct { + base: Decl, + value: Val, + fn_proto: *const ast.Node.FnProto, + + // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous + pub const Val = union { + Unresolved: void, + Ok: *Value.Fn, + }; + + pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { + return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { + const token = tree.tokens.at(tok_index); + break :x switch (token.id) { + Token.Id.Extern => tree.tokenSlicePtr(token), + else => null, + }; + } else null; + } + + pub fn isExported(self: Fn, tree: *ast.Tree) bool { + if (self.fn_proto.extern_export_inline_token) |tok_index| { + const token = tree.tokens.at(tok_index); + return token.id == Token.Id.Keyword_export; + } else { + return false; + } + } + }; + + pub const CompTime = struct { + base: Decl, + }; +}; + +pub const Value = struct { + pub const Fn = struct {}; +}; + +pub const Type = struct { + id: Id, + + pub const Id = enum { + Type, + Void, + Bool, + NoReturn, + Int, + Float, + Pointer, + Array, + Struct, + ComptimeFloat, + ComptimeInt, + Undefined, + Null, + Optional, + ErrorUnion, + ErrorSet, + Enum, + Union, + Fn, + Opaque, + Promise, + }; + + pub const Struct = struct { + base: Type, + decls: *Scope.Decls, + }; +}; + +pub const ParsedFile = struct { + tree: ast.Tree, + realpath: []const u8, +}; diff --git a/src/ir.cpp b/src/ir.cpp index 98b1bd85ad..3fc8306339 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13278,7 +13278,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn; IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline); + fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline); } else { ir_add_error_node(ira, fn_ref->source_node, buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name))); diff --git a/std/atomic/queue_mpsc.zig b/std/atomic/queue_mpsc.zig index 8030565d7a..bc0a94258b 100644 --- a/std/atomic/queue_mpsc.zig +++ b/std/atomic/queue_mpsc.zig @@ -15,6 +15,8 @@ pub fn QueueMpsc(comptime T: type) type { pub const Node = std.atomic.Stack(T).Node; + /// Not thread-safe. The call to init() must complete before any other functions are called. + /// No deinitialization required. pub fn init() Self { return Self{ .inboxes = []std.atomic.Stack(T){ @@ -26,12 +28,15 @@ pub fn QueueMpsc(comptime T: type) type { }; } + /// Fully thread-safe. put() may be called from any thread at any time. pub fn put(self: *Self, node: *Node) void { const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst); const inbox = &self.inboxes[inbox_index]; inbox.push(node); } + /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before + /// the next call to get(). pub fn get(self: *Self) ?*Node { if (self.outbox.pop()) |node| { return node; @@ -43,6 +48,18 @@ pub fn QueueMpsc(comptime T: type) type { } return self.outbox.pop(); } + + /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before + /// the next call to isEmpty(). + pub fn isEmpty(self: *Self) bool { + if (!self.outbox.isEmpty()) return false; + const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst); + const prev_inbox = &self.inboxes[prev_inbox_index]; + while (prev_inbox.pop()) |node| { + self.outbox.push(node); + } + return self.outbox.isEmpty(); + } }; } diff --git a/std/debug/index.zig b/std/debug/index.zig index 57b2dfc300..a5e1c313f0 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -11,6 +11,11 @@ const builtin = @import("builtin"); pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; +pub const runtime_safety = switch (builtin.mode) { + builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, +}; + /// Tries to write to stderr, unbuffered, and ignores any error returned. /// Does not append a newline. /// TODO atomic/multithread support @@ -1098,7 +1103,7 @@ fn readILeb128(in_stream: var) !i64 { /// This should only be used in temporary test programs. pub const global_allocator = &global_fixed_allocator.allocator; -var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); +var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]); var global_allocator_mem: [100 * 1024]u8 = undefined; // TODO make thread safe diff --git a/std/event.zig b/std/event.zig index c6ac04a9d0..2d69d0cb16 100644 --- a/std/event.zig +++ b/std/event.zig @@ -11,53 +11,69 @@ pub const TcpServer = struct { handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, loop: *Loop, - sockfd: i32, + sockfd: ?i32, accept_coro: ?promise, listen_address: std.net.Address, waiting_for_emfile_node: PromiseNode, + listen_resume_node: event.Loop.ResumeNode, const PromiseNode = std.LinkedList(promise).Node; - pub fn init(loop: *Loop) !TcpServer { - const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); - errdefer std.os.close(sockfd); - + pub fn init(loop: *Loop) TcpServer { // TODO can't initialize handler coroutine here because we need well defined copy elision return TcpServer{ .loop = loop, - .sockfd = sockfd, + .sockfd = null, .accept_coro = null, .handleRequestFn = undefined, .waiting_for_emfile_node = undefined, .listen_address = undefined, + .listen_resume_node = event.Loop.ResumeNode{ + .id = event.Loop.ResumeNode.Id.Basic, + .handle = undefined, + }, }; } - pub fn listen(self: *TcpServer, address: *const std.net.Address, handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void) !void { + pub fn listen( + self: *TcpServer, + address: *const std.net.Address, + handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void, + ) !void { self.handleRequestFn = handleRequestFn; - try std.os.posixBind(self.sockfd, &address.os_addr); - try std.os.posixListen(self.sockfd, posix.SOMAXCONN); - self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd)); + const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); + errdefer std.os.close(sockfd); + self.sockfd = sockfd; + + try std.os.posixBind(sockfd, &address.os_addr); + try std.os.posixListen(sockfd, posix.SOMAXCONN); + self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(sockfd)); self.accept_coro = try async TcpServer.handler(self); errdefer cancel self.accept_coro.?; - try self.loop.addFd(self.sockfd, self.accept_coro.?); - errdefer self.loop.removeFd(self.sockfd); + self.listen_resume_node.handle = self.accept_coro.?; + try self.loop.addFd(sockfd, &self.listen_resume_node); + errdefer self.loop.removeFd(sockfd); + } + + /// Stop listening + pub fn close(self: *TcpServer) void { + self.loop.removeFd(self.sockfd.?); + std.os.close(self.sockfd.?); } pub fn deinit(self: *TcpServer) void { - self.loop.removeFd(self.sockfd); if (self.accept_coro) |accept_coro| cancel accept_coro; - std.os.close(self.sockfd); + if (self.sockfd) |sockfd| std.os.close(sockfd); } pub async fn handler(self: *TcpServer) void { while (true) { var accepted_addr: std.net.Address = undefined; - if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { + if (std.os.posixAccept(self.sockfd.?, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| { var socket = std.os.File.openHandle(accepted_fd); _ = async self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) { error.OutOfMemory => { @@ -95,32 +111,65 @@ pub const TcpServer = struct { pub const Loop = struct { allocator: *mem.Allocator, - keep_running: bool, next_tick_queue: std.atomic.QueueMpsc(promise), os_data: OsData, + dispatch_lock: u8, // TODO make this a bool + pending_event_count: usize, + extra_threads: []*std.os.Thread, + final_resume_node: ResumeNode, - const OsData = switch (builtin.os) { - builtin.Os.linux => struct { - epollfd: i32, - }, - else => struct {}, + pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + + pub const ResumeNode = struct { + id: Id, + handle: promise, + + pub const Id = enum { + Basic, + Stop, + EventFd, + }; + + pub const EventFd = struct { + base: ResumeNode, + eventfd: i32, + }; }; - pub const NextTickNode = std.atomic.QueueMpsc(promise).Node; + /// After initialization, call run(). + /// TODO copy elision / named return values so that the threads referencing *Loop + /// have the correct pointer value. + fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { + return self.initInternal(allocator, 1); + } /// The allocator must be thread-safe because we use it for multiplexing /// coroutines onto kernel threads. - pub fn init(allocator: *mem.Allocator) !Loop { - var self = Loop{ - .keep_running = true, + /// After initialization, call run(). + /// TODO copy elision / named return values so that the threads referencing *Loop + /// have the correct pointer value. + fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + // TODO check the actual cpu core count + return self.initInternal(allocator, 4); + } + + /// Thread count is the total thread count. The thread pool size will be + /// max(thread_count - 1, 0) + fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void { + self.* = Loop{ + .pending_event_count = 0, .allocator = allocator, .os_data = undefined, .next_tick_queue = std.atomic.QueueMpsc(promise).init(), + .dispatch_lock = 1, // start locked so threads go directly into epoll wait + .extra_threads = undefined, + .final_resume_node = ResumeNode{ + .id = ResumeNode.Id.Stop, + .handle = undefined, + }, }; - try self.initOsData(); + try self.initOsData(thread_count); errdefer self.deinitOsData(); - - return self; } /// must call stop before deinit @@ -128,13 +177,70 @@ pub const Loop = struct { self.deinitOsData(); } - const InitOsDataError = std.os.LinuxEpollCreateError; + const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || + std.os.SpawnThreadError || std.os.LinuxEpollCtlError; + + const wakeup_bytes = []u8{0x1} ** 8; - fn initOsData(self: *Loop) InitOsDataError!void { + fn initOsData(self: *Loop, thread_count: usize) InitOsDataError!void { switch (builtin.os) { builtin.Os.linux => { - self.os_data.epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC); + const extra_thread_count = thread_count - 1; + self.os_data.available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(); + self.os_data.eventfd_resume_nodes = try self.allocator.alloc( + std.atomic.Stack(ResumeNode.EventFd).Node, + extra_thread_count, + ); + errdefer self.allocator.free(self.os_data.eventfd_resume_nodes); + + errdefer { + while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + } + for (self.os_data.eventfd_resume_nodes) |*eventfd_node| { + eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ + .data = ResumeNode.EventFd{ + .base = ResumeNode{ + .id = ResumeNode.Id.EventFd, + .handle = undefined, + }, + .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), + }, + .next = undefined, + }; + self.os_data.available_eventfd_resume_nodes.push(eventfd_node); + } + + self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); errdefer std.os.close(self.os_data.epollfd); + + self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); + errdefer std.os.close(self.os_data.final_eventfd); + + self.os_data.final_eventfd_event = posix.epoll_event{ + .events = posix.EPOLLIN, + .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, + }; + try std.os.linuxEpollCtl( + self.os_data.epollfd, + posix.EPOLL_CTL_ADD, + self.os_data.final_eventfd, + &self.os_data.final_eventfd_event, + ); + self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); + errdefer self.allocator.free(self.extra_threads); + + var extra_thread_index: usize = 0; + errdefer { + while (extra_thread_index != 0) { + extra_thread_index -= 1; + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + self.extra_threads[extra_thread_index].wait(); + } + } + while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { + self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); + } }, else => {}, } @@ -142,65 +248,154 @@ pub const Loop = struct { fn deinitOsData(self: *Loop) void { switch (builtin.os) { - builtin.Os.linux => std.os.close(self.os_data.epollfd), + builtin.Os.linux => { + std.os.close(self.os_data.final_eventfd); + while (self.os_data.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); + std.os.close(self.os_data.epollfd); + self.allocator.free(self.os_data.eventfd_resume_nodes); + self.allocator.free(self.extra_threads); + }, else => {}, } } - pub fn addFd(self: *Loop, fd: i32, prom: promise) !void { + /// resume_node must live longer than the promise that it holds a reference to. + pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + errdefer { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + try self.addFdNoCounter(fd, resume_node); + } + + fn addFdNoCounter(self: *Loop, fd: i32, resume_node: *ResumeNode) !void { var ev = std.os.linux.epoll_event{ .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, - .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) }, + .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, }; try std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev); } pub fn removeFd(self: *Loop, fd: i32) void { + self.removeFdNoCounter(fd); + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + + fn removeFdNoCounter(self: *Loop, fd: i32) void { std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; } - async fn waitFd(self: *Loop, fd: i32) !void { + + pub async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); + var resume_node = ResumeNode{ + .id = ResumeNode.Id.Basic, + .handle = undefined, + }; suspend |p| { - try self.addFd(fd, p); + resume_node.handle = p; + try self.addFd(fd, &resume_node); } + var a = &resume_node; // TODO better way to explicitly put memory in coro frame } - pub fn stop(self: *Loop) void { - // TODO make atomic - self.keep_running = false; - // TODO activate an fd in the epoll set which should cancel all the promises - } - - /// bring your own linked list node. this means it can't fail. + /// Bring your own linked list node. This means it can't fail. pub fn onNextTick(self: *Loop, node: *NextTickNode) void { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); self.next_tick_queue.put(node); } pub fn run(self: *Loop) void { - while (self.keep_running) { - // TODO multiplex the next tick queue and the epoll event results onto a thread pool - while (self.next_tick_queue.get()) |node| { - resume node.data; - } - if (!self.keep_running) break; - - self.dispatchOsEvents(); + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.workerRun(); + for (self.extra_threads) |extra_thread| { + extra_thread.wait(); } } - fn dispatchOsEvents(self: *Loop) void { - switch (builtin.os) { - builtin.Os.linux => { - var events: [16]std.os.linux.epoll_event = undefined; - const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); - for (events[0..count]) |ev| { - const p = @intToPtr(promise, ev.data.ptr); - resume p; + fn workerRun(self: *Loop) void { + start_over: while (true) { + if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { + while (self.next_tick_queue.get()) |next_tick_node| { + const handle = next_tick_node.data; + if (self.next_tick_queue.isEmpty()) { + // last node, just resume it + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + } + + // non-last node, stick it in the epoll set so that + // other threads can get to it + if (self.os_data.available_eventfd_resume_nodes.pop()) |resume_stack_node| { + const eventfd_node = &resume_stack_node.data; + eventfd_node.base.handle = handle; + // the pending count is already accounted for + self.addFdNoCounter(eventfd_node.eventfd, &eventfd_node.base) catch |_| { + // fine, we didn't need it anyway + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + self.os_data.available_eventfd_resume_nodes.push(resume_stack_node); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + }; + } else { + // threads are too busy, can't add another eventfd to wake one up + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + resume handle; + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + continue :start_over; + } } - }, - else => {}, + + const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst); + if (pending_event_count == 0) { + // cause all the threads to stop + // writing 8 bytes to an eventfd cannot fail + std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; + return; + } + + _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + } + + // only process 1 event so we don't steal from other threads + var events: [1]std.os.linux.epoll_event = undefined; + const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); + for (events[0..count]) |ev| { + const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); + const handle = resume_node.handle; + const resume_node_id = resume_node.id; + switch (resume_node_id) { + ResumeNode.Id.Basic => {}, + ResumeNode.Id.Stop => return, + ResumeNode.Id.EventFd => { + const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node); + self.removeFdNoCounter(event_fd_node.eventfd); + const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node); + self.os_data.available_eventfd_resume_nodes.push(stack_node); + }, + } + resume handle; + if (resume_node_id == ResumeNode.Id.EventFd) { + _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst); + } + } } } + + const OsData = switch (builtin.os) { + builtin.Os.linux => struct { + epollfd: i32, + // pre-allocated eventfds. all permanently active. + // this is how we send promises to be resumed on other threads. + available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd), + eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node, + final_eventfd: i32, + final_eventfd_event: posix.epoll_event, + }, + else => struct {}, + }; }; /// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size @@ -304,9 +499,7 @@ pub fn Channel(comptime T: type) type { // TODO integrate this function with named return values // so we can get rid of this extra result copy var result: T = undefined; - var debug_handle: usize = undefined; suspend |handle| { - debug_handle = @ptrToInt(handle); var my_tick_node = Loop.NextTickNode{ .next = undefined, .data = handle, @@ -438,9 +631,8 @@ test "listen on a port, send bytes, receive bytes" { const self = @fieldParentPtr(Self, "tcp_server", tcp_server); var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 defer socket.close(); - const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) { - error.OutOfMemory => @panic("unable to handle connection: out of memory"), - }; + // TODO guarantee elision of this allocation + const next_handler = async errorableHandler(self, _addr, socket) catch unreachable; (await next_handler) catch |err| { std.debug.panic("unable to handle connection: {}\n", err); }; @@ -461,17 +653,18 @@ test "listen on a port, send bytes, receive bytes" { const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable; const addr = std.net.Address.initIp4(ip4addr, 0); - var loop = try Loop.init(std.debug.global_allocator); - var server = MyServer{ .tcp_server = try TcpServer.init(&loop) }; + var loop: Loop = undefined; + try loop.initSingleThreaded(std.debug.global_allocator); + var server = MyServer{ .tcp_server = TcpServer.init(&loop) }; defer server.tcp_server.deinit(); try server.tcp_server.listen(addr, MyServer.handler); - const p = try async doAsyncTest(&loop, server.tcp_server.listen_address); + const p = try async doAsyncTest(&loop, server.tcp_server.listen_address, &server.tcp_server); defer cancel p; loop.run(); } -async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { +async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *TcpServer) void { errdefer @panic("test failure"); var socket_file = try await try async event.connect(loop, address); @@ -481,7 +674,7 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void { const amt_read = try socket_file.read(buf[0..]); const msg = buf[0..amt_read]; assert(mem.eql(u8, msg, "hello from server\n")); - loop.stop(); + server.close(); } test "std.event.Channel" { @@ -490,7 +683,9 @@ test "std.event.Channel" { const allocator = &da.allocator; - var loop = try Loop.init(allocator); + var loop: Loop = undefined; + // TODO make a multi threaded test + try loop.initSingleThreaded(allocator); defer loop.deinit(); const channel = try Channel(i32).create(&loop, 0); @@ -515,11 +710,248 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void { const value2_promise = try async channel.get(); const value2 = await value2_promise; assert(value2 == 4567); - - loop.stop(); } async fn testChannelPutter(channel: *Channel(i32)) void { await (async channel.put(1234) catch @panic("out of memory")); await (async channel.put(4567) catch @panic("out of memory")); } + +/// Thread-safe async/await lock. +/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and +/// are resumed when the lock is released, in order. +pub const Lock = struct { + loop: *Loop, + shared_bit: u8, // TODO make this a bool + queue: Queue, + queue_empty_bit: u8, // TODO make this a bool + + const Queue = std.atomic.QueueMpsc(promise); + + pub const Held = struct { + lock: *Lock, + + pub fn release(self: Held) void { + // Resume the next item from the queue. + if (self.lock.queue.get()) |node| { + self.lock.loop.onNextTick(node); + return; + } + + // We need to release the lock. + _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // There might be a queue item. If we know the queue is empty, we can be done, + // because the other actor will try to obtain the lock. + // But if there's a queue item, we are the actor which must loop and attempt + // to grab the lock again. + if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + return; + } + + while (true) { + const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit != 0) { + // We did not obtain the lock. Great, the queue is someone else's problem. + return; + } + + // Resume the next item from the queue. + if (self.lock.queue.get()) |node| { + self.lock.loop.onNextTick(node); + return; + } + + // Release the lock again. + _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // Find out if we can be done. + if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + return; + } + } + } + }; + + pub fn init(loop: *Loop) Lock { + return Lock{ + .loop = loop, + .shared_bit = 0, + .queue = Queue.init(), + .queue_empty_bit = 1, + }; + } + + /// Must be called when not locked. Not thread safe. + /// All calls to acquire() and release() must complete before calling deinit(). + pub fn deinit(self: *Lock) void { + assert(self.shared_bit == 0); + while (self.queue.get()) |node| cancel node.data; + } + + pub async fn acquire(self: *Lock) Held { + var my_tick_node: Loop.NextTickNode = undefined; + + s: suspend |handle| { + my_tick_node.data = handle; + self.queue.put(&my_tick_node); + + // At this point, we are in the queue, so we might have already been resumed and this coroutine + // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame. + + // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor + // will attempt to grab the lock. + _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + while (true) { + const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit != 0) { + // We did not obtain the lock. Trust that our queue entry will resume us, and allow + // suspend to complete. + break; + } + // We got the lock. However we might have already been resumed from the queue. + if (self.queue.get()) |node| { + // Whether this node is us or someone else, we tail resume it. + resume node.data; + break; + } else { + // We already got resumed, and there are none left in the queue, which means that + // we aren't even supposed to hold the lock right now. + _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); + + // There might be a queue item. If we know the queue is empty, we can be done, + // because the other actor will try to obtain the lock. + // But if there's a queue item, we are the actor which must loop and attempt + // to grab the lock again. + if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { + break; + } else { + continue; + } + } + unreachable; + } + } + + // TODO this workaround to force my_tick_node to be in the coroutine frame should + // not be necessary + var trash1 = &my_tick_node; + + return Held{ .lock = self }; + } +}; + +/// Thread-safe async/await lock that protects one piece of data. +/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and +/// are resumed when the lock is released, in order. +pub fn Locked(comptime T: type) type { + return struct { + lock: Lock, + private_data: T, + + const Self = this; + + pub const HeldLock = struct { + value: *T, + held: Lock.Held, + + pub fn release(self: HeldLock) void { + self.held.release(); + } + }; + + pub fn init(loop: *Loop, data: T) Self { + return Self{ + .lock = Lock.init(loop), + .private_data = data, + }; + } + + pub fn deinit(self: *Self) void { + self.lock.deinit(); + } + + pub async fn acquire(self: *Self) HeldLock { + return HeldLock{ + // TODO guaranteed allocation elision + .held = await (async self.lock.acquire() catch unreachable), + .value = &self.private_data, + }; + } + }; +} + +test "std.event.Lock" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var lock = Lock.init(&loop); + defer lock.deinit(); + + const handle = try async testLock(&loop, &lock); + defer cancel handle; + loop.run(); + + assert(mem.eql(i32, shared_test_data, [1]i32{3 * 10} ** 10)); +} + +async fn testLock(loop: *Loop, lock: *Lock) void { + const handle1 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node1 = Loop.NextTickNode{ + .next = undefined, + .data = handle1, + }; + loop.onNextTick(&tick_node1); + + const handle2 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node2 = Loop.NextTickNode{ + .next = undefined, + .data = handle2, + }; + loop.onNextTick(&tick_node2); + + const handle3 = async lockRunner(lock) catch @panic("out of memory"); + var tick_node3 = Loop.NextTickNode{ + .next = undefined, + .data = handle3, + }; + loop.onNextTick(&tick_node3); + + await handle1; + await handle2; + await handle3; + + // TODO this is to force tick node memory to be in the coro frame + // there should be a way to make it explicit where the memory is + var a = &tick_node1; + var b = &tick_node2; + var c = &tick_node3; +} + +var shared_test_data = [1]i32{0} ** 10; +var shared_test_index: usize = 0; + +async fn lockRunner(lock: *Lock) void { + suspend; // resumed by onNextTick + + var i: usize = 0; + while (i < 10) : (i += 1) { + const handle = await (async lock.acquire() catch @panic("out of memory")); + defer handle.release(); + + shared_test_index = 0; + while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) { + shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1; + } + } +} diff --git a/std/heap.zig b/std/heap.zig index 2e02733da1..bcace34afe 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -38,7 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void { } /// This allocator makes a syscall directly for every allocation and free. -/// TODO make this thread-safe. The windows implementation will need some atomics. +/// Thread-safe and lock-free. pub const DirectAllocator = struct { allocator: Allocator, heap_handle: ?HeapHandle, @@ -74,34 +74,34 @@ pub const DirectAllocator = struct { const alloc_size = if (alignment <= os.page_size) n else n + alignment; const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); if (addr == p.MAP_FAILED) return error.OutOfMemory; - if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n]; - var aligned_addr = addr & ~usize(alignment - 1); - aligned_addr += alignment; + const aligned_addr = (addr & ~usize(alignment - 1)) + alignment; - //We can unmap the unused portions of our mmap, but we must only - // pass munmap bytes that exist outside our allocated pages or it - // will happily eat us too + // We can unmap the unused portions of our mmap, but we must only + // pass munmap bytes that exist outside our allocated pages or it + // will happily eat us too. - //Since alignment > page_size, we are by definition on a page boundry + // Since alignment > page_size, we are by definition on a page boundary. const unused_start = addr; const unused_len = aligned_addr - 1 - unused_start; - var err = p.munmap(unused_start, unused_len); - debug.assert(p.getErrno(err) == 0); + const err = p.munmap(unused_start, unused_len); + assert(p.getErrno(err) == 0); - //It is impossible that there is an unoccupied page at the top of our - // mmap. + // It is impossible that there is an unoccupied page at the top of our + // mmap. return @intToPtr([*]u8, aligned_addr)[0..n]; }, Os.windows => { const amt = n + alignment + @sizeOf(usize); - const heap_handle = self.heap_handle orelse blk: { + const optional_heap_handle = @atomicLoad(?HeapHandle, ?self.heap_handle, builtin.AtomicOrder.SeqCst); + const heap_handle = optional_heap_handle orelse blk: { const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory; - self.heap_handle = hh; - break :blk hh; + const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh; + _ = os.windows.HeapDestroy(hh); + break :blk other_hh; }; const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory; const root_addr = @ptrToInt(ptr); diff --git a/std/mem.zig b/std/mem.zig index b52d3e9f68..555e1e249d 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const mem = this; pub const Allocator = struct { - const Error = error{OutOfMemory}; + pub const Error = error{OutOfMemory}; /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. diff --git a/std/os/index.zig b/std/os/index.zig index 52b36c351c..74a1b64f6e 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2309,6 +2309,30 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz } } +pub const LinuxEventFdError = error{ + InvalidFlagValue, + SystemResources, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + + Unexpected, +}; + +pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { + const rc = posix.eventfd(initval, flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + else => return unexpectedErrorPosix(err), + + posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, + posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, + posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, + posix.ENODEV => return LinuxEventFdError.SystemResources, + posix.ENOMEM => return LinuxEventFdError.SystemResources, + } +} + pub const PosixGetSockNameError = error{ /// Insufficient resources were available in the system to perform the operation. SystemResources, @@ -2605,10 +2629,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const MainFuncs = struct { extern fn linuxThreadMain(ctx_addr: usize) u8 { - if (@sizeOf(Context) == 0) { - return startFn({}); - } else { - return startFn(@intToPtr(*const Context, ctx_addr).*); + const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; + + switch (@typeId(@typeOf(startFn).ReturnType)) { + builtin.TypeId.Int => { + return startFn(arg); + }, + builtin.TypeId.Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), } } extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index 65aa659c82..1c15be4887 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -523,6 +523,10 @@ pub const CLONE_NEWPID = 0x20000000; pub const CLONE_NEWNET = 0x40000000; pub const CLONE_IO = 0x80000000; +pub const EFD_SEMAPHORE = 1; +pub const EFD_CLOEXEC = O_CLOEXEC; +pub const EFD_NONBLOCK = O_NONBLOCK; + pub const MS_RDONLY = 1; pub const MS_NOSUID = 2; pub const MS_NODEV = 4; @@ -1221,6 +1225,10 @@ pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout)); } +pub fn eventfd(count: u32, flags: u32) usize { + return syscall2(SYS_eventfd2, count, flags); +} + pub fn timerfd_create(clockid: i32, flags: u32) usize { return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags)); } -- cgit v1.2.3 From 9eb51e20ed1a040a617541303db760f80ffd3aa1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 10:43:29 -0400 Subject: fix crash on @ptrToInt of a *void closes #1192 --- src/ir.cpp | 6 ++++++ test/compile_errors.zig | 9 +++++++++ 2 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 98b1bd85ad..5e4c847e14 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19796,6 +19796,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr return ira->codegen->builtin_types.entry_invalid; } + if (!type_has_bits(target->value.type)) { + ir_add_error(ira, target, + buf_sprintf("pointer to size 0 type has no address")); + return ira->codegen->builtin_types.entry_invalid; + } + if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9071f0ad7e..4ed65e449d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@ptrToInt on *void", + \\export fn entry() bool { + \\ return @ptrToInt(&{}) == @ptrToInt(&{}); + \\} + , + ".tmp_source.zig:2:23: error: pointer to size 0 type has no address", + ); + cases.add( "@popCount - non-integer", \\export fn entry(x: f32) u32 { -- cgit v1.2.3 From 2ee67b7642cfeef36d8ebbc08080202b5b1d1958 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 11:13:29 -0400 Subject: langref: docs for invalid error set cast and incorrect pointer alignment also add detection of incorrect pointer alignment at compile-time of pointers that were constructed with `@intToPtr`. --- doc/langref.html.in | 54 ++++++++++++++++++++++++++++++++++++++++++++++--- src/ir.cpp | 9 +++++++++ test/compile_errors.zig | 10 +++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 8eaffb64ad..16e9023f26 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6649,12 +6649,60 @@ pub fn main() void { {#header_close#} {#header_open|Invalid Error Set Cast#} -

    TODO

    +

    At compile-time:

    + {#code_begin|test_err|error.B not a member of error set 'Set2'#} +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +comptime { + _ = @errSetCast(Set2, Set1.B); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +const Set1 = error{ + A, + B, +}; +const Set2 = error{ + A, + C, +}; +pub fn main() void { + _ = foo(Set1.B); +} +fn foo(set1: Set1) Set2 { + return @errSetCast(Set2, set1); +} + {#code_end#} {#header_close#} {#header_open|Incorrect Pointer Alignment#} -

    TODO

    - +

    At compile-time:

    + {#code_begin|test_err|pointer address 0x1 is not aligned to 4 bytes#} +comptime { + const ptr = @intToPtr(*i32, 0x1); + const aligned = @alignCast(4, ptr); +} + {#code_end#} +

    At runtime:

    + {#code_begin|exe_err#} +pub fn main() !void { + var array align(4) = []u32{ 0x11111111, 0x11111111 }; + const bytes = @sliceToBytes(array[0..]); + if (foo(bytes) != 0x11111111) return error.Wrong; +} +fn foo(bytes: []u8) u32 { + const slice4 = bytes[1..5]; + const int_slice = @bytesToSlice(u32, @alignCast(4, slice4)); + return int_slice[0]; +} + {#code_end#} {#header_close#} {#header_open|Wrong Union Field Access#}

    TODO

    diff --git a/src/ir.cpp b/src/ir.cpp index 5e4c847e14..dcd39ccfe5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19370,6 +19370,15 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 if (!val) return ira->codegen->invalid_instruction; + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) + { + ir_add_error(ira, target, + buf_sprintf("pointer address 0x%lx is not aligned to %" PRIu32 " bytes", + val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 4ed65e449d..1b76c01564 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "bad @alignCast at comptime", + \\comptime { + \\ const ptr = @intToPtr(*i32, 0x1); + \\ const aligned = @alignCast(4, ptr); + \\} + , + ".tmp_source.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", + ); + cases.add( "@ptrToInt on *void", \\export fn entry() bool { -- cgit v1.2.3 From 0ac1b83885c7f2a97a8ac25657afcb5c9b80afb4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 9 Jul 2018 17:13:31 -0400 Subject: fix non-portable format specifier --- src/ir.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index dcd39ccfe5..505a32247e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19374,7 +19374,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) { ir_add_error(ira, target, - buf_sprintf("pointer address 0x%lx is not aligned to %" PRIu32 " bytes", + buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes", val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); return ira->codegen->invalid_instruction; } -- cgit v1.2.3 From 28f9230b40ee7aa179705c39616aaf2a5f303b73 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 10:12:08 -0400 Subject: fix crash when calling comptime-known undefined function ptr closes #880 closes #1212 --- src/ir.cpp | 2 ++ test/compile_errors.zig | 13 +++++++++++++ 2 files changed, 15 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 2dc6ddad2c..10ce3254fd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13271,6 +13271,8 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction return ir_finish_anal(ira, cast_instruction->value.type); } else if (fn_ref->value.type->id == TypeTableEntryIdFn) { FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref); + if (fn_table_entry == nullptr) + return ira->codegen->builtin_types.entry_invalid; return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, fn_ref, nullptr, is_comptime, call_instruction->fn_inline); } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1b76c01564..a6db8d50b4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "use of comptime-known undefined function value", + \\const Cmd = struct { + \\ exec: fn () void, + \\}; + \\export fn entry() void { + \\ const command = Cmd{ .exec = undefined }; + \\ command.exec(); + \\} + , + ".tmp_source.zig:6:12: error: use of undefined value", + ); + cases.add( "bad @alignCast at comptime", \\comptime { -- cgit v1.2.3 From 0ce6934e2631eb3beca817d3bce12ecb13aafa13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 11:44:47 -0400 Subject: allow var args calls to async functions --- src/ir.cpp | 53 +++++++++++++++++++++++++++++++++-------------------- std/event/loop.zig | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 10ce3254fd..7f7436010e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12721,14 +12721,22 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal // for extern functions, the var args argument is not counted. // for zig functions, it is. size_t var_args_1_or_0; - if (fn_type_id->cc == CallingConventionUnspecified) { - var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; - } else { + if (fn_type_id->cc == CallingConventionC) { var_args_1_or_0 = 0; + } else { + var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; } size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + ConstExprValue *arg_tuple_value = &call_instruction->args[i]->other->value; + if (arg_tuple_value->type->id == TypeTableEntryIdArgTuple) { + call_param_count -= 1; + call_param_count += arg_tuple_value->data.x_arg_tuple.end_index - + arg_tuple_value->data.x_arg_tuple.start_index; + } + } AstNode *source_node = call_instruction->base.source_node; AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; @@ -12909,11 +12917,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal buf_sprintf("calling a generic function requires compile-time known function value")); return ira->codegen->builtin_types.entry_invalid; } - if (call_instruction->is_async && fn_type_id->is_var_args) { - ir_add_error(ira, call_instruction->fn_ref, - buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/ziglang/zig/issues/557")); - return ira->codegen->builtin_types.entry_invalid; - } // Count the arguments of the function type id we are creating size_t new_fn_arg_count = first_arg_1_or_0; @@ -12988,18 +12991,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal if (type_is_invalid(arg->value.type)) return ira->codegen->builtin_types.entry_invalid; - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - bool is_var_args = param_decl_node->data.param_decl.is_var_args; - if (is_var_args && !found_first_var_arg) { - first_var_arg = inst_fn_type_id.param_count; - found_first_var_arg = true; - } - if (arg->value.type->id == TypeTableEntryIdArgTuple) { for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index; arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1) { + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + bool is_var_args = param_decl_node->data.param_decl.is_var_args; + if (is_var_args && !found_first_var_arg) { + first_var_arg = inst_fn_type_id.param_count; + found_first_var_arg = true; + } + VariableTableEntry *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i); if (arg_var == nullptr) { ir_add_error(ira, arg, @@ -13020,10 +13023,20 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } } - } else if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, - &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) - { - return ira->codegen->builtin_types.entry_invalid; + } else { + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + bool is_var_args = param_decl_node->data.param_decl.is_var_args; + if (is_var_args && !found_first_var_arg) { + first_var_arg = inst_fn_type_id.param_count; + found_first_var_arg = true; + } + + if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, + &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) + { + return ira->codegen->builtin_types.entry_invalid; + } } } diff --git a/std/event/loop.zig b/std/event/loop.zig index 613d4f48a4..646f15875f 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -360,6 +360,28 @@ pub const Loop = struct { } } + /// This is equivalent to an async call, except instead of beginning execution of the async function, + /// it immediately returns to the caller, and the async function is queued in the event loop. It still + /// returns a promise to be awaited. + pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { + const S = struct { + async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { + suspend |p| { + handle.* = p; + var my_tick_node = Loop.NextTickNode{ + .next = undefined, + .data = p, + }; + loop.onNextTick(&my_tick_node); + } + // TODO guaranteed allocation elision for await in same func as async + return await (async func(args2) catch unreachable); + } + }; + var handle: promise->@typeOf(func).ReturnType = undefined; + return async S.asyncFunc(self, &handle, args); + } + fn workerRun(self: *Loop) void { start_over: while (true) { if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { @@ -575,3 +597,33 @@ test "std.event.Loop - basic" { loop.run(); } + +test "std.event.Loop - call" { + var da = std.heap.DirectAllocator.init(); + defer da.deinit(); + + const allocator = &da.allocator; + + var loop: Loop = undefined; + try loop.initMultiThreaded(allocator); + defer loop.deinit(); + + var did_it = false; + const handle = try loop.call(testEventLoop); + const handle2 = try loop.call(testEventLoop2, handle, &did_it); + defer cancel handle2; + + loop.run(); + + assert(did_it); +} + +async fn testEventLoop() i32 { + return 1234; +} + +async fn testEventLoop2(h: promise->i32, did_it: *bool) void { + const value = await h; + assert(value == 1234); + did_it.* = true; +} -- cgit v1.2.3 From 574e31f0a046aa6e6fad73fff2cbbb3617fe1bae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 10 Jul 2018 20:18:43 -0400 Subject: self-hosted: first passing test * introduce std.atomic.Int * add src-self-hosted/test.zig which is tested by the main test suite - it fully utilizes the multithreaded async/await event loop so the tests should Go Fast * `stage2/bin/zig build-obj test.zig` is able to spit out an error if 2 exported functions collide * ability for `zig test` to accept `--object` and `--assembly` arguments * std.build: TestStep supports addLibPath and addObjectFile --- CMakeLists.txt | 1 + build.zig | 152 ++++++++++++++++++++--------------- src-self-hosted/errmsg.zig | 18 +++-- src-self-hosted/introspect.zig | 5 ++ src-self-hosted/main.zig | 36 ++++----- src-self-hosted/module.zig | 98 +++++++++++++++++++---- src-self-hosted/test.zig | 176 +++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 10 ++- std/atomic/index.zig | 2 + std/atomic/int.zig | 19 +++++ std/build.zig | 22 ++++++ 11 files changed, 432 insertions(+), 107 deletions(-) create mode 100644 src-self-hosted/test.zig create mode 100644 std/atomic/int.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index eeb0ec2058..559b3b6964 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,6 +431,7 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" "atomic/index.zig" + "atomic/int.zig" "atomic/queue_mpmc.zig" "atomic/queue_mpsc.zig" "atomic/stack.zig" diff --git a/build.zig b/build.zig index fd154c7504..273048d458 100644 --- a/build.zig +++ b/build.zig @@ -35,70 +35,27 @@ pub fn build(b: *Builder) !void { "BUILD_INFO", }); var index: usize = 0; - const cmake_binary_dir = nextValue(&index, build_info); - const cxx_compiler = nextValue(&index, build_info); - const llvm_config_exe = nextValue(&index, build_info); - const lld_include_dir = nextValue(&index, build_info); - const lld_libraries = nextValue(&index, build_info); - const std_files = nextValue(&index, build_info); - const c_header_files = nextValue(&index, build_info); - const dia_guids_lib = nextValue(&index, build_info); + var ctx = Context{ + .cmake_binary_dir = nextValue(&index, build_info), + .cxx_compiler = nextValue(&index, build_info), + .llvm_config_exe = nextValue(&index, build_info), + .lld_include_dir = nextValue(&index, build_info), + .lld_libraries = nextValue(&index, build_info), + .std_files = nextValue(&index, build_info), + .c_header_files = nextValue(&index, build_info), + .dia_guids_lib = nextValue(&index, build_info), + .llvm = undefined, + }; + ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); - const llvm = findLLVM(b, llvm_config_exe) catch unreachable; + var test_stage2 = b.addTest("src-self-hosted/test.zig"); + test_stage2.setBuildMode(builtin.Mode.Debug); var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); exe.setBuildMode(mode); - // This is for finding /lib/libz.a on alpine linux. - // TODO turn this into -Dextra-lib-path=/lib option - exe.addLibPath("/lib"); - - exe.addIncludeDir("src"); - exe.addIncludeDir(cmake_binary_dir); - addCppLib(b, exe, cmake_binary_dir, "zig_cpp"); - if (lld_include_dir.len != 0) { - exe.addIncludeDir(lld_include_dir); - var it = mem.split(lld_libraries, ";"); - while (it.next()) |lib| { - exe.addObjectFile(lib); - } - } else { - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); - addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); - } - dependOnLib(exe, llvm); - - if (exe.target.getOs() == builtin.Os.linux) { - const libstdcxx_path_padded = try b.exec([][]const u8{ - cxx_compiler, - "-print-file-name=libstdc++.a", - }); - const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; - if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { - warn( - \\Unable to determine path to libstdc++.a - \\On Fedora, install libstdc++-static and try again. - \\ - ); - return error.RequiredLibraryNotFound; - } - exe.addObjectFile(libstdcxx_path); - - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isDarwin()) { - exe.linkSystemLibrary("c++"); - } - - if (dia_guids_lib.len != 0) { - exe.addObjectFile(dia_guids_lib); - } - - if (exe.target.getOs() != builtin.Os.windows) { - exe.linkSystemLibrary("xml2"); - } - exe.linkSystemLibrary("c"); + try configureStage2(b, test_stage2, ctx); + try configureStage2(b, exe, ctx); b.default_step.dependOn(&exe.step); @@ -110,12 +67,16 @@ pub fn build(b: *Builder) !void { exe.setVerboseLink(verbose_link_exe); b.installArtifact(exe); - installStdLib(b, std_files); - installCHeaders(b, c_header_files); + installStdLib(b, ctx.std_files); + installCHeaders(b, ctx.c_header_files); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false; + const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); + test_stage2_step.dependOn(&test_stage2.step); + test_step.dependOn(test_stage2_step); + test_step.dependOn(docs_step); test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", with_lldb)); @@ -133,7 +94,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addGenHTests(b, test_filter)); } -fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void { +fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void { for (dep.libdirs.toSliceConst()) |lib_dir| { lib_exe_obj.addLibPath(lib_dir); } @@ -148,7 +109,7 @@ fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) vo } } -fn addCppLib(b: *Builder, lib_exe_obj: *std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void { +fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void { const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib"; lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable); } @@ -254,3 +215,68 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 { } } } + +fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { + // This is for finding /lib/libz.a on alpine linux. + // TODO turn this into -Dextra-lib-path=/lib option + exe.addLibPath("/lib"); + + exe.addIncludeDir("src"); + exe.addIncludeDir(ctx.cmake_binary_dir); + addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); + if (ctx.lld_include_dir.len != 0) { + exe.addIncludeDir(ctx.lld_include_dir); + var it = mem.split(ctx.lld_libraries, ";"); + while (it.next()) |lib| { + exe.addObjectFile(lib); + } + } else { + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm"); + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf"); + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff"); + addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib"); + } + dependOnLib(exe, ctx.llvm); + + if (exe.target.getOs() == builtin.Os.linux) { + const libstdcxx_path_padded = try b.exec([][]const u8{ + ctx.cxx_compiler, + "-print-file-name=libstdc++.a", + }); + const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?; + if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { + warn( + \\Unable to determine path to libstdc++.a + \\On Fedora, install libstdc++-static and try again. + \\ + ); + return error.RequiredLibraryNotFound; + } + exe.addObjectFile(libstdcxx_path); + + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isDarwin()) { + exe.linkSystemLibrary("c++"); + } + + if (ctx.dia_guids_lib.len != 0) { + exe.addObjectFile(ctx.dia_guids_lib); + } + + if (exe.target.getOs() != builtin.Os.windows) { + exe.linkSystemLibrary("xml2"); + } + exe.linkSystemLibrary("c"); +} + +const Context = struct { + cmake_binary_dir: []const u8, + cxx_compiler: []const u8, + llvm_config_exe: []const u8, + lld_include_dir: []const u8, + lld_libraries: []const u8, + std_files: []const u8, + c_header_files: []const u8, + dia_guids_lib: []const u8, + llvm: LibraryDep, +}; diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index b6fd78d8f6..a92b5145ce 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -11,11 +11,15 @@ pub const Color = enum { On, }; +pub const Span = struct { + first: ast.TokenIndex, + last: ast.TokenIndex, +}; + pub const Msg = struct { path: []const u8, text: []u8, - first_token: TokenIndex, - last_token: TokenIndex, + span: Span, tree: *ast.Tree, }; @@ -39,8 +43,10 @@ pub fn createFromParseError( .tree = tree, .path = path, .text = text_buf.toOwnedSlice(), - .first_token = loc_token, - .last_token = loc_token, + .span = Span{ + .first = loc_token, + .last = loc_token, + }, }); errdefer allocator.destroy(msg); @@ -48,8 +54,8 @@ pub fn createFromParseError( } pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void { - const first_token = msg.tree.tokens.at(msg.first_token); - const last_token = msg.tree.tokens.at(msg.last_token); + const first_token = msg.tree.tokens.at(msg.span.first); + const last_token = msg.tree.tokens.at(msg.span.last); const start_loc = msg.tree.tokenLocationPtr(0, first_token); const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); if (!color_on) { diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 74084b48c6..ecd04c4467 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 { return error.ZigLibDirNotFound; }; } + +/// Caller must free result +pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 { + return std.mem.dupe(allocator, u8, "zig-cache"); +} diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index fe94a4460a..d7ead0ba32 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -481,29 +481,29 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo module.link_out_file = flags.single("out-file"); try module.build(); - const process_build_events_handle = try async processBuildEvents(module, true); + const process_build_events_handle = try async processBuildEvents(module, color); defer cancel process_build_events_handle; loop.run(); } -async fn processBuildEvents(module: *Module, watch: bool) void { - while (watch) { - // TODO directly awaiting async should guarantee memory allocation elision - const build_event = await (async module.events.get() catch unreachable); +async fn processBuildEvents(module: *Module, color: errmsg.Color) void { + // TODO directly awaiting async should guarantee memory allocation elision + const build_event = await (async module.events.get() catch unreachable); - switch (build_event) { - Module.Event.Ok => { - std.debug.warn("Build succeeded\n"); - return; - }, - Module.Event.Error => |err| { - std.debug.warn("build failed: {}\n", @errorName(err)); - @panic("TODO error return trace"); - }, - Module.Event.Fail => |errs| { - @panic("TODO print compile error messages"); - }, - } + switch (build_event) { + Module.Event.Ok => { + std.debug.warn("Build succeeded\n"); + return; + }, + Module.Event.Error => |err| { + std.debug.warn("build failed: {}\n", @errorName(err)); + @panic("TODO error return trace"); + }, + Module.Event.Fail => |msgs| { + for (msgs) |msg| { + errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1); + } + }, } } diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 24be228eb8..44954e4cd1 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -89,12 +89,9 @@ pub const Module = struct { /// the build is complete. build_group: event.Group(BuildError!void), - const BuildErrorsList = std.SegmentedList(BuildErrorDesc, 1); + compile_errors: event.Locked(CompileErrList), - pub const BuildErrorDesc = struct { - code: BuildError, - text: []const u8, - }; + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes pub const BuildError = error{ @@ -131,11 +128,12 @@ pub const Module = struct { NoStdHandles, Overflow, NotSupported, + BufferTooSmall, }; pub const Event = union(enum) { Ok, - Fail: []errmsg.Msg, + Fail: []*errmsg.Msg, Error: BuildError, }; @@ -249,6 +247,7 @@ pub const Module = struct { .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), .build_group = event.Group(BuildError!void).init(loop), + .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), }); } @@ -288,7 +287,17 @@ pub const Module = struct { await (async self.events.put(Event{ .Error = err }) catch unreachable); return; }; - await (async self.events.put(Event.Ok) catch unreachable); + const compile_errors = blk: { + const held = await (async self.compile_errors.acquire() catch unreachable); + defer held.release(); + break :blk held.value.toOwnedSlice(); + }; + + if (compile_errors.len == 0) { + await (async self.events.put(Event.Ok) catch unreachable); + } else { + await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable); + } // for now we stop after 1 return; } @@ -310,10 +319,13 @@ pub const Module = struct { }; errdefer self.a().free(source_code); - var parsed_file = ParsedFile{ - .tree = try std.zig.parse(self.a(), source_code), + const parsed_file = try self.a().create(ParsedFile{ + .tree = undefined, .realpath = root_src_real_path, - }; + }); + errdefer self.a().destroy(parsed_file); + + parsed_file.tree = try std.zig.parse(self.a(), source_code); errdefer parsed_file.tree.deinit(); const tree = &parsed_file.tree; @@ -337,7 +349,7 @@ pub const Module = struct { const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else { @panic("TODO add compile error"); //try self.addCompileError( - // &parsed_file, + // parsed_file, // fn_proto.fn_token, // fn_proto.fn_token + 1, // "missing function name", @@ -357,7 +369,7 @@ pub const Module = struct { }); errdefer self.a().destroy(fn_decl); - try decl_group.call(addTopLevelDecl, self, tree, &fn_decl.base); + try decl_group.call(addTopLevelDecl, self, parsed_file, &fn_decl.base); }, ast.Node.Id.TestDecl => @panic("TODO"), else => unreachable, @@ -367,20 +379,56 @@ pub const Module = struct { try await (async self.build_group.wait() catch unreachable); } - async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void { - const is_export = decl.isExported(tree); + async fn addTopLevelDecl(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { + const is_export = decl.isExported(&parsed_file.tree); if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, decl); + try self.build_group.call(verifyUniqueSymbol, self, parsed_file, decl); } } - async fn verifyUniqueSymbol(self: *Module, decl: *Decl) !void { + fn addCompileError(self: *Module, parsed_file: *ParsedFile, span: errmsg.Span, comptime fmt: []const u8, args: ...) !void { + const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); + errdefer self.loop.allocator.free(text); + + try self.build_group.call(addCompileErrorAsync, self, parsed_file, span.first, span.last, text); + } + + async fn addCompileErrorAsync( + self: *Module, + parsed_file: *ParsedFile, + first_token: ast.TokenIndex, + last_token: ast.TokenIndex, + text: []u8, + ) !void { + const msg = try self.loop.allocator.create(errmsg.Msg{ + .path = parsed_file.realpath, + .text = text, + .span = errmsg.Span{ + .first = first_token, + .last = last_token, + }, + .tree = &parsed_file.tree, + }); + errdefer self.loop.allocator.destroy(msg); + + const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + defer compile_errors.release(); + + try compile_errors.value.append(msg); + } + + async fn verifyUniqueSymbol(self: *Module, parsed_file: *ParsedFile, decl: *Decl) !void { const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable); defer exported_symbol_names.release(); if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| { - @panic("TODO report compile error"); + try self.addCompileError( + parsed_file, + decl.getSpan(), + "exported symbol collision: '{}'", + decl.name, + ); } } @@ -503,6 +551,22 @@ pub const Decl = struct { } } + pub fn getSpan(base: *const Decl) errmsg.Span { + switch (base.id) { + Id.Fn => { + const fn_decl = @fieldParentPtr(Fn, "base", base); + const fn_proto = fn_decl.fn_proto; + const start = fn_proto.fn_token; + const end = fn_proto.name_token orelse start; + return errmsg.Span{ + .first = start, + .last = end + 1, + }; + }, + else => @panic("TODO"), + } + } + pub const Resolution = enum { Unresolved, InProgress, diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig new file mode 100644 index 0000000000..7ce7cf6ee3 --- /dev/null +++ b/src-self-hosted/test.zig @@ -0,0 +1,176 @@ +const std = @import("std"); +const mem = std.mem; +const builtin = @import("builtin"); +const Target = @import("target.zig").Target; +const Module = @import("module.zig").Module; +const introspect = @import("introspect.zig"); +const assertOrPanic = std.debug.assertOrPanic; +const errmsg = @import("errmsg.zig"); + +test "compile errors" { + var ctx: TestContext = undefined; + try ctx.init(); + defer ctx.deinit(); + + try ctx.testCompileError( + \\export fn entry() void {} + \\export fn entry() void {} + , file1, 2, 8, "exported symbol collision: 'entry'"); + + try ctx.run(); +} + +const file1 = "1.zig"; + +const TestContext = struct { + loop: std.event.Loop, + zig_lib_dir: []u8, + direct_allocator: std.heap.DirectAllocator, + arena: std.heap.ArenaAllocator, + zig_cache_dir: []u8, + file_index: std.atomic.Int(usize), + group: std.event.Group(error!void), + any_err: error!void, + + const tmp_dir_name = "stage2_test_tmp"; + + fn init(self: *TestContext) !void { + self.* = TestContext{ + .any_err = {}, + .direct_allocator = undefined, + .arena = undefined, + .loop = undefined, + .zig_lib_dir = undefined, + .zig_cache_dir = undefined, + .group = undefined, + .file_index = std.atomic.Int(usize).init(0), + }; + + self.direct_allocator = std.heap.DirectAllocator.init(); + errdefer self.direct_allocator.deinit(); + + self.arena = std.heap.ArenaAllocator.init(&self.direct_allocator.allocator); + errdefer self.arena.deinit(); + + // TODO faster allocator for coroutines that is thread-safe/lock-free + try self.loop.initMultiThreaded(&self.direct_allocator.allocator); + errdefer self.loop.deinit(); + + self.group = std.event.Group(error!void).init(&self.loop); + errdefer self.group.cancelAll(); + + self.zig_lib_dir = try introspect.resolveZigLibDir(&self.arena.allocator); + errdefer self.arena.allocator.free(self.zig_lib_dir); + + self.zig_cache_dir = try introspect.resolveZigCacheDir(&self.arena.allocator); + errdefer self.arena.allocator.free(self.zig_cache_dir); + + try std.os.makePath(&self.arena.allocator, tmp_dir_name); + errdefer std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; + } + + fn deinit(self: *TestContext) void { + std.os.deleteTree(&self.arena.allocator, tmp_dir_name) catch {}; + self.arena.allocator.free(self.zig_cache_dir); + self.arena.allocator.free(self.zig_lib_dir); + self.loop.deinit(); + self.arena.deinit(); + self.direct_allocator.deinit(); + } + + fn run(self: *TestContext) !void { + const handle = try self.loop.call(waitForGroup, self); + defer cancel handle; + self.loop.run(); + return self.any_err; + } + + async fn waitForGroup(self: *TestContext) void { + self.any_err = await (async self.group.wait() catch unreachable); + } + + fn testCompileError( + self: *TestContext, + source: []const u8, + path: []const u8, + line: usize, + column: usize, + msg: []const u8, + ) !void { + var file_index_buf: [20]u8 = undefined; + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next()); + const file1_path = try std.os.path.join(&self.arena.allocator, tmp_dir_name, file_index, file1); + + if (std.os.path.dirname(file1_path)) |dirname| { + try std.os.makePath(&self.arena.allocator, dirname); + } + + // TODO async I/O + try std.io.writeFile(&self.arena.allocator, file1_path, source); + + var module = try Module.create( + &self.loop, + "test", + file1_path, + Target.Native, + Module.Kind.Obj, + builtin.Mode.Debug, + self.zig_lib_dir, + self.zig_cache_dir, + ); + errdefer module.destroy(); + + try module.build(); + + try self.group.call(getModuleEvent, module, source, path, line, column, msg); + } + + async fn getModuleEvent( + module: *Module, + source: []const u8, + path: []const u8, + line: usize, + column: usize, + text: []const u8, + ) !void { + defer module.destroy(); + const build_event = await (async module.events.get() catch unreachable); + + switch (build_event) { + Module.Event.Ok => { + @panic("build incorrectly succeeded"); + }, + Module.Event.Error => |err| { + @panic("build incorrectly failed"); + }, + Module.Event.Fail => |msgs| { + assertOrPanic(msgs.len != 0); + for (msgs) |msg| { + if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) { + const first_token = msg.tree.tokens.at(msg.span.first); + const last_token = msg.tree.tokens.at(msg.span.first); + const start_loc = msg.tree.tokenLocationPtr(0, first_token); + if (start_loc.line + 1 == line and start_loc.column + 1 == column) { + return; + } + } + } + std.debug.warn( + "\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", + source, + path, + line, + column, + text, + ); + std.debug.warn("\n====found:========\n"); + var stderr = try std.io.getStdErr(); + for (msgs) |msg| { + try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); + } + std.debug.warn("============\n"); + return error.TestFailed; + }, + } + } +}; diff --git a/src/main.cpp b/src/main.cpp index a409778a78..5f96953f21 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -891,15 +891,19 @@ int main(int argc, char **argv) { add_package(g, cur_pkg, g->root_package); - if (cmd == CmdBuild || cmd == CmdRun) { - codegen_set_emit_file_type(g, emit_file_type); - + if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { for (size_t i = 0; i < objects.length; i += 1) { codegen_add_object(g, buf_create_from_str(objects.at(i))); } for (size_t i = 0; i < asm_files.length; i += 1) { codegen_add_assembly(g, buf_create_from_str(asm_files.at(i))); } + } + + + if (cmd == CmdBuild || cmd == CmdRun) { + codegen_set_emit_file_type(g, emit_file_type); + codegen_build(g); codegen_link(g, out_file); if (timing_info) diff --git a/std/atomic/index.zig b/std/atomic/index.zig index c0ea5be183..cf344a8231 100644 --- a/std/atomic/index.zig +++ b/std/atomic/index.zig @@ -1,9 +1,11 @@ pub const Stack = @import("stack.zig").Stack; pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc; pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc; +pub const Int = @import("int.zig").Int; test "std.atomic" { _ = @import("stack.zig"); _ = @import("queue_mpsc.zig"); _ = @import("queue_mpmc.zig"); + _ = @import("int.zig"); } diff --git a/std/atomic/int.zig b/std/atomic/int.zig new file mode 100644 index 0000000000..7042bca78d --- /dev/null +++ b/std/atomic/int.zig @@ -0,0 +1,19 @@ +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; + +/// Thread-safe, lock-free integer +pub fn Int(comptime T: type) type { + return struct { + value: T, + + pub const Self = this; + + pub fn init(init_val: T) Self { + return Self{ .value = init_val }; + } + + pub fn next(self: *Self) T { + return @atomicRmw(T, &self.value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); + } + }; +} diff --git a/std/build.zig b/std/build.zig index 24fa85383a..cea760e8a2 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1596,6 +1596,8 @@ pub const TestStep = struct { target: Target, exec_cmd_args: ?[]const ?[]const u8, include_dirs: ArrayList([]const u8), + lib_paths: ArrayList([]const u8), + object_files: ArrayList([]const u8), pub fn init(builder: *Builder, root_src: []const u8) TestStep { const step_name = builder.fmt("test {}", root_src); @@ -1611,9 +1613,15 @@ pub const TestStep = struct { .target = Target{ .Native = {} }, .exec_cmd_args = null, .include_dirs = ArrayList([]const u8).init(builder.allocator), + .lib_paths = ArrayList([]const u8).init(builder.allocator), + .object_files = ArrayList([]const u8).init(builder.allocator), }; } + pub fn addLibPath(self: *TestStep, path: []const u8) void { + self.lib_paths.append(path) catch unreachable; + } + pub fn setVerbose(self: *TestStep, value: bool) void { self.verbose = value; } @@ -1638,6 +1646,10 @@ pub const TestStep = struct { self.filter = text; } + pub fn addObjectFile(self: *TestStep, path: []const u8) void { + self.object_files.append(path) catch unreachable; + } + pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { self.target = Target{ .Cross = CrossTarget{ @@ -1699,6 +1711,11 @@ pub const TestStep = struct { try zig_args.append(self.name_prefix); } + for (self.object_files.toSliceConst()) |object_file| { + try zig_args.append("--object"); + try zig_args.append(builder.pathFromRoot(object_file)); + } + { var it = self.link_libs.iterator(); while (true) { @@ -1734,6 +1751,11 @@ pub const TestStep = struct { try zig_args.append(rpath); } + for (self.lib_paths.toSliceConst()) |lib_path| { + try zig_args.append("--library-path"); + try zig_args.append(lib_path); + } + for (builder.lib_paths.toSliceConst()) |lib_path| { try zig_args.append("--library-path"); try zig_args.append(lib_path); -- cgit v1.2.3 From 3f30897fdcdb6c5579bc5609dda9746f67551870 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 13:23:37 -0400 Subject: add compile error for disallowed types in extern structs closes #1218 --- src/analyze.cpp | 23 ++++++++++++++++++++--- std/c/darwin.zig | 2 +- test/compile_errors.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 9b60f7374a..5635cce411 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1430,10 +1430,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: + case TypeTableEntryIdVoid: return false; case TypeTableEntryIdOpaque: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVoid: case TypeTableEntryIdBool: return true; case TypeTableEntryIdInt: @@ -1460,7 +1460,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdOptional: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; - return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; + if (child_type->id != TypeTableEntryIdPointer && child_type->id != TypeTableEntryIdFn) { + return false; + } + return type_allowed_in_extern(g, child_type); } case TypeTableEntryIdEnum: return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked; @@ -1637,7 +1640,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c fn_type_id.return_type = specified_return_type; } - if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, fn_type_id.return_type)) { + if (!calling_convention_allows_zig_types(fn_type_id.cc) && + fn_type_id.return_type->id != TypeTableEntryIdVoid && + !type_allowed_in_extern(g, fn_type_id.return_type)) + { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", buf_ptr(&fn_type_id.return_type->name), @@ -1939,6 +1945,17 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) { break; } + if (struct_type->data.structure.layout == ContainerLayoutExtern) { + if (!type_allowed_in_extern(g, field_type)) { + AstNode *field_source_node = decl_node->data.container_decl.fields.at(i); + add_node_error(g, field_source_node, + buf_sprintf("extern structs cannot contain fields of type '%s'", + buf_ptr(&field_type->name))); + struct_type->data.structure.is_invalid = true; + break; + } + } + if (!type_has_bits(field_type)) continue; diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 133ef62f05..4189dfeadc 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -44,7 +44,7 @@ pub const timezone = extern struct { tz_dsttime: i32, }; -pub const mach_timebase_info_data = struct { +pub const mach_timebase_info_data = extern struct { numer: u32, denom: u32, }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a6db8d50b4..58c73b8ae4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,33 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "optional pointer to void in extern struct", + \\const Foo = extern struct { + \\ x: ?*const void, + \\}; + \\const Bar = extern struct { + \\ foo: Foo, + \\ y: i32, + \\}; + \\export fn entry(bar: *Bar) void {} + , + ".tmp_source.zig:2:5: error: extern structs cannot contain fields of type '?*const void'", + ); + + cases.add( + "use of comptime-known undefined function value", + \\const Cmd = struct { + \\ exec: fn () void, + \\}; + \\export fn entry() void { + \\ const command = Cmd{ .exec = undefined }; + \\ command.exec(); + \\} + , + ".tmp_source.zig:6:12: error: use of undefined value", + ); + cases.add( "use of comptime-known undefined function value", \\const Cmd = struct { -- cgit v1.2.3 From ce11d6d16cf388ec7abff9680ee3a263185a9986 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 11 Jul 2018 21:37:47 -0400 Subject: ir: refactor lvalues --- src/all_types.hpp | 11 ++++---- src/ir.cpp | 77 ++++++++++++++++++++++++++----------------------------- src/ir_print.cpp | 6 ++--- 3 files changed, 44 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 6dcf1894d8..2da0677e1b 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2005,12 +2005,6 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; -struct LVal { - bool is_ptr; - bool is_const; - bool is_volatile; -}; - enum IrInstructionId { IrInstructionIdInvalid, IrInstructionIdBr, @@ -2972,6 +2966,11 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; +enum LVal { + LValNone, + LValPtr, +}; + struct IrInstructionDeclRef { IrInstruction base; diff --git a/src/ir.cpp b/src/ir.cpp index 7f7436010e..eb62cc8bdf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -39,9 +39,6 @@ struct IrAnalyze { IrBasicBlock *const_predecessor_bb; }; -static const LVal LVAL_NONE = { false, false, false }; -static const LVal LVAL_PTR = { true, false, false }; - enum ConstCastResultId { ConstCastResultIdOk, ConstCastResultIdErrSet, @@ -3164,7 +3161,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, case ReturnKindError: { assert(expr_node); - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr); @@ -3192,7 +3189,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false); - if (lval.is_ptr) + if (lval == LValPtr) return unwrapped_ptr; else return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); @@ -3357,7 +3354,7 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no } static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LVAL_PTR); + IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) @@ -3368,7 +3365,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) } static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LVAL_PTR); + IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr); if (lvalue == irb->codegen->invalid_instruction) return lvalue; IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); @@ -3470,7 +3467,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As AstNode *op1_node = node->data.bin_op_expr.op1; AstNode *op2_node = node->data.bin_op_expr.op2; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LVAL_PTR); + IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -3657,7 +3654,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, Buf *variable_name = node->data.symbol_expr.symbol; - if (buf_eql_str(variable_name, "_") && lval.is_ptr) { + if (buf_eql_str(variable_name, "_") && lval == LValPtr) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, node); const_instruction->base.value.type = get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_void, false); @@ -3669,8 +3666,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name); if (primitive_table_entry) { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value); - if (lval.is_ptr) { - return ir_build_ref(irb, scope, node, value, lval.is_const, lval.is_volatile); + if (lval == LValPtr) { + return ir_build_ref(irb, scope, node, value, false, false); } else { return value; } @@ -3679,7 +3676,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name); if (var) { IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var); - if (lval.is_ptr) + if (lval == LValPtr) return var_ptr; else return ir_build_load_ptr(irb, scope, node, var_ptr); @@ -3705,7 +3702,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode assert(node->type == NodeTypeArrayAccessExpr); AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; - IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LVAL_PTR); + IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr); if (array_ref_instruction == irb->codegen->invalid_instruction) return array_ref_instruction; @@ -3716,7 +3713,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, subscript_instruction, true, PtrLenSingle); - if (lval.is_ptr) + if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); @@ -3728,7 +3725,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode AstNode *container_ref_node = node->data.field_access_expr.struct_expr; Buf *field_name = node->data.field_access_expr.field_name; - IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LVAL_PTR); + IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr); if (container_ref_instruction == irb->codegen->invalid_instruction) return container_ref_instruction; @@ -4386,7 +4383,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdField: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR); + IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr); if (arg0_value == irb->codegen->invalid_instruction) return arg0_value; @@ -4397,7 +4394,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value); - if (lval.is_ptr) + if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); @@ -4928,18 +4925,18 @@ static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, Ast } static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) { - return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LVAL_NONE); + return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); } static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) { - if (!lval.is_ptr) + if (lval != LValPtr) return value; if (value == irb->codegen->invalid_instruction) return value; // We needed a pointer to a value, but we got a value. So we create // an instruction which just makes a const pointer of it. - return ir_build_ref(irb, scope, value->source_node, value, lval.is_const, lval.is_volatile); + return ir_build_ref(irb, scope, value->source_node, value, false, false); } static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5001,7 +4998,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -5009,7 +5006,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode if (payload_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - if (lval.is_ptr) + if (lval == LValPtr) return payload_ptr; return ir_build_load_ptr(irb, scope, source_node, payload_ptr); @@ -5046,7 +5043,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval); case PrefixOpAddrOf: { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval); + return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval); } } zig_unreachable(); @@ -5186,7 +5183,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } else { payload_scope = scope; } - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR); + IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); @@ -5269,7 +5266,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR); + IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); @@ -5413,7 +5410,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo } assert(elem_node->type == NodeTypeSymbol); - IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LVAL_PTR); + IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr); if (array_val_ptr == irb->codegen->invalid_instruction) return array_val_ptr; @@ -5700,7 +5697,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no AstNode *else_node = node->data.test_expr.else_node; bool var_is_ptr = node->data.test_expr.var_is_ptr; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; @@ -5778,7 +5775,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * Buf *var_symbol = node->data.if_err_expr.var_symbol; Buf *err_symbol = node->data.if_err_expr.err_symbol; - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LVAL_PTR); + IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; @@ -5904,7 +5901,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * assert(node->type == NodeTypeSwitchExpr); AstNode *target_node = node->data.switch_expr.expr; - IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LVAL_PTR); + IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr); if (target_value_ptr == irb->codegen->invalid_instruction) return target_value_ptr; IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); @@ -6277,7 +6274,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_extra(irb, array_node, scope, LVAL_PTR); + IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr); if (ptr_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6311,11 +6308,11 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LVAL_NONE); + return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); } - IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LVAL_PTR); + IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr); if (err_union_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6868,7 +6865,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node); if (ptr_instruction == irb->codegen->invalid_instruction) return ptr_instruction; - if (lval.is_ptr) + if (lval == LValPtr) return ptr_instruction; return ir_build_load_ptr(irb, scope, node, ptr_instruction); @@ -6884,12 +6881,12 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; - IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR); + IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); - if (lval.is_ptr) + if (lval == LValPtr) return unwrapped_ptr; return ir_build_load_ptr(irb, scope, node, unwrapped_ptr); @@ -6959,7 +6956,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc } static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) { - return ir_gen_node_extra(irb, node, scope, LVAL_NONE); + return ir_gen_node_extra(irb, node, scope, LValNone); } static void invalidate_exec(IrExecutable *exec) { @@ -7089,7 +7086,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup"); } - IrInstruction *result = ir_gen_node_extra(irb, node, scope, LVAL_NONE); + IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone); assert(result); if (irb->exec->invalid) return false; @@ -19752,7 +19749,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, Tld *tld = instruction->tld; LVal lval = instruction->lval; - resolve_top_level_decl(ira->codegen, tld, lval.is_ptr, instruction->base.source_node); + resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node); if (tld->resolution == TldResolutionInvalid) return ira->codegen->builtin_types.entry_invalid; @@ -19773,7 +19770,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } - if (lval.is_ptr) { + if (lval == LValPtr) { ir_link_new_instruction(var_ptr, &instruction->base); return var_ptr->value.type; } else { @@ -19794,7 +19791,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn_entry); - if (lval.is_ptr) { + if (lval == LValPtr) { IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false); ir_link_new_instruction(ptr_instr, &instruction->base); return ptr_instr->value.type; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 780cf9e756..6182958d0a 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -1005,10 +1005,8 @@ static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { } static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) { - const char *ptr_str = instruction->lval.is_ptr ? "ptr " : ""; - const char *const_str = instruction->lval.is_const ? "const " : ""; - const char *volatile_str = instruction->lval.is_volatile ? "volatile " : ""; - fprintf(irp->f, "declref %s%s%s%s", const_str, volatile_str, ptr_str, buf_ptr(instruction->tld->name)); + const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : ""; + fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); } static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) { -- cgit v1.2.3 From 5354d1f5fc496beb8313488ea1690e02e9c630fa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 12:34:42 -0400 Subject: allow == for comparing optional pointers closes #658 --- src/codegen.cpp | 4 +--- src/ir.cpp | 30 +++++++++++++----------------- test/cases/optional.zig | 21 +++++++++++++++++++++ 3 files changed, 35 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 54e2da7d61..3f54c120b4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2249,10 +2249,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); } else if (type_entry->id == TypeTableEntryIdEnum || type_entry->id == TypeTableEntryIdErrorSet || - type_entry->id == TypeTableEntryIdPointer || type_entry->id == TypeTableEntryIdBool || - type_entry->id == TypeTableEntryIdPromise || - type_entry->id == TypeTableEntryIdFn) + get_codegen_ptr_type(type_entry) != nullptr) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); diff --git a/src/ir.cpp b/src/ir.cpp index eb62cc8bdf..f452ef43e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11147,7 +11147,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp if (type_is_invalid(resolved_type)) return resolved_type; - + bool operator_allowed; switch (resolved_type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); // handled above @@ -11156,6 +11156,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdComptimeInt: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: + operator_allowed = true; break; case TypeTableEntryIdBool: @@ -11170,19 +11171,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: - if (!is_equality_cmp) { - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - break; - case TypeTableEntryIdEnum: - if (!is_equality_cmp) { - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } + operator_allowed = is_equality_cmp; break; case TypeTableEntryIdUnreachable: @@ -11190,12 +11180,18 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdStruct: case TypeTableEntryIdUndefined: case TypeTableEntryIdNull: - case TypeTableEntryIdOptional: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdUnion: - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->builtin_types.entry_invalid; + operator_allowed = false; + break; + case TypeTableEntryIdOptional: + operator_allowed = is_equality_cmp && get_codegen_ptr_type(resolved_type) != nullptr; + break; + } + if (!operator_allowed) { + ir_add_error_node(ira, source_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); + return ira->codegen->builtin_types.entry_invalid; } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); diff --git a/test/cases/optional.zig b/test/cases/optional.zig index 0129252dab..d43682bbec 100644 --- a/test/cases/optional.zig +++ b/test/cases/optional.zig @@ -7,3 +7,24 @@ test "optional pointer to size zero struct" { var o: ?*EmptyStruct = &e; assert(o != null); } + +test "equality compare nullable pointers" { + testNullPtrsEql(); + comptime testNullPtrsEql(); +} + +fn testNullPtrsEql() void { + var number: i32 = 1234; + + var x: ?*i32 = null; + var y: ?*i32 = null; + assert(x == y); + y = &number; + assert(x != y); + assert(x != &number); + assert(&number != x); + x = &number; + assert(x == y); + assert(x == &number); + assert(&number == x); +} -- cgit v1.2.3 From 860d3da9156a0b1f4a1e3e644b423da3e768bb86 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 13:37:01 -0400 Subject: ir: remove dead code --- src/ir.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index f452ef43e0..23ca901c99 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -246,8 +246,6 @@ static void ir_ref_bb(IrBasicBlock *bb) { static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { assert(instruction->id != IrInstructionIdInvalid); instruction->ref_count += 1; - if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction)) - ir_ref_bb(instruction->owner_bb); } static void ir_ref_var(VariableTableEntry *var) { -- cgit v1.2.3 From 171f33b961fd19a91bc9dc80e1b69865d06f3ff2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 14:18:37 -0400 Subject: ir: remove unnecessary and probably buggy code --- src/ir.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 23ca901c99..1b197d3ed1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9237,26 +9237,9 @@ static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_typ } 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; - IrInstructionVarPtr *var_ptr_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - var_ptr_instruction->var = old_var_ptr_instruction->var; - new_instruction = &var_ptr_instruction->base; - } else if (old_instruction->id == IrInstructionIdFieldPtr) { - IrInstructionFieldPtr *field_ptr_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - new_instruction = &field_ptr_instruction->base; - } else if (old_instruction->id == IrInstructionIdElemPtr) { - IrInstructionElemPtr *elem_ptr_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - new_instruction = &elem_ptr_instruction->base; - } else { - IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - new_instruction = &const_instruction->base; - } + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + IrInstruction *new_instruction = &const_instruction->base; new_instruction->value.special = ConstValSpecialStatic; return new_instruction; } -- cgit v1.2.3 From c87102c3046cfabffe0e680e4793f5ce2a88caa2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 13 Jul 2018 14:53:54 -0400 Subject: ir_get_ref: delete unnecessary and probably buggy code --- src/ir.cpp | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 1b197d3ed1..3007bbcf64 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9593,23 +9593,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi if (type_is_invalid(value->value.type)) 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; - } - - type_ensure_zero_bits_known(ira->codegen, value->value.type); - if (type_is_invalid(value->value.type)) { - return ira->codegen->invalid_instruction; - } - - if (!type_has_bits(value->value.type)) { - return load_ptr_inst->ptr; - } - } - if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) -- cgit v1.2.3 From 722b9b9e595027e76ab4255f13ad0eca539358ac Mon Sep 17 00:00:00 2001 From: Eduardo Sánchez Muñoz Date: Sat, 14 Jul 2018 01:12:23 +0200 Subject: codegen: Store returned value if type is 'handle_is_ptr' and function is not 'first_arg_ret'. Seems to fix #1230, includes test. --- src/codegen.cpp | 4 ++++ test/behavior.zig | 1 + test/cases/bugs/1230.zig | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 test/cases/bugs/1230.zig (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 3f54c120b4..0bcc211164 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3166,6 +3166,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr return nullptr; } else if (first_arg_ret) { return instruction->tmp_ptr; + } else if (handle_is_ptr(src_return_type)) { + auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); + LLVMSetAlignment(store_instr, LLVMGetAlignment(instruction->tmp_ptr)); + return instruction->tmp_ptr; } else { return result; } diff --git a/test/behavior.zig b/test/behavior.zig index 450dded56c..21b1c597e1 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -9,6 +9,7 @@ comptime { _ = @import("cases/bitcast.zig"); _ = @import("cases/bool.zig"); _ = @import("cases/bugs/1111.zig"); + _ = @import("cases/bugs/1230.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); diff --git a/test/cases/bugs/1230.zig b/test/cases/bugs/1230.zig new file mode 100644 index 0000000000..3fd22357d7 --- /dev/null +++ b/test/cases/bugs/1230.zig @@ -0,0 +1,11 @@ +const S = extern struct { + x: i32, +}; + +extern fn ret_struct() S { + return S { .x = 0 }; +} + +test "extern return small struct (bug 1230)" { + const s = ret_struct(); +} -- cgit v1.2.3 From e9a03cccf375f11aa4e0a8a3515e499c88d05cde Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 10:53:15 -0400 Subject: all integer sizes are available as primitives * fix wrong implicit cast for `@IntType` bit_count parameter. * fix incorrect docs for `@IntType` bit_count parameter. closes #1242 closes #745 closes #1240 --- doc/langref.html.in | 12 ++++---- src/all_types.hpp | 4 --- src/analyze.cpp | 73 +++++++++++++++++++++---------------------------- src/analyze.hpp | 3 +- src/codegen.cpp | 13 --------- src/ir.cpp | 21 ++++++++------ src/translate_c.cpp | 2 +- std/buffer.zig | 2 -- std/crypto/sha1.zig | 2 -- std/json.zig | 3 -- std/math/big/int.zig | 1 - std/math/exp2.zig | 24 ++++++++-------- std/math/index.zig | 2 +- std/os/time.zig | 1 - test/cases/misc.zig | 5 ---- test/cases/struct.zig | 1 - test/compile_errors.zig | 9 ++++++ 17 files changed, 74 insertions(+), 104 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index ea672ccb17..46b325832b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2310,11 +2310,11 @@ test "while loop continue expression" { } test "while loop continue expression, more complicated" { - var i1: usize = 1; - var j1: usize = 1; - while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) { - const my_ij1 = i1 * j1; - assert(my_ij1 < 2000); + var i: usize = 1; + var j: usize = 1; + while (i * j < 2000) : ({ i *= 2; j *= 3; }) { + const my_ij = i * j; + assert(my_ij < 2000); } } {#code_end#} @@ -5424,7 +5424,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } {#header_close#} {#header_open|@IntType#} -
    @IntType(comptime is_signed: bool, comptime bit_count: u8) type
    +
    @IntType(comptime is_signed: bool, comptime bit_count: u32) type

    This function returns an integer type with the given signness and bit count.

    diff --git a/src/all_types.hpp b/src/all_types.hpp index 2da0677e1b..bcd6a04cc3 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1587,7 +1587,6 @@ struct CodeGen { struct { TypeTableEntry *entry_bool; - TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128] TypeTableEntry *entry_c_int[CIntTypeCount]; TypeTableEntry *entry_c_longdouble; TypeTableEntry *entry_c_void; @@ -1596,12 +1595,9 @@ struct CodeGen { TypeTableEntry *entry_u32; TypeTableEntry *entry_u29; TypeTableEntry *entry_u64; - TypeTableEntry *entry_u128; TypeTableEntry *entry_i8; - TypeTableEntry *entry_i16; TypeTableEntry *entry_i32; TypeTableEntry *entry_i64; - TypeTableEntry *entry_i128; TypeTableEntry *entry_isize; TypeTableEntry *entry_usize; TypeTableEntry *entry_f16; diff --git a/src/analyze.cpp b/src/analyze.cpp index 5635cce411..2ace893508 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3227,9 +3227,8 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { } { - auto entry = g->primitive_type_table.maybe_get(tld->name); - if (entry) { - TypeTableEntry *type = entry->value; + TypeTableEntry *type = get_primitive_type(g, tld->name); + if (type != nullptr) { add_node_error(g, tld->source_node, buf_sprintf("declaration shadows type '%s'", buf_ptr(&type->name))); } @@ -3474,9 +3473,8 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->value->type = g->builtin_types.entry_invalid; } else { - auto primitive_table_entry = g->primitive_type_table.maybe_get(name); - if (primitive_table_entry) { - TypeTableEntry *type = primitive_table_entry->value; + TypeTableEntry *type = get_primitive_type(g, name); + if (type != nullptr) { add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->value->type = g->builtin_types.entry_invalid; @@ -4307,43 +4305,7 @@ void semantic_analyze(CodeGen *g) { } } -TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits) { - size_t index; - if (size_in_bits == 2) { - index = 0; - } else if (size_in_bits == 3) { - index = 1; - } else if (size_in_bits == 4) { - index = 2; - } else if (size_in_bits == 5) { - index = 3; - } else if (size_in_bits == 6) { - index = 4; - } else if (size_in_bits == 7) { - index = 5; - } else if (size_in_bits == 8) { - index = 6; - } else if (size_in_bits == 16) { - index = 7; - } else if (size_in_bits == 29) { - index = 8; - } else if (size_in_bits == 32) { - index = 9; - } else if (size_in_bits == 64) { - index = 10; - } else if (size_in_bits == 128) { - index = 11; - } else { - return nullptr; - } - return &g->builtin_types.entry_int[is_signed ? 0 : 1][index]; -} - TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { - TypeTableEntry **common_entry = get_int_type_ptr(g, is_signed, size_in_bits); - if (common_entry) - return *common_entry; - TypeId type_id = {}; type_id.id = TypeTableEntryIdInt; type_id.data.integer.is_signed = is_signed; @@ -4953,6 +4915,8 @@ bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; + if (type_is_invalid(var_scope->var->value->type)) + return false; if (can_mutate_comptime_var_state(var_scope->var->value)) return false; } else if (scope->id == ScopeIdFnDef) { @@ -6310,3 +6274,28 @@ bool type_can_fail(TypeTableEntry *type_entry) { bool fn_type_can_fail(FnTypeId *fn_type_id) { return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; } + +TypeTableEntry *get_primitive_type(CodeGen *g, Buf *name) { + if (buf_len(name) >= 2) { + uint8_t first_c = buf_ptr(name)[0]; + if (first_c == 'i' || first_c == 'u') { + for (size_t i = 1; i < buf_len(name); i += 1) { + uint8_t c = buf_ptr(name)[i]; + if (c < '0' || c > '9') { + goto not_integer; + } + } + bool is_signed = (first_c == 'i'); + uint32_t bit_count = atoi(buf_ptr(name) + 1); + return get_int_type(g, is_signed, bit_count); + } + } + +not_integer: + + auto primitive_table_entry = g->primitive_type_table.maybe_get(name); + if (primitive_table_entry != nullptr) { + return primitive_table_entry->value; + } + return nullptr; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index 5168509fe0..e4dfae4ecb 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -19,7 +19,6 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry); -TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits); TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); @@ -204,4 +203,6 @@ bool type_can_fail(TypeTableEntry *type_entry); bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type); AstNode *type_decl_node(TypeTableEntry *type_entry); +TypeTableEntry *get_primitive_type(CodeGen *g, Buf *name); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 0bcc211164..c38ae1036a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6161,16 +6161,6 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_arg_tuple = entry; } - for (size_t int_size_i = 0; int_size_i < array_length(int_sizes_in_bits); int_size_i += 1) { - uint8_t size_in_bits = int_sizes_in_bits[int_size_i]; - for (size_t is_sign_i = 0; is_sign_i < array_length(is_signed_list); is_sign_i += 1) { - bool is_signed = is_signed_list[is_sign_i]; - TypeTableEntry *entry = make_int_type(g, is_signed, size_in_bits); - g->primitive_type_table.put(&entry->name, entry); - get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry; - } - } - for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) { const CIntTypeInfo *info = &c_int_type_infos[i]; uint32_t size_in_bits = target_c_type_size_in_bits(&g->zig_target, info->id); @@ -6286,12 +6276,9 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_u29 = get_int_type(g, false, 29); g->builtin_types.entry_u32 = get_int_type(g, false, 32); g->builtin_types.entry_u64 = get_int_type(g, false, 64); - g->builtin_types.entry_u128 = get_int_type(g, false, 128); g->builtin_types.entry_i8 = get_int_type(g, true, 8); - g->builtin_types.entry_i16 = get_int_type(g, true, 16); g->builtin_types.entry_i32 = get_int_type(g, true, 32); g->builtin_types.entry_i64 = get_int_type(g, true, 64); - g->builtin_types.entry_i128 = get_int_type(g, true, 128); { g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void"); diff --git a/src/ir.cpp b/src/ir.cpp index 3007bbcf64..0804134d2a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3217,9 +3217,8 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->value->type = codegen->builtin_types.entry_invalid; } else { - auto primitive_table_entry = codegen->primitive_type_table.maybe_get(name); - if (primitive_table_entry) { - TypeTableEntry *type = primitive_table_entry->value; + TypeTableEntry *type = get_primitive_type(codegen, name); + if (type != nullptr) { add_node_error(codegen, node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); variable_entry->value->type = codegen->builtin_types.entry_invalid; @@ -3661,9 +3660,9 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, return &const_instruction->base; } - auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name); - if (primitive_table_entry) { - IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value); + TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name); + if (primitive_type != nullptr) { + IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { return ir_build_ref(irb, scope, node, value, false, false); } else { @@ -10691,11 +10690,11 @@ static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out return true; } -static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { +static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *int_type, uint64_t *out) { if (type_is_invalid(value->value.type)) return false; - IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_usize); + IrInstruction *casted_value = ir_implicit_cast(ira, value, int_type); if (type_is_invalid(casted_value->value.type)) return false; @@ -10707,6 +10706,10 @@ static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out return true; } +static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) { + return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out); +} + static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) { if (type_is_invalid(value->value.type)) return false; @@ -18025,7 +18028,7 @@ static TypeTableEntry *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruc IrInstruction *bit_count_value = instruction->bit_count->other; uint64_t bit_count; - if (!ir_resolve_usize(ira, bit_count_value, &bit_count)) + if (!ir_resolve_unsigned(ira, bit_count_value, ira->codegen->builtin_types.entry_u32, &bit_count)) return ira->codegen->builtin_types.entry_invalid; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index db46d31c5b..267a716a9d 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -427,7 +427,7 @@ static AstNode *get_global(Context *c, Buf *name) { if (entry) return entry->value; } - if (c->codegen->primitive_type_table.maybe_get(name) != nullptr) { + if (get_primitive_type(c->codegen, name) != nullptr) { return trans_create_node_symbol(c, name); } return nullptr; diff --git a/std/buffer.zig b/std/buffer.zig index 0d82918580..aff7fa86ef 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -5,8 +5,6 @@ const Allocator = mem.Allocator; const assert = debug.assert; const ArrayList = std.ArrayList; -const fmt = std.fmt; - /// A buffer that allocates memory and maintains a null byte at the end. pub const Buffer = struct { list: ArrayList(u8), diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 5c91590c88..451cfb3122 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -4,8 +4,6 @@ const endian = @import("../endian.zig"); const debug = @import("../debug/index.zig"); const builtin = @import("builtin"); -pub const u160 = @IntType(false, 160); - const RoundParam = struct { a: usize, b: usize, diff --git a/std/json.zig b/std/json.zig index 8986034fb4..e62d5a3466 100644 --- a/std/json.zig +++ b/std/json.zig @@ -6,9 +6,6 @@ const std = @import("index.zig"); const debug = std.debug; const mem = std.mem; -const u1 = @IntType(false, 1); -const u256 = @IntType(false, 256); - // A single token slice into the parent string. // // Use `token.slice()` on the input at the current position to get the current slice. diff --git a/std/math/big/int.zig b/std/math/big/int.zig index 29673538eb..caa9d0a7ed 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -996,7 +996,6 @@ pub const Int = struct { // They will still run on larger than this and should pass, but the multi-limb code-paths // may be untested in some cases. -const u256 = @IntType(false, 256); const al = debug.global_allocator; test "big.int comptime_int set" { diff --git a/std/math/exp2.zig b/std/math/exp2.zig index 90ea088181..d590b0b60b 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -75,18 +75,18 @@ fn exp2_32(x: f32) f32 { } var uf = x + redux; - var i0 = @bitCast(u32, uf); - i0 += tblsiz / 2; + var i_0 = @bitCast(u32, uf); + i_0 += tblsiz / 2; - const k = i0 / tblsiz; + const k = i_0 / tblsiz; // NOTE: musl relies on undefined overflow shift behaviour. Appears that this produces the // intended result but should confirm how GCC/Clang handle this to ensure. const uk = @bitCast(f64, u64(0x3FF + k) << 52); - i0 &= tblsiz - 1; + i_0 &= tblsiz - 1; uf -= redux; const z: f64 = x - uf; - var r: f64 = exp2ft[i0]; + var r: f64 = exp2ft[i_0]; const t: f64 = r * z; r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4); return @floatCast(f32, r * uk); @@ -401,18 +401,18 @@ fn exp2_64(x: f64) f64 { // reduce x var uf = x + redux; // NOTE: musl performs an implicit 64-bit to 32-bit u32 truncation here - var i0 = @truncate(u32, @bitCast(u64, uf)); - i0 += tblsiz / 2; + var i_0 = @truncate(u32, @bitCast(u64, uf)); + i_0 += tblsiz / 2; - const k: u32 = i0 / tblsiz * tblsiz; + const k: u32 = i_0 / tblsiz * tblsiz; const ik = @bitCast(i32, k / tblsiz); - i0 %= tblsiz; + i_0 %= tblsiz; uf -= redux; - // r = exp2(y) = exp2t[i0] * p(z - eps[i]) + // r = exp2(y) = exp2t[i_0] * p(z - eps[i]) var z = x - uf; - const t = exp2dt[2 * i0]; - z -= exp2dt[2 * i0 + 1]; + const t = exp2dt[2 * i_0]; + z -= exp2dt[2 * i_0 + 1]; const r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))); return math.scalbn(r, ik); diff --git a/std/math/index.zig b/std/math/index.zig index 17b66f5568..e5fd0f3685 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -354,7 +354,7 @@ test "math.rotl" { pub fn Log2Int(comptime T: type) type { // comptime ceil log2 - comptime var count: usize = 0; + comptime var count = 0; comptime var s = T.bit_count - 1; inline while (s != 0) : (s >>= 1) { count += 1; diff --git a/std/os/time.zig b/std/os/time.zig index 73ba5bba82..795605d7a9 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -25,7 +25,6 @@ pub fn sleep(seconds: usize, nanoseconds: usize) void { } } -const u63 = @IntType(false, 63); pub fn posixSleep(seconds: u63, nanoseconds: u63) void { var req = posix.timespec{ .tv_sec = seconds, diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 0f181a7b4e..1c0189571b 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -58,11 +58,6 @@ test "floating point primitive bit counts" { assert(f64.bit_count == 64); } -const u1 = @IntType(false, 1); -const u63 = @IntType(false, 63); -const i1 = @IntType(true, 1); -const i63 = @IntType(true, 63); - test "@minValue and @maxValue" { assert(@maxValue(u1) == 1); assert(@maxValue(u8) == 255); diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 2941ecb56a..20d46999d5 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -240,7 +240,6 @@ fn getC(data: *const BitField1) u2 { return data.c; } -const u24 = @IntType(false, 24); const Foo24Bits = packed struct { field: u24, }; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 58c73b8ae4..d5582b1584 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "optional pointer to void in extern struct", + \\comptime { + \\ _ = @IntType(false, @maxValue(u32) + 1); + \\} + , + ".tmp_source.zig:2:40: error: integer value 4294967296 cannot be implicitly casted to type 'u32'", + ); + cases.add( "optional pointer to void in extern struct", \\const Foo = extern struct { -- cgit v1.2.3 From d3ce9d0643421da77063472cb1124123cda753bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 11:59:37 -0400 Subject: codegen: remove unused variable --- src/codegen.cpp | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index c38ae1036a..f8801ea132 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6062,21 +6062,6 @@ static void do_code_gen(CodeGen *g) { } } -static const uint8_t int_sizes_in_bits[] = { - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 16, - 29, - 32, - 64, - 128, -}; - struct CIntTypeInfo { CIntType id; const char *name; -- cgit v1.2.3 From 0fa24b6b7568557c29c9b3ee213ce2b06fcd6367 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 19:26:15 -0400 Subject: allow implicit cast of undefined to optional --- src/ir.cpp | 2 +- test/cases/cast.zig | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 0804134d2a..35b6b4cef4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9408,7 +9408,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; - ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad); + ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk); if (!val) return ira->codegen->invalid_instruction; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 5688d90e11..63cc6313e1 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -468,3 +468,20 @@ test "@intCast i32 to u7" { var z = x >> @intCast(u7, y); assert(z == 0xff); } + +test "implicit cast undefined to optional" { + assert(MakeType(void).getNull() == null); + assert(MakeType(void).getNonNull() != null); +} + +fn MakeType(comptime T: type) type { + return struct { + fn getNull() ?T { + return null; + } + + fn getNonNull() ?T { + return T(undefined); + } + }; +} -- cgit v1.2.3 From 97bfeac13f89e1b5a22fcd7d4705341b4c3e1950 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 20:52:50 -0400 Subject: self-hosted: create tmp dir for .o files and emit .o file for fn --- CMakeLists.txt | 1 + src-self-hosted/codegen.zig | 86 ++++++++++- src-self-hosted/compilation.zig | 335 ++++++++++++++++++++++++++++++++-------- src-self-hosted/ir.zig | 10 +- src-self-hosted/llvm.zig | 75 ++++++++- src-self-hosted/main.zig | 8 +- src-self-hosted/package.zig | 29 ++++ src-self-hosted/scope.zig | 32 ++-- src-self-hosted/target.zig | 116 ++++++++++---- src-self-hosted/test.zig | 3 +- src-self-hosted/type.zig | 58 +++---- src-self-hosted/value.zig | 41 ++++- src/zig_llvm.cpp | 5 + src/zig_llvm.h | 3 +- std/atomic/int.zig | 4 + std/buffer.zig | 13 ++ std/dwarf.zig | 37 +++++ std/event/future.zig | 39 ++++- std/index.zig | 3 + std/lazy_init.zig | 85 ++++++++++ 20 files changed, 808 insertions(+), 175 deletions(-) create mode 100644 src-self-hosted/package.zig create mode 100644 std/lazy_init.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index e606855555..0e7c1df350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -479,6 +479,7 @@ set(ZIG_STD_FILES "index.zig" "io.zig" "json.zig" + "lazy_init.zig" "linked_list.zig" "macho.zig" "math/acos.zig" diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 698f1e5b45..28ba2a1564 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -1,19 +1,22 @@ const std = @import("std"); +const builtin = @import("builtin"); const Compilation = @import("compilation.zig").Compilation; -// we go through llvm instead of c for 2 reasons: -// 1. to avoid accidentally calling the non-thread-safe functions -// 2. patch up some of the types to remove nullability const llvm = @import("llvm.zig"); +const c = @import("c.zig"); const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const event = std.event; const assert = std.debug.assert; +const DW = std.dwarf; pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { fn_val.base.ref(); defer fn_val.base.deref(comp); - defer code.destroy(comp.a()); + defer code.destroy(comp.gpa()); + + var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable); + errdefer output_path.deinit(); const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); defer llvm_handle.release(comp.event_loop_local); @@ -23,13 +26,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; defer llvm.DisposeModule(module); + llvm.SetTarget(module, comp.llvm_triple.ptr()); + llvm.SetDataLayout(module, comp.target_layout_str); + + if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) { + llvm.AddModuleCodeViewFlag(module); + } else { + llvm.AddModuleDebugInfoFlag(module); + } + const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; defer llvm.DisposeBuilder(builder); + const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory; + defer llvm.DisposeDIBuilder(dibuilder); + + // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes + // the git revision. + const producer = try std.Buffer.allocPrint( + &code.arena.allocator, + "zig {}.{}.{}", + u32(c.ZIG_VERSION_MAJOR), + u32(c.ZIG_VERSION_MINOR), + u32(c.ZIG_VERSION_PATCH), + ); + const flags = c""; + const runtime_version = 0; + const compile_unit_file = llvm.CreateFile( + dibuilder, + comp.name.ptr(), + comp.root_package.root_src_dir.ptr(), + ) orelse return error.OutOfMemory; + const is_optimized = comp.build_mode != builtin.Mode.Debug; + const compile_unit = llvm.CreateCompileUnit( + dibuilder, + DW.LANG_C99, + compile_unit_file, + producer.ptr(), + is_optimized, + flags, + runtime_version, + c"", + 0, + !comp.strip, + ) orelse return error.OutOfMemory; + var ofile = ObjectFile{ .comp = comp, .module = module, .builder = builder, + .dibuilder = dibuilder, .context = context, .lock = event.Lock.init(comp.loop), }; @@ -41,8 +87,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); //} - // TODO - //ZigLLVMDIBuilderFinalize(g->dbuilder); + llvm.DIBuilderFinalize(dibuilder); if (comp.verbose_llvm_ir) { llvm.DumpModule(ofile.module); @@ -53,17 +98,42 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) var error_ptr: ?[*]u8 = null; _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); } + + assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types + + const is_small = comp.build_mode == builtin.Mode.ReleaseSmall; + const is_debug = comp.build_mode == builtin.Mode.Debug; + + var err_msg: [*]u8 = undefined; + // TODO integrate this with evented I/O + if (llvm.TargetMachineEmitToFile( + comp.target_machine, + module, + output_path.ptr(), + llvm.EmitBinary, + &err_msg, + is_debug, + is_small, + )) { + if (std.debug.runtime_safety) { + std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg); + } + return error.WritingObjectFileFailed; + } + //validate_inline_fns(g); TODO + fn_val.containing_object = output_path; } pub const ObjectFile = struct { comp: *Compilation, module: llvm.ModuleRef, builder: llvm.BuilderRef, + dibuilder: *llvm.DIBuilder, context: llvm.ContextRef, lock: event.Lock, - fn a(self: *ObjectFile) *std.mem.Allocator { - return self.comp.a(); + fn gpa(self: *ObjectFile) *std.mem.Allocator { + return self.comp.gpa(); } }; diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1dbbf21206..d5380a0644 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -26,16 +26,31 @@ const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; const codegen = @import("codegen.zig"); +const Package = @import("package.zig").Package; /// Data that is local to the event loop. pub const EventLoopLocal = struct { loop: *event.Loop, llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), - fn init(loop: *event.Loop) EventLoopLocal { + /// TODO pool these so that it doesn't have to lock + prng: event.Locked(std.rand.DefaultPrng), + + var lazy_init_targets = std.lazyInit(void); + + fn init(loop: *event.Loop) !EventLoopLocal { + lazy_init_targets.get() orelse { + Target.initializeAll(); + lazy_init_targets.resolve(); + }; + + var seed_bytes: [@sizeOf(u64)]u8 = undefined; + try std.os.getRandomBytes(seed_bytes[0..]); + const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); return EventLoopLocal{ .loop = loop, .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), + .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), }; } @@ -76,10 +91,16 @@ pub const Compilation = struct { event_loop_local: *EventLoopLocal, loop: *event.Loop, name: Buffer, + llvm_triple: Buffer, root_src_path: ?[]const u8, target: Target, + llvm_target: llvm.TargetRef, build_mode: builtin.Mode, zig_lib_dir: []const u8, + zig_std_dir: []const u8, + + /// lazily created when we need it + tmp_dir: event.Future(BuildError![]u8), version_major: u32, version_minor: u32, @@ -106,8 +127,16 @@ pub const Compilation = struct { lib_dirs: []const []const u8, rpath_list: []const []const u8, assembly_files: []const []const u8, + + /// paths that are explicitly provided by the user to link against link_objects: []const []const u8, + /// functions that have their own objects that we need to link + /// it uses an optional pointer so that tombstone removals are possible + fn_link_set: event.Locked(FnLinkSet), + + pub const FnLinkSet = std.LinkedList(?*Value.Fn); + windows_subsystem_windows: bool, windows_subsystem_console: bool, @@ -141,7 +170,7 @@ pub const Compilation = struct { /// Before code generation starts, must wait on this group to make sure /// the build is complete. - build_group: event.Group(BuildError!void), + prelink_group: event.Group(BuildError!void), compile_errors: event.Locked(CompileErrList), @@ -155,6 +184,16 @@ pub const Compilation = struct { false_value: *Value.Bool, noreturn_value: *Value.NoReturn, + target_machine: llvm.TargetMachineRef, + target_data_ref: llvm.TargetDataRef, + target_layout_str: [*]u8, + + /// for allocating things which have the same lifetime as this Compilation + arena_allocator: std.heap.ArenaAllocator, + + root_package: *Package, + std_package: *Package, + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -195,6 +234,9 @@ pub const Compilation = struct { BufferTooSmall, Unimplemented, // TODO remove this one SemanticAnalysisFailed, // TODO remove this one + ReadOnlyFileSystem, + LinkQuotaExceeded, + EnvironmentVariableNotFound, }; pub const Event = union(enum) { @@ -234,31 +276,31 @@ pub const Compilation = struct { event_loop_local: *EventLoopLocal, name: []const u8, root_src_path: ?[]const u8, - target: *const Target, + target: Target, kind: Kind, build_mode: builtin.Mode, + is_static: bool, zig_lib_dir: []const u8, cache_dir: []const u8, ) !*Compilation { const loop = event_loop_local.loop; - - var name_buffer = try Buffer.init(loop.allocator, name); - errdefer name_buffer.deinit(); - - const events = try event.Channel(Event).create(loop, 0); - errdefer events.destroy(); - - const comp = try loop.allocator.create(Compilation{ + const comp = try event_loop_local.loop.allocator.create(Compilation{ .loop = loop, + .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), .event_loop_local = event_loop_local, - .events = events, - .name = name_buffer, + .events = undefined, .root_src_path = root_src_path, - .target = target.*, + .target = target, + .llvm_target = undefined, .kind = kind, .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, + .zig_std_dir = undefined, .cache_dir = cache_dir, + .tmp_dir = event.Future(BuildError![]u8).init(loop), + + .name = undefined, + .llvm_triple = undefined, .version_major = 0, .version_minor = 0, @@ -283,7 +325,7 @@ pub const Compilation = struct { .is_test = false, .each_lib_rpath = false, .strip = false, - .is_static = false, + .is_static = is_static, .linker_rdynamic = false, .clang_argv = [][]const u8{}, .llvm_argv = [][]const u8{}, @@ -291,9 +333,10 @@ pub const Compilation = struct { .rpath_list = [][]const u8{}, .assembly_files = [][]const u8{}, .link_objects = [][]const u8{}, + .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()), .windows_subsystem_windows = false, .windows_subsystem_console = false, - .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), + .link_libs_list = undefined, .libc_link_lib = null, .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, @@ -303,7 +346,7 @@ pub const Compilation = struct { .emit_file_type = Emit.Binary, .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), - .build_group = event.Group(BuildError!void).init(loop), + .prelink_group = event.Group(BuildError!void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), .meta_type = undefined, @@ -314,13 +357,82 @@ pub const Compilation = struct { .false_value = undefined, .noreturn_type = undefined, .noreturn_value = undefined, + + .target_machine = undefined, + .target_data_ref = undefined, + .target_layout_str = undefined, + + .root_package = undefined, + .std_package = undefined, }); + errdefer { + comp.arena_allocator.deinit(); + comp.loop.allocator.destroy(comp); + } + + comp.name = try Buffer.init(comp.arena(), name); + comp.llvm_triple = try target.getTriple(comp.arena()); + comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); + comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); + comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std"); + + const opt_level = switch (build_mode) { + builtin.Mode.Debug => llvm.CodeGenLevelNone, + else => llvm.CodeGenLevelAggressive, + }; + + const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC; + + // LLVM creates invalid binaries on Windows sometimes. + // See https://github.com/ziglang/zig/issues/508 + // As a workaround we do not use target native features on Windows. + var target_specific_cpu_args: ?[*]u8 = null; + var target_specific_cpu_features: ?[*]u8 = null; + errdefer llvm.DisposeMessage(target_specific_cpu_args); + errdefer llvm.DisposeMessage(target_specific_cpu_features); + if (target == Target.Native and !target.isWindows()) { + target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; + target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; + } + + comp.target_machine = llvm.CreateTargetMachine( + comp.llvm_target, + comp.llvm_triple.ptr(), + target_specific_cpu_args orelse c"", + target_specific_cpu_features orelse c"", + opt_level, + reloc_mode, + llvm.CodeModelDefault, + ) orelse return error.OutOfMemory; + errdefer llvm.DisposeTargetMachine(comp.target_machine); + + comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory; + errdefer llvm.DisposeTargetData(comp.target_data_ref); + + comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory; + errdefer llvm.DisposeMessage(comp.target_layout_str); + + comp.events = try event.Channel(Event).create(comp.loop, 0); + errdefer comp.events.destroy(); + + if (root_src_path) |root_src| { + const dirname = std.os.path.dirname(root_src) orelse "."; + const basename = std.os.path.basename(root_src); + + comp.root_package = try Package.create(comp.arena(), dirname, basename); + comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig"); + try comp.root_package.add("std", comp.std_package); + } else { + comp.root_package = try Package.create(comp.arena(), ".", ""); + } + try comp.initTypes(); + return comp; } fn initTypes(comp: *Compilation) !void { - comp.meta_type = try comp.a().create(Type.MetaType{ + comp.meta_type = try comp.gpa().create(Type.MetaType{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -333,9 +445,9 @@ pub const Compilation = struct { }); comp.meta_type.value = &comp.meta_type.base; comp.meta_type.base.base.typeof = &comp.meta_type.base; - errdefer comp.a().destroy(comp.meta_type); + errdefer comp.gpa().destroy(comp.meta_type); - comp.void_type = try comp.a().create(Type.Void{ + comp.void_type = try comp.gpa().create(Type.Void{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -345,9 +457,9 @@ pub const Compilation = struct { .id = builtin.TypeId.Void, }, }); - errdefer comp.a().destroy(comp.void_type); + errdefer comp.gpa().destroy(comp.void_type); - comp.noreturn_type = try comp.a().create(Type.NoReturn{ + comp.noreturn_type = try comp.gpa().create(Type.NoReturn{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -357,9 +469,9 @@ pub const Compilation = struct { .id = builtin.TypeId.NoReturn, }, }); - errdefer comp.a().destroy(comp.noreturn_type); + errdefer comp.gpa().destroy(comp.noreturn_type); - comp.bool_type = try comp.a().create(Type.Bool{ + comp.bool_type = try comp.gpa().create(Type.Bool{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -369,18 +481,18 @@ pub const Compilation = struct { .id = builtin.TypeId.Bool, }, }); - errdefer comp.a().destroy(comp.bool_type); + errdefer comp.gpa().destroy(comp.bool_type); - comp.void_value = try comp.a().create(Value.Void{ + comp.void_value = try comp.gpa().create(Value.Void{ .base = Value{ .id = Value.Id.Void, .typeof = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.a().destroy(comp.void_value); + errdefer comp.gpa().destroy(comp.void_value); - comp.true_value = try comp.a().create(Value.Bool{ + comp.true_value = try comp.gpa().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -388,9 +500,9 @@ pub const Compilation = struct { }, .x = true, }); - errdefer comp.a().destroy(comp.true_value); + errdefer comp.gpa().destroy(comp.true_value); - comp.false_value = try comp.a().create(Value.Bool{ + comp.false_value = try comp.gpa().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -398,19 +510,23 @@ pub const Compilation = struct { }, .x = false, }); - errdefer comp.a().destroy(comp.false_value); + errdefer comp.gpa().destroy(comp.false_value); - comp.noreturn_value = try comp.a().create(Value.NoReturn{ + comp.noreturn_value = try comp.gpa().create(Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, .typeof = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.a().destroy(comp.noreturn_value); + errdefer comp.gpa().destroy(comp.noreturn_value); } pub fn destroy(self: *Compilation) void { + if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { + os.deleteTree(self.arena(), tmp_dir) catch {}; + } else |_| {}; + self.noreturn_value.base.deref(self); self.void_value.base.deref(self); self.false_value.base.deref(self); @@ -420,14 +536,18 @@ pub const Compilation = struct { self.meta_type.base.base.deref(self); self.events.destroy(); - self.name.deinit(); - self.a().destroy(self); + llvm.DisposeMessage(self.target_layout_str); + llvm.DisposeTargetData(self.target_data_ref); + llvm.DisposeTargetMachine(self.target_machine); + + self.arena_allocator.deinit(); + self.gpa().destroy(self); } pub fn build(self: *Compilation) !void { if (self.llvm_argv.len != 0) { - var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ + var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, }); @@ -436,7 +556,7 @@ pub const Compilation = struct { c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); } - _ = try async self.buildAsync(); + _ = try async self.buildAsync(); } async fn buildAsync(self: *Compilation) void { @@ -464,7 +584,7 @@ pub const Compilation = struct { } } else |err| { // if there's an error then the compile errors have dangling references - self.a().free(compile_errors); + self.gpa().free(compile_errors); await (async self.events.put(Event{ .Error = err }) catch unreachable); } @@ -477,26 +597,26 @@ pub const Compilation = struct { async fn addRootSrc(self: *Compilation) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { + const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; }; - errdefer self.a().free(root_src_real_path); + errdefer self.gpa().free(root_src_real_path); // TODO async/await readFileAlloc() - const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { + const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { try printError("unable to open '{}': {}", root_src_real_path, err); return err; }; - errdefer self.a().free(source_code); + errdefer self.gpa().free(source_code); - const parsed_file = try self.a().create(ParsedFile{ + const parsed_file = try self.gpa().create(ParsedFile{ .tree = undefined, .realpath = root_src_real_path, }); - errdefer self.a().destroy(parsed_file); + errdefer self.gpa().destroy(parsed_file); - parsed_file.tree = try std.zig.parse(self.a(), source_code); + parsed_file.tree = try std.zig.parse(self.gpa(), source_code); errdefer parsed_file.tree.deinit(); const tree = &parsed_file.tree; @@ -525,7 +645,7 @@ pub const Compilation = struct { continue; }; - const fn_decl = try self.a().create(Decl.Fn{ + const fn_decl = try self.gpa().create(Decl.Fn{ .base = Decl{ .id = Decl.Id.Fn, .name = name, @@ -538,7 +658,7 @@ pub const Compilation = struct { .value = Decl.Fn.Val{ .Unresolved = {} }, .fn_proto = fn_proto, }); - errdefer self.a().destroy(fn_decl); + errdefer self.gpa().destroy(fn_decl); try decl_group.call(addTopLevelDecl, self, &fn_decl.base); }, @@ -547,15 +667,15 @@ pub const Compilation = struct { } } try await (async decl_group.wait() catch unreachable); - try await (async self.build_group.wait() catch unreachable); + try await (async self.prelink_group.wait() catch unreachable); } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { const is_export = decl.isExported(&decl.parsed_file.tree); if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, decl); - try self.build_group.call(resolveDecl, self, decl); + try self.prelink_group.call(verifyUniqueSymbol, self, decl); + try self.prelink_group.call(resolveDecl, self, decl); } } @@ -563,7 +683,7 @@ pub const Compilation = struct { const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); errdefer self.loop.allocator.free(text); - try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); + try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text); } async fn addCompileErrorAsync( @@ -625,11 +745,11 @@ pub const Compilation = struct { } } - const link_lib = try self.a().create(LinkLib{ + const link_lib = try self.gpa().create(LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, - .symbols = ArrayList([]u8).init(self.a()), + .symbols = ArrayList([]u8).init(self.gpa()), }); try self.link_libs_list.append(link_lib); if (is_libc) { @@ -638,9 +758,71 @@ pub const Compilation = struct { return link_lib; } - fn a(self: Compilation) *mem.Allocator { + /// General Purpose Allocator. Must free when done. + fn gpa(self: Compilation) *mem.Allocator { return self.loop.allocator; } + + /// Arena Allocator. Automatically freed when the Compilation is destroyed. + fn arena(self: *Compilation) *mem.Allocator { + return &self.arena_allocator.allocator; + } + + /// If the temporary directory for this compilation has not been created, it creates it. + /// Then it creates a random file name in that dir and returns it. + pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { + const tmp_dir = try await (async self.getTmpDir() catch unreachable); + const file_prefix = await (async self.getRandomFileName() catch unreachable); + + const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); + defer self.gpa().free(file_name); + + const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]); + errdefer self.gpa().free(full_path); + + return Buffer.fromOwnedSlice(self.gpa(), full_path); + } + + /// If the temporary directory for this Compilation has not been created, creates it. + /// Then returns it. The directory is unique to this Compilation and cleaned up when + /// the Compilation deinitializes. + async fn getTmpDir(self: *Compilation) ![]const u8 { + if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*; + self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable); + self.tmp_dir.resolve(); + return self.tmp_dir.data; + } + + async fn getTmpDirImpl(self: *Compilation) ![]u8 { + const comp_dir_name = await (async self.getRandomFileName() catch unreachable); + const zig_dir_path = try getZigDir(self.gpa()); + defer self.gpa().free(zig_dir_path); + + const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]); + try os.makePath(self.gpa(), tmp_dir); + return tmp_dir; + } + + async fn getRandomFileName(self: *Compilation) [12]u8 { + // here we replace the standard +/ with -_ so that it can be used in a file name + const b64_fs_encoder = std.base64.Base64Encoder.init( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", + std.base64.standard_pad_char, + ); + + var rand_bytes: [9]u8 = undefined; + + { + const held = await (async self.event_loop_local.prng.acquire() catch unreachable); + defer held.release(); + + held.value.random.bytes(rand_bytes[0..]); + } + + var result: [12]u8 = undefined; + b64_fs_encoder.encode(result[0..], rand_bytes); + return result; + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -662,13 +844,11 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib /// This declaration has been blessed as going into the final code generation. pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { - if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { - decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); - decl.resolution.resolve(); - return decl.resolution.data; - } else { - return (await (async decl.resolution.get() catch unreachable)).*; - } + if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; + + decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.resolve(); + return decl.resolution.data; } /// The function that actually does the generation. @@ -698,7 +878,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args); defer fn_type.base.base.deref(comp); - var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name); + var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); errdefer symbol_name.deinit(); const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); @@ -719,7 +899,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { error.SemanticAnalysisFailed => return {}, else => return err, }; - defer unanalyzed_code.destroy(comp.a()); + defer unanalyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { std.debug.warn("unanalyzed:\n"); @@ -738,7 +918,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { error.SemanticAnalysisFailed => return {}, else => return err, }; - errdefer analyzed_code.destroy(comp.a()); + errdefer analyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { std.debug.warn("analyzed:\n"); @@ -747,5 +927,30 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. - try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); + try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); + try comp.prelink_group.call(addFnToLinkSet, comp, fn_val); +} + +async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { + fn_val.base.ref(); + defer fn_val.base.deref(comp); + + fn_val.link_set_node.data = fn_val; + + const held = await (async comp.fn_link_set.acquire() catch unreachable); + defer held.release(); + + held.value.append(fn_val.link_set_node); +} + +fn getZigDir(allocator: *mem.Allocator) ![]u8 { + const home_dir = try getHomeDir(allocator); + defer allocator.free(home_dir); + + return os.path.join(allocator, home_dir, ".zig"); +} + +/// TODO move to zig std lib, and make it work for other OSes +fn getHomeDir(allocator: *mem.Allocator) ![]u8 { + return os.getEnvVarOwned(allocator, "HOME"); } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 0e0a4f9bf3..c1f9c97001 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -453,7 +453,7 @@ pub const Code = struct { arena: std.heap.ArenaAllocator, return_type: ?*Type, - /// allocator is comp.a() + /// allocator is comp.gpa() pub fn destroy(self: *Code, allocator: *Allocator) void { self.arena.deinit(); allocator.destroy(self); @@ -483,13 +483,13 @@ pub const Builder = struct { pub const Error = Analyze.Error; pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder { - const code = try comp.a().create(Code{ + const code = try comp.gpa().create(Code{ .basic_block_list = undefined, - .arena = std.heap.ArenaAllocator.init(comp.a()), + .arena = std.heap.ArenaAllocator.init(comp.gpa()), .return_type = null, }); code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); - errdefer code.destroy(comp.a()); + errdefer code.destroy(comp.gpa()); return Builder{ .comp = comp, @@ -502,7 +502,7 @@ pub const Builder = struct { } pub fn abort(self: *Builder) void { - self.code.destroy(self.comp.a()); + self.code.destroy(self.comp.gpa()); } /// Call code.destroy() when done diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 13480dc2c6..b196656367 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -2,6 +2,12 @@ const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; +// we wrap the c module for 3 reasons: +// 1. to avoid accidentally calling the non-thread-safe functions +// 2. patch up some of the types to remove nullability +// 3. some functions have been augmented by zig_llvm.cpp to be more powerful, +// such as ZigLLVMTargetMachineEmitToFile + pub const AttributeIndex = c_uint; pub const Bool = c_int; @@ -12,25 +18,51 @@ pub const ValueRef = removeNullability(c.LLVMValueRef); pub const TypeRef = removeNullability(c.LLVMTypeRef); pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); pub const AttributeRef = removeNullability(c.LLVMAttributeRef); +pub const TargetRef = removeNullability(c.LLVMTargetRef); +pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef); +pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef); +pub const DIBuilder = c.ZigLLVMDIBuilder; pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddFunction = c.LLVMAddFunction; +pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; +pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; +pub const ConstAllOnes = c.LLVMConstAllOnes; pub const ConstInt = c.LLVMConstInt; +pub const ConstNull = c.LLVMConstNull; pub const ConstStringInContext = c.LLVMConstStringInContext; pub const ConstStructInContext = c.LLVMConstStructInContext; +pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData; pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; +pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit; +pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder; pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; +pub const CreateFile = c.ZigLLVMCreateFile; pub const CreateStringAttribute = c.LLVMCreateStringAttribute; +pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout; +pub const CreateTargetMachine = c.LLVMCreateTargetMachine; +pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize; pub const DisposeBuilder = c.LLVMDisposeBuilder; +pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder; +pub const DisposeMessage = c.LLVMDisposeMessage; pub const DisposeModule = c.LLVMDisposeModule; +pub const DisposeTargetData = c.LLVMDisposeTargetData; +pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine; pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; pub const DumpModule = c.LLVMDumpModule; pub const FP128TypeInContext = c.LLVMFP128TypeInContext; pub const FloatTypeInContext = c.LLVMFloatTypeInContext; pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; +pub const GetHostCPUName = c.ZigLLVMGetHostCPUName; pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; +pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures; pub const HalfTypeInContext = c.LLVMHalfTypeInContext; +pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; +pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters; +pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos; +pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs; +pub const InitializeAllTargets = c.LLVMInitializeAllTargets; pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; pub const Int128TypeInContext = c.LLVMInt128TypeInContext; pub const Int16TypeInContext = c.LLVMInt16TypeInContext; @@ -47,13 +79,16 @@ pub const MDStringInContext = c.LLVMMDStringInContext; pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; +pub const SetDataLayout = c.LLVMSetDataLayout; +pub const SetTarget = c.LLVMSetTarget; pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; -pub const ConstAllOnes = c.LLVMConstAllOnes; -pub const ConstNull = c.LLVMConstNull; + +pub const GetTargetFromTriple = LLVMGetTargetFromTriple; +extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool; pub const VerifyModule = LLVMVerifyModule; extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; @@ -83,6 +118,31 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; pub const VerifierFailureAction = c.LLVMVerifierFailureAction; +pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone; +pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess; +pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault; +pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive; +pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel; + +pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault; +pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic; +pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC; +pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic; +pub const RelocMode = c.LLVMRelocMode; + +pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault; +pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault; +pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall; +pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel; +pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium; +pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge; +pub const CodeModel = c.LLVMCodeModel; + +pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly; +pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; +pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; +pub const EmitOutputType = c.ZigLLVM_EmitOutputType; + fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; @@ -90,3 +150,14 @@ fn removeNullability(comptime T: type) type { pub const BuildRet = LLVMBuildRet; extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; + +pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; +extern fn ZigLLVMTargetMachineEmitToFile( + targ_machine_ref: TargetMachineRef, + module_ref: ModuleRef, + filename: [*]const u8, + output_type: EmitOutputType, + error_message: *[*]u8, + is_debug: bool, + is_small: bool, +) bool; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index c9478954c5..8b668e35bd 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -363,6 +363,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } }; + const is_static = flags.present("static"); + const assembly_files = flags.many("assembly"); const link_objects = flags.many("object"); if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { @@ -389,7 +391,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co try loop.initMultiThreaded(allocator); defer loop.deinit(); - var event_loop_local = EventLoopLocal.init(&loop); + var event_loop_local = try EventLoopLocal.init(&loop); defer event_loop_local.deinit(); var comp = try Compilation.create( @@ -399,6 +401,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co Target.Native, out_type, build_mode, + is_static, zig_lib_dir, full_cache_dir, ); @@ -426,7 +429,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.clang_argv = clang_argv_buf.toSliceConst(); comp.strip = flags.present("strip"); - comp.is_static = flags.present("static"); if (flags.single("libc-lib-dir")) |libc_lib_dir| { comp.libc_lib_dir = libc_lib_dir; @@ -481,9 +483,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } comp.emit_file_type = emit_type; - comp.link_objects = link_objects; comp.assembly_files = assembly_files; comp.link_out_file = flags.single("out-file"); + comp.link_objects = link_objects; try comp.build(); const process_build_events_handle = try async processBuildEvents(comp, color); diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig new file mode 100644 index 0000000000..720b279651 --- /dev/null +++ b/src-self-hosted/package.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const mem = std.mem; +const assert = std.debug.assert; +const Buffer = std.Buffer; + +pub const Package = struct { + root_src_dir: Buffer, + root_src_path: Buffer, + + /// relative to root_src_dir + table: Table, + + pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8); + + /// makes internal copies of root_src_dir and root_src_path + /// allocator should be an arena allocator because Package never frees anything + pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { + return allocator.create(Package{ + .root_src_dir = try Buffer.init(allocator, root_src_dir), + .root_src_path = try Buffer.init(allocator, root_src_path), + .table = Table.init(allocator), + }); + } + + pub fn add(self: *Package, name: []const u8, package: *Package) !void { + const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package); + assert(entry == null); + } +}; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 4326617fa0..1c519d6c08 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -64,7 +64,7 @@ pub const Scope = struct { /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { - const self = try comp.a().create(Decls{ + const self = try comp.gpa().create(Decls{ .base = Scope{ .id = Id.Decls, .parent = parent, @@ -72,9 +72,9 @@ pub const Scope = struct { }, .table = undefined, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); - self.table = Decl.Table.init(comp.a()); + self.table = Decl.Table.init(comp.gpa()); errdefer self.table.deinit(); if (parent) |p| p.ref(); @@ -126,7 +126,7 @@ pub const Scope = struct { /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { - const self = try comp.a().create(Block{ + const self = try comp.gpa().create(Block{ .base = Scope{ .id = Id.Block, .parent = parent, @@ -138,14 +138,14 @@ pub const Scope = struct { .is_comptime = undefined, .safety = Safety.Auto, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); if (parent) |p| p.ref(); return self; } pub fn destroy(self: *Block, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -158,7 +158,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { - const self = try comp.a().create(FnDef{ + const self = try comp.gpa().create(FnDef{ .base = Scope{ .id = Id.FnDef, .parent = parent, @@ -173,7 +173,7 @@ pub const Scope = struct { } pub fn destroy(self: *FnDef, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -182,7 +182,7 @@ pub const Scope = struct { /// Creates a CompTime scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { - const self = try comp.a().create(CompTime{ + const self = try comp.gpa().create(CompTime{ .base = Scope{ .id = Id.CompTime, .parent = parent, @@ -195,7 +195,7 @@ pub const Scope = struct { } pub fn destroy(self: *CompTime, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -216,7 +216,7 @@ pub const Scope = struct { kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try comp.a().create(Defer{ + const self = try comp.gpa().create(Defer{ .base = Scope{ .id = Id.Defer, .parent = parent, @@ -225,7 +225,7 @@ pub const Scope = struct { .defer_expr_scope = defer_expr_scope, .kind = kind, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); defer_expr_scope.base.ref(); @@ -235,7 +235,7 @@ pub const Scope = struct { pub fn destroy(self: *Defer, comp: *Compilation) void { self.defer_expr_scope.base.deref(comp); - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -245,7 +245,7 @@ pub const Scope = struct { /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try comp.a().create(DeferExpr{ + const self = try comp.gpa().create(DeferExpr{ .base = Scope{ .id = Id.DeferExpr, .parent = parent, @@ -253,14 +253,14 @@ pub const Scope = struct { }, .expr_node = expr_node, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); if (parent) |p| p.ref(); return self; } pub fn destroy(self: *DeferExpr, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; }; diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 724d99ea23..db673e421a 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -1,60 +1,118 @@ +const std = @import("std"); const builtin = @import("builtin"); -const c = @import("c.zig"); - -pub const CrossTarget = struct { - arch: builtin.Arch, - os: builtin.Os, - environ: builtin.Environ, -}; +const llvm = @import("llvm.zig"); pub const Target = union(enum) { Native, - Cross: CrossTarget, + Cross: Cross, - pub fn oFileExt(self: *const Target) []const u8 { - const environ = switch (self.*) { - Target.Native => builtin.environ, - Target.Cross => |t| t.environ, - }; - return switch (environ) { - builtin.Environ.msvc => ".obj", + pub const Cross = struct { + arch: builtin.Arch, + os: builtin.Os, + environ: builtin.Environ, + object_format: builtin.ObjectFormat, + }; + + pub fn oFileExt(self: Target) []const u8 { + return switch (self.getObjectFormat()) { + builtin.ObjectFormat.coff => ".obj", else => ".o", }; } - pub fn exeFileExt(self: *const Target) []const u8 { + pub fn exeFileExt(self: Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn getOs(self: *const Target) builtin.Os { - return switch (self.*) { + pub fn getOs(self: Target) builtin.Os { + return switch (self) { Target.Native => builtin.os, - Target.Cross => |t| t.os, + @TagType(Target).Cross => |t| t.os, + }; + } + + pub fn getArch(self: Target) builtin.Arch { + return switch (self) { + Target.Native => builtin.arch, + @TagType(Target).Cross => |t| t.arch, + }; + } + + pub fn getEnviron(self: Target) builtin.Environ { + return switch (self) { + Target.Native => builtin.environ, + @TagType(Target).Cross => |t| t.environ, + }; + } + + pub fn getObjectFormat(self: Target) builtin.ObjectFormat { + return switch (self) { + Target.Native => builtin.object_format, + @TagType(Target).Cross => |t| t.object_format, }; } - pub fn isDarwin(self: *const Target) bool { + pub fn isWasm(self: Target) bool { + return switch (self.getArch()) { + builtin.Arch.wasm32, builtin.Arch.wasm64 => true, + else => false, + }; + } + + pub fn isDarwin(self: Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: *const Target) bool { + pub fn isWindows(self: Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, }; } -}; -pub fn initializeAll() void { - c.LLVMInitializeAllTargets(); - c.LLVMInitializeAllTargetInfos(); - c.LLVMInitializeAllTargetMCs(); - c.LLVMInitializeAllAsmPrinters(); - c.LLVMInitializeAllAsmParsers(); -} + pub fn initializeAll() void { + llvm.InitializeAllTargets(); + llvm.InitializeAllTargetInfos(); + llvm.InitializeAllTargetMCs(); + llvm.InitializeAllAsmPrinters(); + llvm.InitializeAllAsmParsers(); + } + + pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer { + var result = try std.Buffer.initSize(allocator, 0); + errdefer result.deinit(); + + // LLVM WebAssembly output support requires the target to be activated at + // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. + // + // LLVM determines the output format based on the environment suffix, + // defaulting to an object based on the architecture. The default format in + // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to + // explicitly set this ourself in order for it to work. + // + // This is fixed in LLVM 7 and you will be able to get wasm output by + // using the target triple `wasm32-unknown-unknown-unknown`. + const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron()); + + var out = &std.io.BufferOutStream.init(&result).stream; + try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name); + + return result; + } + + pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef { + var result: llvm.TargetRef = undefined; + var err_msg: [*]u8 = undefined; + if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) { + std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg); + return error.UnsupportedTarget; + } + return result; + } +}; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 3edb267ca9..45e5362124 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -46,7 +46,7 @@ pub const TestContext = struct { try self.loop.initMultiThreaded(allocator); errdefer self.loop.deinit(); - self.event_loop_local = EventLoopLocal.init(&self.loop); + self.event_loop_local = try EventLoopLocal.init(&self.loop); errdefer self.event_loop_local.deinit(); self.group = std.event.Group(error!void).init(&self.loop); @@ -107,6 +107,7 @@ pub const TestContext = struct { Target.Native, Compilation.Kind.Obj, builtin.Mode.Debug, + true, // is_static self.zig_lib_dir, self.zig_cache_dir, ); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 670547cce2..bb1fb9bb01 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -160,7 +160,7 @@ pub const Type = struct { decls: *Scope.Decls, pub fn destroy(self: *Struct, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { @@ -180,7 +180,7 @@ pub const Type = struct { }; pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { - const result = try comp.a().create(Fn{ + const result = try comp.gpa().create(Fn{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -193,7 +193,7 @@ pub const Type = struct { .params = params, .is_var_args = is_var_args, }); - errdefer comp.a().destroy(result); + errdefer comp.gpa().destroy(result); result.return_type.base.ref(); for (result.params) |param| { @@ -207,7 +207,7 @@ pub const Type = struct { for (self.params) |param| { param.typeof.base.deref(comp); } - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { @@ -215,8 +215,8 @@ pub const Type = struct { Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, else => try self.return_type.getLlvmType(ofile), }; - const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len); - defer ofile.a().free(llvm_param_types); + const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len); + defer ofile.gpa().free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); } @@ -241,7 +241,7 @@ pub const Type = struct { } pub fn destroy(self: *MetaType, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -255,7 +255,7 @@ pub const Type = struct { } pub fn destroy(self: *Void, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -269,7 +269,7 @@ pub const Type = struct { } pub fn destroy(self: *Bool, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { @@ -287,7 +287,7 @@ pub const Type = struct { } pub fn destroy(self: *NoReturn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -295,7 +295,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Int, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { @@ -307,7 +307,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Float, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { @@ -332,7 +332,7 @@ pub const Type = struct { pub const Size = builtin.TypeInfo.Pointer.Size; pub fn destroy(self: *Pointer, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn get( @@ -355,7 +355,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Array, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { @@ -367,7 +367,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -375,7 +375,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -383,7 +383,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Undefined, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -391,7 +391,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Null, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -399,7 +399,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Optional, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { @@ -411,7 +411,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { @@ -423,7 +423,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ErrorSet, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { @@ -435,7 +435,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Enum, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { @@ -447,7 +447,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Union, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { @@ -459,7 +459,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Namespace, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -467,7 +467,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Block, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -475,7 +475,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *BoundFn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { @@ -487,7 +487,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ArgTuple, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -495,7 +495,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Opaque, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { @@ -507,7 +507,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Promise, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e3b91d2807..be19c6bccf 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -4,6 +4,7 @@ const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; const ObjectFile = @import("codegen.zig").ObjectFile; const llvm = @import("llvm.zig"); +const Buffer = std.Buffer; /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -68,7 +69,7 @@ pub const Value = struct { /// The main external name that is used in the .o file. /// TODO https://github.com/ziglang/zig/issues/265 - symbol_name: std.Buffer, + symbol_name: Buffer, /// parent should be the top level decls or container decls fndef_scope: *Scope.FnDef, @@ -79,10 +80,22 @@ pub const Value = struct { /// parent is child_scope block_scope: *Scope.Block, + /// Path to the object file that contains this function + containing_object: Buffer, + + link_set_node: *std.LinkedList(?*Value.Fn).Node, + /// Creates a Fn value with 1 ref /// Takes ownership of symbol_name - pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { - const self = try comp.a().create(Fn{ + pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn { + const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{ + .data = null, + .next = undefined, + .prev = undefined, + }); + errdefer comp.gpa().destroy(link_set_node); + + const self = try comp.gpa().create(Fn{ .base = Value{ .id = Value.Id.Fn, .typeof = &fn_type.base, @@ -92,6 +105,8 @@ pub const Value = struct { .child_scope = &fndef_scope.base, .block_scope = undefined, .symbol_name = symbol_name, + .containing_object = Buffer.initNull(comp.gpa()), + .link_set_node = link_set_node, }); fn_type.base.base.ref(); fndef_scope.fn_val = self; @@ -100,9 +115,19 @@ pub const Value = struct { } pub fn destroy(self: *Fn, comp: *Compilation) void { + // remove with a tombstone so that we do not have to grab a lock + if (self.link_set_node.data != null) { + // it's now the job of the link step to find this tombstone and + // deallocate it. + self.link_set_node.data = null; + } else { + comp.gpa().destroy(self.link_set_node); + } + + self.containing_object.deinit(); self.fndef_scope.base.deref(comp); self.symbol_name.deinit(); - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -115,7 +140,7 @@ pub const Value = struct { } pub fn destroy(self: *Void, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -134,7 +159,7 @@ pub const Value = struct { } pub fn destroy(self: *Bool, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { @@ -156,7 +181,7 @@ pub const Value = struct { } pub fn destroy(self: *NoReturn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -170,7 +195,7 @@ pub const Value = struct { }; pub fn destroy(self: *Ptr, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; }; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 24f2a8a343..a43d2d182c 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -440,6 +440,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso return reinterpret_cast(di_builder); } +void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) { + DIBuilder *di_builder = reinterpret_cast(dbuilder); + delete di_builder; +} + void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) { unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get( line, column, reinterpret_cast(scope))); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index d34300b8ae..6f25df8674 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -39,7 +39,7 @@ struct ZigLLVMInsertionPoint; ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); -/// Caller must free memory. +/// Caller must free memory with LLVMDisposeMessage ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void); ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); @@ -139,6 +139,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void); ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void); ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); +ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder); ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module); diff --git a/std/atomic/int.zig b/std/atomic/int.zig index d51454c673..4103d52719 100644 --- a/std/atomic/int.zig +++ b/std/atomic/int.zig @@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type { pub fn get(self: *Self) T { return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); } + + pub fn xchg(self: *Self, new_value: T) T { + return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst); + } }; } diff --git a/std/buffer.zig b/std/buffer.zig index aff7fa86ef..3b58002aba 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -54,6 +54,19 @@ pub const Buffer = struct { return result; } + pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer { + const countSize = struct { + fn countSize(size: *usize, bytes: []const u8) (error{}!void) { + size.* += bytes.len; + } + }.countSize; + var size: usize = 0; + std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {}; + var self = try Buffer.initSize(allocator, size); + assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); + return self; + } + pub fn deinit(self: *Buffer) void { self.list.deinit(); } diff --git a/std/dwarf.zig b/std/dwarf.zig index 76ed122447..2cf8ed953e 100644 --- a/std/dwarf.zig +++ b/std/dwarf.zig @@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03; pub const LNE_set_discriminator = 0x04; pub const LNE_lo_user = 0x80; pub const LNE_hi_user = 0xff; + +pub const LANG_C89 = 0x0001; +pub const LANG_C = 0x0002; +pub const LANG_Ada83 = 0x0003; +pub const LANG_C_plus_plus = 0x0004; +pub const LANG_Cobol74 = 0x0005; +pub const LANG_Cobol85 = 0x0006; +pub const LANG_Fortran77 = 0x0007; +pub const LANG_Fortran90 = 0x0008; +pub const LANG_Pascal83 = 0x0009; +pub const LANG_Modula2 = 0x000a; +pub const LANG_Java = 0x000b; +pub const LANG_C99 = 0x000c; +pub const LANG_Ada95 = 0x000d; +pub const LANG_Fortran95 = 0x000e; +pub const LANG_PLI = 0x000f; +pub const LANG_ObjC = 0x0010; +pub const LANG_ObjC_plus_plus = 0x0011; +pub const LANG_UPC = 0x0012; +pub const LANG_D = 0x0013; +pub const LANG_Python = 0x0014; +pub const LANG_Go = 0x0016; +pub const LANG_C_plus_plus_11 = 0x001a; +pub const LANG_Rust = 0x001c; +pub const LANG_C11 = 0x001d; +pub const LANG_C_plus_plus_14 = 0x0021; +pub const LANG_Fortran03 = 0x0022; +pub const LANG_Fortran08 = 0x0023; +pub const LANG_lo_user = 0x8000; +pub const LANG_hi_user = 0xffff; +pub const LANG_Mips_Assembler = 0x8001; +pub const LANG_Upc = 0x8765; +pub const LANG_HP_Bliss = 0x8003; +pub const LANG_HP_Basic91 = 0x8004; +pub const LANG_HP_Pascal91 = 0x8005; +pub const LANG_HP_IMacro = 0x8006; +pub const LANG_HP_Assembler = 0x8007; diff --git a/std/event/future.zig b/std/event/future.zig index 0f27b4131b..f5d14d1ca6 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder; const Lock = std.event.Lock; const Loop = std.event.Loop; -/// This is a value that starts out unavailable, until a value is put(). +/// This is a value that starts out unavailable, until resolve() is called /// While it is unavailable, coroutines suspend when they try to get() it, -/// and then are resumed when the value is put(). -/// At this point the value remains forever available, and another put() is not allowed. +/// and then are resumed when resolve() is called. +/// At this point the value remains forever available, and another resolve() is not allowed. pub fn Future(comptime T: type) type { return struct { lock: Lock, data: T, - available: u8, // TODO make this a bool + + /// TODO make this an enum + /// 0 - not started + /// 1 - started + /// 2 - finished + available: u8, const Self = this; const Queue = std.atomic.Queue(promise); @@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type { /// available. /// Thread-safe. pub async fn get(self: *Self) *T { - if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { return &self.data; } const held = await (async self.lock.acquire() catch unreachable); @@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type { /// Gets the data without waiting for it. If it's available, a pointer is /// returned. Otherwise, null is returned. pub fn getOrNull(self: *Self) ?*T { - if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { return &self.data; } else { return null; } } + /// If someone else has started working on the data, wait for them to complete + /// and return a pointer to the data. Otherwise, return null, and the caller + /// should start working on the data. + /// It's not required to call start() before resolve() but it can be useful since + /// this method is thread-safe. + pub async fn start(self: *Self) ?*T { + const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + switch (state) { + 1 => { + const held = await (async self.lock.acquire() catch unreachable); + held.release(); + return &self.data; + }, + 2 => return &self.data, + else => unreachable, + } + } + /// Make the data become available. May be called only once. /// Before calling this, modify the `data` property. pub fn resolve(self: *Self) void { - const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - assert(prev == 0); // put() called twice + const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); + assert(prev == 0 or prev == 1); // resolve() called twice Lock.Held.release(Lock.Held{ .lock = &self.lock }); } }; diff --git a/std/index.zig b/std/index.zig index 3b523f519f..2f4cfb7553 100644 --- a/std/index.zig +++ b/std/index.zig @@ -36,6 +36,8 @@ pub const sort = @import("sort.zig"); pub const unicode = @import("unicode.zig"); pub const zig = @import("zig/index.zig"); +pub const lazyInit = @import("lazy_init.zig").lazyInit; + test "std" { // run tests from these _ = @import("atomic/index.zig"); @@ -71,4 +73,5 @@ test "std" { _ = @import("sort.zig"); _ = @import("unicode.zig"); _ = @import("zig/index.zig"); + _ = @import("lazy_init.zig"); } diff --git a/std/lazy_init.zig b/std/lazy_init.zig new file mode 100644 index 0000000000..c46c067810 --- /dev/null +++ b/std/lazy_init.zig @@ -0,0 +1,85 @@ +const std = @import("index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; + +/// Thread-safe initialization of global data. +/// TODO use a mutex instead of a spinlock +pub fn lazyInit(comptime T: type) LazyInit(T) { + return LazyInit(T){ + .data = undefined, + .state = 0, + }; +} + +fn LazyInit(comptime T: type) type { + return struct { + state: u8, // TODO make this an enum + data: Data, + + const Self = this; + + // TODO this isn't working for void, investigate and then remove this special case + const Data = if (@sizeOf(T) == 0) u8 else T; + const Ptr = if (T == void) void else *T; + + /// Returns a usable pointer to the initialized data, + /// or returns null, indicating that the caller should + /// perform the initialization and then call resolve(). + pub fn get(self: *Self) ?Ptr { + while (true) { + var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + switch (state) { + 0 => continue, + 1 => { + // TODO mutex instead of a spinlock + continue; + }, + 2 => { + if (@sizeOf(T) == 0) { + return T(undefined); + } else { + return &self.data; + } + }, + else => unreachable, + } + } + } + + pub fn resolve(self: *Self) void { + const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); + assert(prev == 1); // resolve() called twice + } + }; +} + +var global_number = lazyInit(i32); + +test "std.lazyInit" { + if (global_number.get()) |_| @panic("bad") else { + global_number.data = 1234; + global_number.resolve(); + } + if (global_number.get()) |x| { + assert(x.* == 1234); + } else { + @panic("bad"); + } + if (global_number.get()) |x| { + assert(x.* == 1234); + } else { + @panic("bad"); + } +} + +var global_void = lazyInit(void); + +test "std.lazyInit(void)" { + if (global_void.get()) |_| @panic("bad") else { + global_void.resolve(); + } + assert(global_void.get() != null); + assert(global_void.get() != null); +} -- cgit v1.2.3 From 69e3b4e7dca816bbdd75207b8f44a5d7e0556a37 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Tue, 17 Jul 2018 23:27:18 +0900 Subject: revert commit 860d3da9156a0b1f4a1e3e644b423da3e768bb86 ; please see #1249 for more information; (#1255) --- src/ir.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 35b6b4cef4..a12bd054a7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -246,6 +246,8 @@ static void ir_ref_bb(IrBasicBlock *bb) { static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) { assert(instruction->id != IrInstructionIdInvalid); instruction->ref_count += 1; + if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction)) + ir_ref_bb(instruction->owner_bb); } static void ir_ref_var(VariableTableEntry *var) { -- cgit v1.2.3 From ecf8da00c53b20085cc32e84030caf32e8b3e16b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 13:18:13 -0400 Subject: self-hosted: linking --- src-self-hosted/codegen.zig | 8 + src-self-hosted/compilation.zig | 39 +++-- src-self-hosted/link.zig | 314 ++++++++++++++++++++++++++++++++++++++++ src/zig_llvm.h | 3 + 4 files changed, 348 insertions(+), 16 deletions(-) create mode 100644 src-self-hosted/link.zig (limited to 'src') diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 28ba2a1564..f8233bc795 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -90,6 +90,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) llvm.DIBuilderFinalize(dibuilder); if (comp.verbose_llvm_ir) { + std.debug.warn("raw module:\n"); llvm.DumpModule(ofile.module); } @@ -122,6 +123,13 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) } //validate_inline_fns(g); TODO fn_val.containing_object = output_path; + if (comp.verbose_llvm_ir) { + std.debug.warn("optimized module:\n"); + llvm.DumpModule(ofile.module); + } + if (comp.verbose_link) { + std.debug.warn("created {}\n", output_path.toSliceConst()); + } } pub const ObjectFile = struct { diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 4e7526a199..5cb7565a98 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -27,6 +27,7 @@ const Type = Value.Type; const Span = errmsg.Span; const codegen = @import("codegen.zig"); const Package = @import("package.zig").Package; +const link = @import("link.zig").link; /// Data that is local to the event loop. pub const EventLoopLocal = struct { @@ -238,6 +239,7 @@ pub const Compilation = struct { LinkQuotaExceeded, EnvironmentVariableNotFound, AppDataDirUnavailable, + LinkFailed, }; pub const Event = union(enum) { @@ -563,8 +565,7 @@ pub const Compilation = struct { async fn buildAsync(self: *Compilation) void { while (true) { // TODO directly awaiting async should guarantee memory allocation elision - // TODO also async before suspending should guarantee memory allocation elision - const build_result = await (async self.addRootSrc() catch unreachable); + const build_result = await (async self.compileAndLink() catch unreachable); // this makes a handy error return trace and stack trace in debug mode if (std.debug.runtime_safety) { @@ -595,7 +596,7 @@ pub const Compilation = struct { } } - async fn addRootSrc(self: *Compilation) !void { + async fn compileAndLink(self: *Compilation) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); // TODO async/await os.path.real const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { @@ -669,6 +670,17 @@ pub const Compilation = struct { } try await (async decl_group.wait() catch unreachable); try await (async self.prelink_group.wait() catch unreachable); + + const any_prelink_errors = blk: { + const compile_errors = await (async self.compile_errors.acquire() catch unreachable); + defer compile_errors.release(); + + break :blk compile_errors.value.len != 0; + }; + + if (!any_prelink_errors) { + try link(self); + } } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { @@ -722,11 +734,6 @@ pub const Compilation = struct { } } - pub fn link(self: *Compilation, out_file: ?[]const u8) !void { - warn("TODO link"); - return error.Todo; - } - pub fn haveLibC(self: *Compilation) bool { return self.libc_link_lib != null; } @@ -882,9 +889,8 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); errdefer symbol_name.deinit(); + // The Decl.Fn owns the initial 1 reference count const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); - defer fn_val.base.deref(comp); - fn_decl.value = Decl.Fn.Val{ .Ok = fn_val }; const unanalyzed_code = (await (async ir.gen( @@ -948,22 +954,23 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 { return getAppDataDir(allocator, "zig"); } - const GetAppDataDirError = error{ OutOfMemory, AppDataDirUnavailable, }; - /// Caller owns returned memory. /// TODO move to zig std lib fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 { switch (builtin.os) { builtin.Os.windows => { var dir_path_ptr: [*]u16 = undefined; - switch (os.windows.SHGetKnownFolderPath(&os.windows.FOLDERID_LocalAppData, os.windows.KF_FLAG_CREATE, - null, &dir_path_ptr,)) - { + switch (os.windows.SHGetKnownFolderPath( + &os.windows.FOLDERID_LocalAppData, + os.windows.KF_FLAG_CREATE, + null, + &dir_path_ptr, + )) { os.windows.S_OK => { defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); const global_dir = try utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)); @@ -974,7 +981,7 @@ fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirEr else => return error.AppDataDirUnavailable, } }, - // TODO for macos it should be "~/Library/Application Support/" + // TODO for macos it should be "~/Library/Application Support/" else => { const home_dir = os.getEnvVarOwned(allocator, "HOME") catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig new file mode 100644 index 0000000000..915451c931 --- /dev/null +++ b/src-self-hosted/link.zig @@ -0,0 +1,314 @@ +const std = @import("std"); +const c = @import("c.zig"); +const builtin = @import("builtin"); +const ObjectFormat = builtin.ObjectFormat; +const Compilation = @import("compilation.zig").Compilation; + +const Context = struct { + comp: *Compilation, + arena: std.heap.ArenaAllocator, + args: std.ArrayList([*]const u8), + link_in_crt: bool, + + link_err: error{OutOfMemory}!void, + link_msg: std.Buffer, +}; + +pub fn link(comp: *Compilation) !void { + var ctx = Context{ + .comp = comp, + .arena = std.heap.ArenaAllocator.init(comp.gpa()), + .args = undefined, + .link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe, + .link_err = {}, + .link_msg = undefined, + }; + defer ctx.arena.deinit(); + ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator); + ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator); + + // even though we're calling LLD as a library it thinks the first + // argument is its own exe name + try ctx.args.append(c"lld"); + + try constructLinkerArgs(&ctx); + + if (comp.verbose_link) { + for (ctx.args.toSliceConst()) |arg, i| { + const space = if (i == 0) "" else " "; + std.debug.warn("{}{s}", space, arg); + } + std.debug.warn("\n"); + } + + const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); + const args_slice = ctx.args.toSlice(); + if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) { + if (!ctx.link_msg.isNull()) { + // TODO capture these messages and pass them through the system, reporting them through the + // event system instead of printing them directly here. + // perhaps try to parse and understand them. + std.debug.warn("{}\n", ctx.link_msg.toSliceConst()); + } + return error.LinkFailed; + } +} + +extern fn ZigLLDLink( + oformat: c.ZigLLVM_ObjectFormatType, + args: [*]const [*]const u8, + arg_count: usize, + append_diagnostic: extern fn (*c_void, [*]const u8, usize) void, + context: *c_void, +) bool; + +extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void { + const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); + ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]); +} + +fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void { + if (ctx.link_msg.isNull()) { + try ctx.link_msg.resize(0); + } + try ctx.link_msg.append(msg); +} + +fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType { + return switch (ofmt) { + ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat, + ObjectFormat.coff => c.ZigLLVM_COFF, + ObjectFormat.elf => c.ZigLLVM_ELF, + ObjectFormat.macho => c.ZigLLVM_MachO, + ObjectFormat.wasm => c.ZigLLVM_Wasm, + }; +} + +fn constructLinkerArgs(ctx: *Context) !void { + switch (ctx.comp.target.getObjectFormat()) { + ObjectFormat.unknown => unreachable, + ObjectFormat.coff => return constructLinkerArgsCoff(ctx), + ObjectFormat.elf => return constructLinkerArgsElf(ctx), + ObjectFormat.macho => return constructLinkerArgsMachO(ctx), + ObjectFormat.wasm => return constructLinkerArgsWasm(ctx), + } +} + +fn constructLinkerArgsElf(ctx: *Context) !void { + //if (g->libc_link_lib != nullptr) { + // find_libc_lib_path(g); + //} + + //if (g->linker_script) { + // lj->args.append("-T"); + // lj->args.append(g->linker_script); + //} + + //if (g->no_rosegment_workaround) { + // lj->args.append("--no-rosegment"); + //} + //lj->args.append("--gc-sections"); + + //lj->args.append("-m"); + //lj->args.append(getLDMOption(&g->zig_target)); + + //bool is_lib = g->out_type == OutTypeLib; + //bool shared = !g->is_static && is_lib; + //Buf *soname = nullptr; + //if (g->is_static) { + // if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb || + // g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb) + // { + // lj->args.append("-Bstatic"); + // } else { + // lj->args.append("-static"); + // } + //} else if (shared) { + // lj->args.append("-shared"); + + // if (buf_len(&lj->out_file) == 0) { + // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "", + // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + // } + // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major); + //} + + //lj->args.append("-o"); + //lj->args.append(buf_ptr(&lj->out_file)); + + //if (lj->link_in_crt) { + // const char *crt1o; + // const char *crtbegino; + // if (g->is_static) { + // crt1o = "crt1.o"; + // crtbegino = "crtbeginT.o"; + // } else { + // crt1o = "Scrt1.o"; + // crtbegino = "crtbegin.o"; + // } + // lj->args.append(get_libc_file(g, crt1o)); + // lj->args.append(get_libc_file(g, "crti.o")); + // lj->args.append(get_libc_static_file(g, crtbegino)); + //} + + //for (size_t i = 0; i < g->rpath_list.length; i += 1) { + // Buf *rpath = g->rpath_list.at(i); + // add_rpath(lj, rpath); + //} + //if (g->each_lib_rpath) { + // for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + // LinkLib *link_lib = g->link_libs_list.at(i); + // if (buf_eql_str(link_lib->name, "c")) { + // continue; + // } + // bool does_exist; + // Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); + // if (os_file_exists(test_path, &does_exist) != ErrorNone) { + // zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); + // } + // if (does_exist) { + // add_rpath(lj, buf_create_from_str(lib_dir)); + // break; + // } + // } + // } + //} + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append("-L"); + // lj->args.append(lib_dir); + //} + + //if (g->libc_link_lib != nullptr) { + // lj->args.append("-L"); + // lj->args.append(buf_ptr(g->libc_lib_dir)); + + // lj->args.append("-L"); + // lj->args.append(buf_ptr(g->libc_static_lib_dir)); + //} + + //if (!g->is_static) { + // if (g->dynamic_linker != nullptr) { + // assert(buf_len(g->dynamic_linker) != 0); + // lj->args.append("-dynamic-linker"); + // lj->args.append(buf_ptr(g->dynamic_linker)); + // } else { + // Buf *resolved_dynamic_linker = get_dynamic_linker_path(g); + // lj->args.append("-dynamic-linker"); + // lj->args.append(buf_ptr(resolved_dynamic_linker)); + // } + //} + + //if (shared) { + // lj->args.append("-soname"); + // lj->args.append(buf_ptr(soname)); + //} + + // .o files + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + // if (g->libc_link_lib == nullptr) { + // Buf *builtin_o_path = build_o(g, "builtin"); + // lj->args.append(buf_ptr(builtin_o_path)); + // } + + // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage + // Buf *compiler_rt_o_path = build_compiler_rt(g); + // lj->args.append(buf_ptr(compiler_rt_o_path)); + //} + + //for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + // LinkLib *link_lib = g->link_libs_list.at(i); + // if (buf_eql_str(link_lib->name, "c")) { + // continue; + // } + // Buf *arg; + // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || + // buf_ends_with_str(link_lib->name, ".so")) + // { + // arg = link_lib->name; + // } else { + // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); + // } + // lj->args.append(buf_ptr(arg)); + //} + + //// libc dep + //if (g->libc_link_lib != nullptr) { + // if (g->is_static) { + // lj->args.append("--start-group"); + // lj->args.append("-lgcc"); + // lj->args.append("-lgcc_eh"); + // lj->args.append("-lc"); + // lj->args.append("-lm"); + // lj->args.append("--end-group"); + // } else { + // lj->args.append("-lgcc"); + // lj->args.append("--as-needed"); + // lj->args.append("-lgcc_s"); + // lj->args.append("--no-as-needed"); + // lj->args.append("-lc"); + // lj->args.append("-lm"); + // lj->args.append("-lgcc"); + // lj->args.append("--as-needed"); + // lj->args.append("-lgcc_s"); + // lj->args.append("--no-as-needed"); + // } + //} + + //// crt end + //if (lj->link_in_crt) { + // lj->args.append(get_libc_static_file(g, "crtend.o")); + // lj->args.append(get_libc_file(g, "crtn.o")); + //} + + //if (!g->is_native_target) { + // lj->args.append("--allow-shlib-undefined"); + //} + + //if (g->zig_target.os == OsZen) { + // lj->args.append("-e"); + // lj->args.append("_start"); + + // lj->args.append("--image-base=0x10000000"); + //} +} + +fn constructLinkerArgsCoff(ctx: *Context) void { + @panic("TODO"); +} + +fn constructLinkerArgsMachO(ctx: *Context) void { + @panic("TODO"); +} + +fn constructLinkerArgsWasm(ctx: *Context) void { + @panic("TODO"); +} + +fn addFnObjects(ctx: *Context) !void { + // at this point it's guaranteed nobody else has this lock, so we circumvent it + // and avoid having to be a coroutine + const fn_link_set = &ctx.comp.fn_link_set.private_data; + + var it = fn_link_set.first; + while (it) |node| { + const fn_val = node.data orelse { + // handle the tombstone. See Value.Fn.destroy. + it = node.next; + fn_link_set.remove(node); + ctx.comp.gpa().destroy(node); + continue; + }; + try ctx.args.append(fn_val.containing_object.ptr()); + it = node.next; + } +} diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 6f25df8674..63d69bd23e 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -22,6 +22,9 @@ #define ZIG_EXTERN_C #endif +// ATTENTION: If you modify this file, be sure to update the corresponding +// extern function declarations in the self-hosted compiler. + struct ZigLLVMDIType; struct ZigLLVMDIBuilder; struct ZigLLVMDICompileUnit; -- cgit v1.2.3 From cbfe9a407776d3b56a4da2a3bc3108a2f31b9212 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Jul 2018 23:37:17 -0400 Subject: fix @setEvalBranchQuota not respected in generic fn calls closes #1257 --- src/ir.cpp | 1 + test/cases/eval.zig | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index a12bd054a7..96ade9d392 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13092,6 +13092,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal impl_fn->ir_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; + impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; impl_fn->analyzed_executable.is_generic_instantiation = true; ira->codegen->fn_defs.append(impl_fn); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 83d2e80176..9da475994d 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -642,3 +642,13 @@ test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); assert(std.mem.eql(u8, str, "Int")); } + +test "setting backward branch quota just before a generic fn call" { + @setEvalBranchQuota(1001); + loopNTimes(1001); +} + +fn loopNTimes(comptime n: usize) void { + comptime var i = 0; + inline while (i < n) : (i += 1) {} +} -- cgit v1.2.3 From c393a399fb4b463f095a8510595263759fd82cd6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Jul 2018 10:51:18 -0400 Subject: fix invalid character test on windows --- src/tokenizer.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f7f41af8a6..1d3db5567a 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -460,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) { static void invalid_char_error(Tokenize *t, uint8_t c) { if (c == '\r') { tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); - } else if (isprint(c)) { + return; + } + + const char *sh = get_escape_shorthand(c); + if (sh) { + tokenize_error(t, "invalid character: '%s'", sh); + return; + } + + if (isprint(c)) { tokenize_error(t, "invalid character: '%c'", c); - } else { - const char *sh = get_escape_shorthand(c); - if (sh) { - tokenize_error(t, "invalid character: '%s'", sh); - } else { - tokenize_error(t, "invalid character: '\\x%x'", c); - } + return; } + + tokenize_error(t, "invalid character: '\\x%02x'", c); } void tokenize(Buf *buf, Tokenization *out) { -- cgit v1.2.3 From fd3a41dadc92e7b69b409af5f747004996465032 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 18 Jul 2018 17:00:42 +0200 Subject: Allow pointers to anything in extern/exported declarations (#1258) * type_allowed_in_extern accepts all ptr not size 0 * Generate correct headers for none extern structs/unions/enums --- src/analyze.cpp | 4 ++- src/codegen.cpp | 81 ++++++++++++++++++++++++++++++++------------------------- test/gen_h.zig | 47 +++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 2ace893508..06d611f80d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1454,7 +1454,9 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdFn: return type_entry->data.fn.fn_type_id.cc == CallingConventionC; case TypeTableEntryIdPointer: - return type_allowed_in_extern(g, type_entry->data.pointer.child_type); + if (type_size(g, type_entry) == 0) + return false; + return true; case TypeTableEntryIdStruct: return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; case TypeTableEntryIdOptional: diff --git a/src/codegen.cpp b/src/codegen.cpp index f8801ea132..6e121be270 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7508,51 +7508,60 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdPromise: zig_unreachable(); case TypeTableEntryIdEnum: - assert(type_entry->data.enumeration.layout == ContainerLayoutExtern); - fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); - for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; - Buf *value_buf = buf_alloc(); - bigint_append_buf(value_buf, &enum_field->value, 10); - fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); - if (field_i != type_entry->data.enumeration.src_field_count - 1) { - fprintf(out_h, ","); + if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { + fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); + for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { + TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; + Buf *value_buf = buf_alloc(); + bigint_append_buf(value_buf, &enum_field->value, 10); + fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); + if (field_i != type_entry->data.enumeration.src_field_count - 1) { + fprintf(out_h, ","); + } + fprintf(out_h, "\n"); } - fprintf(out_h, "\n"); + fprintf(out_h, "};\n\n"); + } else { + fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name)); } - fprintf(out_h, "};\n\n"); break; case TypeTableEntryIdStruct: - assert(type_entry->data.structure.layout == ContainerLayoutExtern); - fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); - for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { - TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); - - if (struct_field->type_entry->id == TypeTableEntryIdArray) { - fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), - buf_ptr(struct_field->name), - struct_field->type_entry->data.array.len); - } else { - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); - } + if (type_entry->data.structure.layout == ContainerLayoutExtern) { + fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); + for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { + TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; + + Buf *type_name_buf = buf_alloc(); + get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); + + if (struct_field->type_entry->id == TypeTableEntryIdArray) { + fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), + buf_ptr(struct_field->name), + struct_field->type_entry->data.array.len); + } else { + fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); + } + } + fprintf(out_h, "};\n\n"); + } else { + fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name)); } - fprintf(out_h, "};\n\n"); break; case TypeTableEntryIdUnion: - assert(type_entry->data.unionation.layout == ContainerLayoutExtern); - fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); - for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { - TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, union_field->type_entry, type_name_buf); - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); + if (type_entry->data.unionation.layout == ContainerLayoutExtern) { + fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); + for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { + TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; + + Buf *type_name_buf = buf_alloc(); + get_c_type(g, gen_h, union_field->type_entry, type_name_buf); + fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); + } + fprintf(out_h, "};\n\n"); + } else { + fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name)); } - fprintf(out_h, "};\n\n"); break; case TypeTableEntryIdOpaque: fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name)); diff --git a/test/gen_h.zig b/test/gen_h.zig index e6a757ea6d..b3aaa263d6 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -76,4 +76,51 @@ pub fn addCases(cases: *tests.GenHContext) void { \\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]); \\ ); + + cases.add("ptr to zig struct", + \\const S = struct { + \\ a: u8, + \\}; + \\ + \\export fn a(s: *S) u8 { + \\ return s.a; + \\} + + , + \\struct S; + \\TEST_EXPORT uint8_t a(struct S * s); + \\ + ); + + cases.add("ptr to zig union", + \\const U = union(enum) { + \\ A: u8, + \\ B: u16, + \\}; + \\ + \\export fn a(s: *U) u8 { + \\ return s.A; + \\} + + , + \\union U; + \\TEST_EXPORT uint8_t a(union U * s); + \\ + ); + + cases.add("ptr to zig enum", + \\const E = enum(u8) { + \\ A, + \\ B, + \\}; + \\ + \\export fn a(s: *E) u8 { + \\ return @enumToInt(s.*); + \\} + + , + \\enum E; + \\TEST_EXPORT uint8_t a(enum E * s); + \\ + ); } -- cgit v1.2.3 From a9f0681f85679b602cd5f322f6b9b4b2d18a5c4a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 10:47:17 -0400 Subject: prevent non-export symbols from clobbering builtins closes #1263 --- src/codegen.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 6e121be270..7420da9797 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -60,6 +60,33 @@ PackageTableEntry *new_anonymous_package(void) { return new_package("", ""); } +static const char *symbols_that_llvm_depends_on[] = { + "memcpy", + "memset", + "sqrt", + "powi", + "sin", + "cos", + "pow", + "exp", + "exp2", + "log", + "log10", + "log2", + "fma", + "fabs", + "minnum", + "maxnum", + "copysign", + "floor", + "ceil", + "trunc", + "rint", + "nearbyint", + "round", + // TODO probably all of compiler-rt needs to go here +}; + CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir) { @@ -94,6 +121,10 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); + for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { + g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); + } + if (root_src_path) { Buf *src_basename = buf_alloc(); Buf *src_dir = buf_alloc(); -- cgit v1.2.3 From 0a880d5e6034ab35778e4f4db33a385b2a5ad7cc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Jul 2018 18:05:01 -0400 Subject: fix generation of error defers for fns inside fns closes #878 --- src/ir.cpp | 84 ++++++++++++++++++++++++++++++++++++---------------- test/cases/defer.zig | 15 ++++++++++ 2 files changed, 74 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 96ade9d392..fe5fb77085 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2961,16 +2961,34 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; - while (inner_scope != outer_scope) { - assert(inner_scope); - if (inner_scope->id == ScopeIdDefer) { - AstNode *defer_node = inner_scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - results[defer_kind] += 1; + Scope *scope = inner_scope; + while (scope != outer_scope) { + assert(scope); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + results[defer_kind] += 1; + scope = scope->parent; + continue; + } + case ScopeIdDecls: + case ScopeIdFnDef: + return; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); } - inner_scope = inner_scope->parent; } } @@ -2986,27 +3004,43 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o if (!scope) return is_noreturn; - if (scope->id == ScopeIdDefer) { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - if (defer_kind == ReturnKindUnconditional || - (gen_error_defers && defer_kind == ReturnKindError)) - { - AstNode *defer_expr_node = defer_node->data.defer.expr; - Scope *defer_expr_scope = defer_node->data.defer.expr_scope; - IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); - if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { - is_noreturn = true; - } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + if (defer_kind == ReturnKindUnconditional || + (gen_error_defers && defer_kind == ReturnKindError)) + { + AstNode *defer_expr_node = defer_node->data.defer.expr; + Scope *defer_expr_scope = defer_node->data.defer.expr_scope; + IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); + if (defer_expr_value != irb->codegen->invalid_instruction) { + if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) { + is_noreturn = true; + } else { + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + } } } + scope = scope->parent; + continue; } - + case ScopeIdDecls: + case ScopeIdFnDef: + return is_noreturn; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + case ScopeIdCoroPrelude: + zig_unreachable(); } - scope = scope->parent; } return is_noreturn; } diff --git a/test/cases/defer.zig b/test/cases/defer.zig index d2b00d1f91..7d4d1bc3d8 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -61,3 +61,18 @@ test "defer and labeled break" { assert(i == 1); } + +test "errdefer does not apply to fn inside fn" { + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); +} + +fn testNestedFnErrDefer() error!void { + var a: i32 = 0; + errdefer a += 1; + const S = struct { + fn baz() error { + return error.Bad; + } + }; + return S.baz(); +} -- cgit v1.2.3 From 58c5f94a99a78346286065bbf390e4c30be1b707 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 20 Jul 2018 23:37:37 -0400 Subject: self-hosted: share C++ code for finding libc on windows --- CMakeLists.txt | 7 + src-self-hosted/c.zig | 1 + src-self-hosted/libc_installation.zig | 25 ++- src/analyze.cpp | 7 +- src/os.cpp | 248 +----------------------- src/os.hpp | 10 +- src/windows_sdk.cpp | 346 ++++++++++++++++++++++++++++++++++ src/windows_sdk.h | 45 +++++ std/os/windows/advapi32.zig | 30 +++ std/os/windows/index.zig | 192 +------------------ std/os/windows/kernel32.zig | 162 ++++++++++++++++ std/os/windows/ole32.zig | 18 ++ std/os/windows/shell32.zig | 4 + std/os/windows/shlwapi.zig | 4 + std/os/windows/user32.zig | 4 + 15 files changed, 658 insertions(+), 445 deletions(-) create mode 100644 src/windows_sdk.cpp create mode 100644 src/windows_sdk.h create mode 100644 std/os/windows/advapi32.zig create mode 100644 std/os/windows/kernel32.zig create mode 100644 std/os/windows/ole32.zig create mode 100644 std/os/windows/shell32.zig create mode 100644 std/os/windows/shlwapi.zig create mode 100644 std/os/windows/user32.zig (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 30d7bb4856..20755cfc1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,6 +426,7 @@ set(ZIG_SOURCES ) set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" + "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) set(ZIG_STD_FILES @@ -567,8 +568,14 @@ set(ZIG_STD_FILES "os/linux/x86_64.zig" "os/path.zig" "os/time.zig" + "os/windows/advapi32.zig" "os/windows/error.zig" "os/windows/index.zig" + "os/windows/kernel32.zig" + "os/windows/ole32.zig" + "os/windows/shell32.zig" + "os/windows/shlwapi.zig" + "os/windows/user32.zig" "os/windows/util.zig" "os/zen.zig" "rand/index.zig" diff --git a/src-self-hosted/c.zig b/src-self-hosted/c.zig index 3912462985..778d851240 100644 --- a/src-self-hosted/c.zig +++ b/src-self-hosted/c.zig @@ -4,4 +4,5 @@ pub use @cImport({ @cInclude("inttypes.h"); @cInclude("config.h"); @cInclude("zig_llvm.h"); + @cInclude("windows_sdk.h"); }); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 8444c47310..5a9b7561fa 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const event = std.event; const Target = @import("target.zig").Target; +const c = @import("c.zig"); /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { @@ -122,7 +123,7 @@ pub const LibCInstallation = struct { \\# Only needed when targeting Windows. \\kernel32_lib_dir={} \\ - \\# The full path to the dynamic linker. + \\# The full path to the dynamic linker, on the target system. \\# Only needed when targeting Linux. \\dynamic_linker_path={} \\ @@ -143,10 +144,24 @@ pub const LibCInstallation = struct { errdefer group.cancelAll(); switch (builtin.os) { builtin.Os.windows => { - try group.call(findNativeIncludeDirWindows, self, loop); - try group.call(findNativeLibDirWindows, self, loop); - try group.call(findNativeMsvcLibDir, self, loop); - try group.call(findNativeKernel32LibDir, self, loop); + var sdk: *c.ZigWindowsSDK = undefined; + switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) { + c.ZigFindWindowsSdkError.None => { + defer c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + + errdefer if (self.msvc_lib_dir) |s| loop.allocator.free(s); + if (sdk.msvc_lib_dir_ptr) |ptr| { + self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); + } + //try group.call(findNativeIncludeDirWindows, self, loop); + //try group.call(findNativeLibDirWindows, self, loop); + //try group.call(findNativeMsvcLibDir, self, loop); + //try group.call(findNativeKernel32LibDir, self, loop); + }, + c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, + c.ZigFindWindowsSdkError.NotFound => return error.NotFound, + c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound, + } }, builtin.Os.linux => { try group.call(findNativeIncludeDirLinux, self, loop); diff --git a/src/analyze.cpp b/src/analyze.cpp index 06d611f80d..6bbe5f6037 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4379,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { if (g->win_sdk == nullptr) { - if (os_find_windows_sdk(&g->win_sdk)) { + if (zig_find_windows_sdk(&g->win_sdk)) { fprintf(stderr, "unable to determine windows sdk path\n"); exit(1); } @@ -4499,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) { ZigWindowsSDK *sdk = get_windows_sdk(g); if (g->msvc_lib_dir == nullptr) { - Buf* vc_lib_dir = buf_alloc(); - if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { + if (sdk->msvc_lib_dir_ptr == nullptr) { fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); exit(1); } - g->msvc_lib_dir = vc_lib_dir; + g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); } if (g->libc_lib_dir == nullptr) { diff --git a/src/os.cpp b/src/os.cpp index d52295950d..91a591a7b6 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -26,7 +26,6 @@ #include #include #include -#include "windows_com.hpp" typedef SSIZE_T ssize_t; #else @@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) { #endif } -int os_find_windows_sdk(ZigWindowsSDK **out_sdk) { -#if defined(ZIG_OS_WINDOWS) - ZigWindowsSDK *result_sdk = allocate(1); - buf_resize(&result_sdk->path10, 0); - buf_resize(&result_sdk->path81, 0); - - HKEY key; - HRESULT rc; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key); - if (rc != ERROR_SUCCESS) { - return ErrorFileNotFound; - } - - { - DWORD tmp_buf_len = MAX_PATH; - buf_resize(&result_sdk->path10, tmp_buf_len); - rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len); - if (rc == ERROR_FILE_NOT_FOUND) { - buf_resize(&result_sdk->path10, 0); - } else { - buf_resize(&result_sdk->path10, tmp_buf_len); - } - } - { - DWORD tmp_buf_len = MAX_PATH; - buf_resize(&result_sdk->path81, tmp_buf_len); - rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len); - if (rc == ERROR_FILE_NOT_FOUND) { - buf_resize(&result_sdk->path81, 0); - } else { - buf_resize(&result_sdk->path81, tmp_buf_len); - } - } - - if (buf_len(&result_sdk->path10) != 0) { - Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10)); - - // enumerate files in sdk path looking for latest version - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - return ErrorFileNotFound; - } - int v0 = 0, v1 = 0, v2 = 0, v3 = 0; - bool found_version_dir = false; - for (;;) { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - int c0 = 0, c1 = 0, c2 = 0, c3 = 0; - sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3); - if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) { - // Microsoft released 26624 as 10240 accidentally. - // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive - c2 = 26624; - } - if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { - v0 = c0, v1 = c1, v2 = c2, v3 = c3; - buf_init_from_str(&result_sdk->version10, ffd.cFileName); - found_version_dir = true; - } - } - if (FindNextFile(hFind, &ffd) == 0) { - FindClose(hFind); - break; - } - } - if (!found_version_dir) { - buf_resize(&result_sdk->path10, 0); - } - } - - if (buf_len(&result_sdk->path81) != 0) { - Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81)); - - // enumerate files in sdk path looking for latest version - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - return ErrorFileNotFound; - } - int v0 = 0, v1 = 0; - bool found_version_dir = false; - for (;;) { - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - int c0 = 0, c1 = 0; - sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); - if ((c0 > v0) || (c1 > v1)) { - v0 = c0, v1 = c1; - buf_init_from_str(&result_sdk->version81, ffd.cFileName); - found_version_dir = true; - } - } - if (FindNextFile(hFind, &ffd) == 0) { - FindClose(hFind); - break; - } - } - if (!found_version_dir) { - buf_resize(&result_sdk->path81, 0); - } - } - - *out_sdk = result_sdk; - return 0; -#else - return ErrorFileNotFound; -#endif -} - -int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) { -#if defined(ZIG_OS_WINDOWS) - buf_resize(output_buf, 0); - //COM Smart Pointerse requires explicit scope - { - HRESULT rc; - rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if (rc != S_OK) { - goto com_done; - } - - //This COM class is installed when a VS2017 - ISetupConfigurationPtr setup_config; - rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); - if (rc != S_OK) { - goto com_done; - } - - IEnumSetupInstancesPtr all_instances; - rc = setup_config->EnumInstances(&all_instances); - if (rc != S_OK) { - goto com_done; - } - - ISetupInstance* curr_instance; - ULONG found_inst; - while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { - BSTR bstr_inst_path; - rc = curr_instance->GetInstallationPath(&bstr_inst_path); - if (rc != S_OK) { - goto com_done; - } - //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length - UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); - ULONG tmp_path_len = bstr_path_len / 2 + 1; - char* conv_path = (char*)bstr_inst_path; - char *tmp_path = (char*)alloca(tmp_path_len); - memset(tmp_path, 0, tmp_path_len); - uint32_t c = 0; - for (uint32_t i = 0; i < bstr_path_len; i += 2) { - tmp_path[c] = conv_path[i]; - ++c; - assert(c != tmp_path_len); - } - - buf_append_str(output_buf, tmp_path); - buf_append_char(output_buf, '\\'); - - Buf* tmp_buf = buf_alloc(); - buf_append_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); - FILE* tools_file = fopen(buf_ptr(tmp_buf), "r"); - if (!tools_file) { - goto com_done; - } - memset(tmp_path, 0, tmp_path_len); - fgets(tmp_path, tmp_path_len, tools_file); - strtok(tmp_path, " \r\n"); - fclose(tools_file); - buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); - switch (platform_type) { - case ZigLLVM_x86: - buf_append_str(output_buf, "x86\\"); - break; - case ZigLLVM_x86_64: - buf_append_str(output_buf, "x64\\"); - break; - case ZigLLVM_arm: - buf_append_str(output_buf, "arm\\"); - break; - default: - zig_panic("Attemped to use vcruntime for non-supported platform."); - } - buf_resize(tmp_buf, 0); - buf_append_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "vcruntime.lib"); - - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; - } - } - } - -com_done:; - HKEY key; - HRESULT rc; - rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); - if (rc != ERROR_SUCCESS) { - return ErrorFileNotFound; - } - - DWORD dw_type = 0; - DWORD cb_data = 0; - rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); - if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { - return ErrorFileNotFound; - } - - Buf* tmp_buf = buf_alloc_fixed(cb_data); - RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data); - //RegQueryValueExA returns the length of the string INCLUDING the null terminator - buf_resize(tmp_buf, cb_data-1); - buf_append_str(tmp_buf, "VC\\Lib\\"); - switch (platform_type) { - case ZigLLVM_x86: - //x86 is in the root of the Lib folder - break; - case ZigLLVM_x86_64: - buf_append_str(tmp_buf, "amd64\\"); - break; - case ZigLLVM_arm: - buf_append_str(tmp_buf, "arm\\"); - break; - default: - zig_panic("Attemped to use vcruntime for non-supported platform."); - } - - buf_append_buf(output_buf, tmp_buf); - buf_append_str(tmp_buf, "vcruntime.lib"); - - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; - } else { - buf_resize(output_buf, 0); - return ErrorFileNotFound; - } -#else - return ErrorFileNotFound; -#endif -} - int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); + buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); + buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { return 0; } @@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy #if defined(ZIG_OS_WINDOWS) { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10)); + buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy } { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81)); + buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); diff --git a/src/os.hpp b/src/os.hpp index b94e98ec3d..cfe4e8f3a2 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -12,6 +12,7 @@ #include "buffer.hpp" #include "error.hpp" #include "zig_llvm.h" +#include "windows_sdk.h" #include #include @@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c); int os_self_exe_path(Buf *out_path); -struct ZigWindowsSDK { - Buf path10; - Buf version10; - Buf path81; - Buf version81; -}; - -int os_find_windows_sdk(ZigWindowsSDK **out_sdk); -int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); diff --git a/src/windows_sdk.cpp b/src/windows_sdk.cpp new file mode 100644 index 0000000000..059bdee4e9 --- /dev/null +++ b/src/windows_sdk.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "windows_sdk.h" + +#if defined(_WIN32) + +#include "windows_com.hpp" +#include +#include + +struct ZigWindowsSDKPrivate { + ZigWindowsSDK base; +}; + +enum NativeArch { + NativeArchArm, + NativeArchi386, + NativeArchx86_64, +}; + +#if defined(_M_ARM) || defined(__arm_) +static const NativeArch native_arch = NativeArchArm; +#endif +#if defined(_M_IX86) || defined(__i386__) +static const NativeArch native_arch = NativeArchi386; +#endif +#if defined(_M_X64) || defined(__x86_64__) +static const NativeArch native_arch = NativeArchx86_64; +#endif + +void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) { + if (sdk == nullptr) { + return; + } + free((void*)sdk->path10_ptr); + free((void*)sdk->version10_ptr); + free((void*)sdk->path81_ptr); + free((void*)sdk->version81_ptr); + free((void*)sdk->msvc_lib_dir_ptr); +} + +static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) { + //COM Smart Pointers requires explicit scope + { + HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (rc != S_OK && rc != S_FALSE) { + goto com_done; + } + + //This COM class is installed when a VS2017 + ISetupConfigurationPtr setup_config; + rc = setup_config.CreateInstance(__uuidof(SetupConfiguration)); + if (rc != S_OK) { + goto com_done; + } + + IEnumSetupInstancesPtr all_instances; + rc = setup_config->EnumInstances(&all_instances); + if (rc != S_OK) { + goto com_done; + } + + ISetupInstance* curr_instance; + ULONG found_inst; + while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) { + BSTR bstr_inst_path; + rc = curr_instance->GetInstallationPath(&bstr_inst_path); + if (rc != S_OK) { + goto com_done; + } + //BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length + //TODO call an actual function to do this + UINT bstr_path_len = *((UINT*)bstr_inst_path - 1); + ULONG tmp_path_len = bstr_path_len / 2 + 1; + char* conv_path = (char*)bstr_inst_path; + // TODO don't use alloca + char *tmp_path = (char*)alloca(tmp_path_len); + memset(tmp_path, 0, tmp_path_len); + uint32_t c = 0; + for (uint32_t i = 0; i < bstr_path_len; i += 2) { + tmp_path[c] = conv_path[i]; + ++c; + assert(c != tmp_path_len); + } + char output_path[4096]; + output_path[0] = 0; + char *out_append_ptr = output_path; + + out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path); + + char tmp_buf[4096]; + sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"); + FILE* tools_file = fopen(tmp_buf, "rb"); + if (!tools_file) { + goto com_done; + } + memset(tmp_path, 0, tmp_path_len); + fgets(tmp_path, tmp_path_len, tools_file); + strtok(tmp_path, " \r\n"); + fclose(tools_file); + out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path); + switch (native_arch) { + case NativeArchi386: + out_append_ptr += sprintf(out_append_ptr, "x86\\"); + break; + case NativeArchx86_64: + out_append_ptr += sprintf(out_append_ptr, "x64\\"); + break; + case NativeArchArm: + out_append_ptr += sprintf(out_append_ptr, "arm\\"); + break; + } + sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib"); + + if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { + priv->base.msvc_lib_dir_ptr = strdup(output_path); + if (priv->base.msvc_lib_dir_ptr == nullptr) { + return ZigFindWindowsSdkErrorOutOfMemory; + } + priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr); + return ZigFindWindowsSdkErrorNone; + } + } + } + +com_done:; + HKEY key; + HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key); + if (rc != ERROR_SUCCESS) { + return ZigFindWindowsSdkErrorNotFound; + } + + DWORD dw_type = 0; + DWORD cb_data = 0; + rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data); + if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) { + return ZigFindWindowsSdkErrorNotFound; + } + + char tmp_buf[4096]; + + RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data); + // RegQueryValueExA returns the length of the string INCLUDING the null terminator + char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1); + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\"); + switch (native_arch) { + case NativeArchi386: + //x86 is in the root of the Lib folder + break; + case NativeArchx86_64: + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\"); + break; + case NativeArchArm: + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\"); + break; + } + + char *output_path = strdup(tmp_buf); + if (output_path == nullptr) { + return ZigFindWindowsSdkErrorOutOfMemory; + } + + tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib"); + + if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) { + priv->base.msvc_lib_dir_ptr = output_path; + priv->base.msvc_lib_dir_len = strlen(output_path); + return ZigFindWindowsSdkErrorNone; + } else { + free(output_path); + return ZigFindWindowsSdkErrorNotFound; + } +} + +static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) { + if (priv->base.path10_ptr == nullptr) + return ZigFindWindowsSdkErrorNone; + + char sdk_lib_dir[4096]; + int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr); + if (n < 0 || n >= 4096) { + return ZigFindWindowsSdkErrorPathTooLong; + } + + // enumerate files in sdk path looking for latest version + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); + if (hFind == INVALID_HANDLE_VALUE) { + return ZigFindWindowsSdkErrorNotFound; + } + int v0 = 0, v1 = 0, v2 = 0, v3 = 0; + for (;;) { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + int c0 = 0, c1 = 0, c2 = 0, c3 = 0; + sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3); + if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) { + // Microsoft released 26624 as 10240 accidentally. + // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive + c2 = 26624; + } + if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { + v0 = c0, v1 = c1, v2 = c2, v3 = c3; + free((void*)priv->base.version10_ptr); + priv->base.version10_ptr = strdup(ffd.cFileName); + if (priv->base.version10_ptr == nullptr) { + FindClose(hFind); + return ZigFindWindowsSdkErrorOutOfMemory; + } + } + } + if (FindNextFile(hFind, &ffd) == 0) { + FindClose(hFind); + break; + } + } + priv->base.version10_len = strlen(priv->base.version10_ptr); + return ZigFindWindowsSdkErrorNone; +} + +static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) { + if (priv->base.path81_ptr == nullptr) + return ZigFindWindowsSdkErrorNone; + + char sdk_lib_dir[4096]; + int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr); + if (n < 0 || n >= 4096) { + return ZigFindWindowsSdkErrorPathTooLong; + } + + // enumerate files in sdk path looking for latest version + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd); + if (hFind == INVALID_HANDLE_VALUE) { + return ZigFindWindowsSdkErrorNotFound; + } + int v0 = 0, v1 = 0; + for (;;) { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + int c0 = 0, c1 = 0; + sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); + if ((c0 > v0) || (c1 > v1)) { + v0 = c0, v1 = c1; + free((void*)priv->base.version81_ptr); + priv->base.version81_ptr = strdup(ffd.cFileName); + if (priv->base.version81_ptr == nullptr) { + FindClose(hFind); + return ZigFindWindowsSdkErrorOutOfMemory; + } + } + } + if (FindNextFile(hFind, &ffd) == 0) { + FindClose(hFind); + break; + } + } + priv->base.version81_len = strlen(priv->base.version81_ptr); + return ZigFindWindowsSdkErrorNone; +} + +ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { + ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate)); + if (priv == nullptr) { + return ZigFindWindowsSdkErrorOutOfMemory; + } + + HKEY key; + HRESULT rc; + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, + KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key); + if (rc != ERROR_SUCCESS) { + zig_free_windows_sdk(&priv->base); + return ZigFindWindowsSdkErrorNotFound; + } + + { + DWORD tmp_buf_len = MAX_PATH; + priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1); + if (priv->base.path10_ptr == nullptr) { + zig_free_windows_sdk(&priv->base); + return ZigFindWindowsSdkErrorOutOfMemory; + } + rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len); + if (rc == ERROR_SUCCESS) { + priv->base.path10_len = tmp_buf_len; + } else { + free((void*)priv->base.path10_ptr); + priv->base.path10_ptr = nullptr; + } + } + { + DWORD tmp_buf_len = MAX_PATH; + priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1); + if (priv->base.path81_ptr == nullptr) { + zig_free_windows_sdk(&priv->base); + return ZigFindWindowsSdkErrorOutOfMemory; + } + rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len); + if (rc == ERROR_SUCCESS) { + priv->base.path81_len = tmp_buf_len; + } else { + free((void*)priv->base.path81_ptr); + priv->base.path81_ptr = nullptr; + } + } + + { + ZigFindWindowsSdkError err = find_10_version(priv); + if (err == ZigFindWindowsSdkErrorOutOfMemory) { + zig_free_windows_sdk(&priv->base); + return err; + } + } + { + ZigFindWindowsSdkError err = find_81_version(priv); + if (err == ZigFindWindowsSdkErrorOutOfMemory) { + zig_free_windows_sdk(&priv->base); + return err; + } + } + + { + ZigFindWindowsSdkError err = find_msvc_lib_dir(priv); + if (err == ZigFindWindowsSdkErrorOutOfMemory) { + zig_free_windows_sdk(&priv->base); + return err; + } + } + + *out_sdk = &priv->base; + return ZigFindWindowsSdkErrorNone; +} + +#else + +void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {} +ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { + return ZigFindWindowsSdkErrorNotFound; +} + +#endif diff --git a/src/windows_sdk.h b/src/windows_sdk.h new file mode 100644 index 0000000000..080ed55bed --- /dev/null +++ b/src/windows_sdk.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_WINDOWS_SDK_H +#define ZIG_WINDOWS_SDK_H + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +struct ZigWindowsSDK { + const char *path10_ptr; + size_t path10_len; + + const char *version10_ptr; + size_t version10_len; + + const char *path81_ptr; + size_t path81_len; + + const char *version81_ptr; + size_t version81_len; + + const char *msvc_lib_dir_ptr; + size_t msvc_lib_dir_len; +}; + +enum ZigFindWindowsSdkError { + ZigFindWindowsSdkErrorNone, + ZigFindWindowsSdkErrorOutOfMemory, + ZigFindWindowsSdkErrorNotFound, + ZigFindWindowsSdkErrorPathTooLong, +}; + +ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); + +ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); + +#endif diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig new file mode 100644 index 0000000000..dcb5a636ea --- /dev/null +++ b/std/os/windows/advapi32.zig @@ -0,0 +1,30 @@ +use @import("index.zig"); + +pub const PROV_RSA_FULL = 1; + +pub const REGSAM = ACCESS_MASK; +pub const ACCESS_MASK = DWORD; +pub const PHKEY = &HKEY; +pub const HKEY = &HKEY__; +pub const HKEY__ = extern struct { + unused: c_int, +}; +pub const LSTATUS = LONG; + +pub extern "advapi32" stdcallcc fn CryptAcquireContextA( + phProv: *HCRYPTPROV, + pszContainer: ?LPCSTR, + pszProvider: ?LPCSTR, + dwProvType: DWORD, + dwFlags: DWORD, +) BOOL; + +pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; + +pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; + +pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM, + phkResult: &HKEY,) LSTATUS; + +pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, + lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 96c4d3861c..90ccfaf6c5 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,190 +1,19 @@ const std = @import("../../index.zig"); const assert = std.debug.assert; + +pub use @import("advapi32.zig"); +pub use @import("kernel32.zig"); +pub use @import("ole32.zig"); +pub use @import("shell32.zig"); +pub use @import("shlwapi.zig"); +pub use @import("user32.zig"); + test "import" { _ = @import("util.zig"); } pub const ERROR = @import("error.zig"); -pub extern "advapi32" stdcallcc fn CryptAcquireContextA( - phProv: *HCRYPTPROV, - pszContainer: ?LPCSTR, - pszProvider: ?LPCSTR, - dwProvType: DWORD, - dwFlags: DWORD, -) BOOL; - -pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL; - -pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; - -pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; - -pub extern "kernel32" stdcallcc fn CreateDirectoryA( - lpPathName: LPCSTR, - lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, -) BOOL; - -pub extern "kernel32" stdcallcc fn CreateFileA( - lpFileName: LPCSTR, - dwDesiredAccess: DWORD, - dwShareMode: DWORD, - lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, - dwCreationDisposition: DWORD, - dwFlagsAndAttributes: DWORD, - hTemplateFile: ?HANDLE, -) HANDLE; - -pub extern "kernel32" stdcallcc fn CreatePipe( - hReadPipe: *HANDLE, - hWritePipe: *HANDLE, - lpPipeAttributes: *const SECURITY_ATTRIBUTES, - nSize: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn CreateProcessA( - lpApplicationName: ?LPCSTR, - lpCommandLine: LPSTR, - lpProcessAttributes: ?*SECURITY_ATTRIBUTES, - lpThreadAttributes: ?*SECURITY_ATTRIBUTES, - bInheritHandles: BOOL, - dwCreationFlags: DWORD, - lpEnvironment: ?*c_void, - lpCurrentDirectory: ?LPCSTR, - lpStartupInfo: *STARTUPINFOA, - lpProcessInformation: *PROCESS_INFORMATION, -) BOOL; - -pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( - lpSymlinkFileName: LPCSTR, - lpTargetFileName: LPCSTR, - dwFlags: DWORD, -) BOOLEAN; - -pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; - -pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; - -pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; - -pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; - -pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; -pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; - -pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; - -pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; - -pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; - -pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; - -pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; - -pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; - -pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; - -pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; - -pub extern "kernel32" stdcallcc fn GetLastError() DWORD; - -pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( - in_hFile: HANDLE, - in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - out_lpFileInformation: *c_void, - in_dwBufferSize: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( - hFile: HANDLE, - lpszFilePath: LPSTR, - cchFilePath: DWORD, - dwFlags: DWORD, -) DWORD; - -pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; -pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; -pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; - -pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; -pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; -pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; -pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; -pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; -pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; - -pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; - -pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; - -pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; - -pub extern "kernel32" stdcallcc fn MoveFileExA( - lpExistingFileName: LPCSTR, - lpNewFileName: LPCSTR, - dwFlags: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; - -pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; - -pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; - -pub extern "kernel32" stdcallcc fn ReadFile( - in_hFile: HANDLE, - out_lpBuffer: *c_void, - in_nNumberOfBytesToRead: DWORD, - out_lpNumberOfBytesRead: *DWORD, - in_out_lpOverlapped: ?*OVERLAPPED, -) BOOL; - -pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; - -pub extern "kernel32" stdcallcc fn SetFilePointerEx( - in_fFile: HANDLE, - in_liDistanceToMove: LARGE_INTEGER, - out_opt_ldNewFilePointer: ?*LARGE_INTEGER, - in_dwMoveMethod: DWORD, -) BOOL; - -pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; - -pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; - -pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; - -pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; - -pub extern "kernel32" stdcallcc fn WriteFile( - in_hFile: HANDLE, - in_lpBuffer: *const c_void, - in_nNumberOfBytesToWrite: DWORD, - out_lpNumberOfBytesWritten: ?*DWORD, - in_out_lpOverlapped: ?*OVERLAPPED, -) BOOL; - -//TODO: call unicode versions instead of relying on ANSI code page -pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; - -pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; - -pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; - -pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; - -pub const PROV_RSA_FULL = 1; - pub const BOOL = c_int; pub const BOOLEAN = BYTE; pub const BYTE = u8; @@ -206,6 +35,7 @@ pub const LPSTR = [*]CHAR; pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; pub const LPVOID = *c_void; pub const LPWSTR = [*]WCHAR; +pub const LPCWSTR = [*]const WCHAR; pub const PVOID = *c_void; pub const PWSTR = [*]WCHAR; pub const SIZE_T = usize; @@ -442,10 +272,6 @@ pub const SYSTEM_INFO = extern struct { wProcessorRevision: WORD, }; -pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; - -pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; - pub const HRESULT = c_long; pub const KNOWNFOLDERID = GUID; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig new file mode 100644 index 0000000000..fa3473ad05 --- /dev/null +++ b/std/os/windows/kernel32.zig @@ -0,0 +1,162 @@ +use @import("index.zig"); + +pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; + +pub extern "kernel32" stdcallcc fn CreateDirectoryA( + lpPathName: LPCSTR, + lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateFileA( + lpFileName: LPCSTR, + dwDesiredAccess: DWORD, + dwShareMode: DWORD, + lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, + dwCreationDisposition: DWORD, + dwFlagsAndAttributes: DWORD, + hTemplateFile: ?HANDLE, +) HANDLE; + +pub extern "kernel32" stdcallcc fn CreatePipe( + hReadPipe: *HANDLE, + hWritePipe: *HANDLE, + lpPipeAttributes: *const SECURITY_ATTRIBUTES, + nSize: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateProcessA( + lpApplicationName: ?LPCSTR, + lpCommandLine: LPSTR, + lpProcessAttributes: ?*SECURITY_ATTRIBUTES, + lpThreadAttributes: ?*SECURITY_ATTRIBUTES, + bInheritHandles: BOOL, + dwCreationFlags: DWORD, + lpEnvironment: ?*c_void, + lpCurrentDirectory: ?LPCSTR, + lpStartupInfo: *STARTUPINFOA, + lpProcessInformation: *PROCESS_INFORMATION, +) BOOL; + +pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( + lpSymlinkFileName: LPCSTR, + lpTargetFileName: LPCSTR, + dwFlags: DWORD, +) BOOLEAN; + +pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; + +pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; + +pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; + +pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; + +pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; +pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; +pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; + +pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; + +pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; + +pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD; + +pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8; + +pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD; + +pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; + +pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; + +pub extern "kernel32" stdcallcc fn GetLastError() DWORD; + +pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( + in_hFile: HANDLE, + in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, + out_lpFileInformation: *c_void, + in_dwBufferSize: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( + hFile: HANDLE, + lpszFilePath: LPSTR, + cchFilePath: DWORD, + dwFlags: DWORD, +) DWORD; + +pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; +pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void; +pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; + +pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; +pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; +pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; +pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; +pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; +pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; + +pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; + +pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; + +pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; + +pub extern "kernel32" stdcallcc fn MoveFileExA( + lpExistingFileName: LPCSTR, + lpNewFileName: LPCSTR, + dwFlags: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; + +pub extern "kernel32" stdcallcc fn ReadFile( + in_hFile: HANDLE, + out_lpBuffer: *c_void, + in_nNumberOfBytesToRead: DWORD, + out_lpNumberOfBytesRead: *DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, +) BOOL; + +pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; + +pub extern "kernel32" stdcallcc fn SetFilePointerEx( + in_fFile: HANDLE, + in_liDistanceToMove: LARGE_INTEGER, + out_opt_ldNewFilePointer: ?*LARGE_INTEGER, + in_dwMoveMethod: DWORD, +) BOOL; + +pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL; + +pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void; + +pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL; + +pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD; + +pub extern "kernel32" stdcallcc fn WriteFile( + in_hFile: HANDLE, + in_lpBuffer: *const c_void, + in_nNumberOfBytesToWrite: DWORD, + out_lpNumberOfBytesWritten: ?*DWORD, + in_out_lpOverlapped: ?*OVERLAPPED, +) BOOL; + +//TODO: call unicode versions instead of relying on ANSI code page +pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; + +pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; diff --git a/std/os/windows/ole32.zig b/std/os/windows/ole32.zig new file mode 100644 index 0000000000..84d8089d07 --- /dev/null +++ b/std/os/windows/ole32.zig @@ -0,0 +1,18 @@ +use @import("index.zig"); + +pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; +pub extern "ole32.dll" stdcallcc fn CoUninitialize() void; +pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD; +pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; + + +pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; +pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; +pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; +pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY; +pub const COINIT = extern enum { + COINIT_APARTMENTTHREADED = 2, + COINIT_MULTITHREADED = 0, + COINIT_DISABLE_OLE1DDE = 4, + COINIT_SPEED_OVER_MEMORY = 8, +}; diff --git a/std/os/windows/shell32.zig b/std/os/windows/shell32.zig new file mode 100644 index 0000000000..f10466add3 --- /dev/null +++ b/std/os/windows/shell32.zig @@ -0,0 +1,4 @@ +use @import("index.zig"); + +pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; + diff --git a/std/os/windows/shlwapi.zig b/std/os/windows/shlwapi.zig new file mode 100644 index 0000000000..6bccefaf98 --- /dev/null +++ b/std/os/windows/shlwapi.zig @@ -0,0 +1,4 @@ +use @import("index.zig"); + +pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; + diff --git a/std/os/windows/user32.zig b/std/os/windows/user32.zig new file mode 100644 index 0000000000..37f9f6f3b8 --- /dev/null +++ b/std/os/windows/user32.zig @@ -0,0 +1,4 @@ +use @import("index.zig"); + +pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; + -- cgit v1.2.3 From d767fae47e89aef53505191cf11ce7e592a784b2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 00:35:53 -0400 Subject: self-hosted: add first compare-output test --- src-self-hosted/compilation.zig | 6 +-- src-self-hosted/main.zig | 12 +----- src-self-hosted/test.zig | 91 ++++++++++++++++++++++++++++++++++++----- src/windows_sdk.h | 2 + std/os/test.zig | 6 +-- test/stage2/compare_output.zig | 12 ++++++ 6 files changed, 101 insertions(+), 28 deletions(-) create mode 100644 test/stage2/compare_output.zig (limited to 'src') diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 3adeb55b56..356fecc6da 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -128,7 +128,6 @@ pub const Compilation = struct { version_patch: u32, linker_script: ?[]const u8, - cache_dir: []const u8, out_h_path: ?[]const u8, is_test: bool, @@ -326,7 +325,6 @@ pub const Compilation = struct { build_mode: builtin.Mode, is_static: bool, zig_lib_dir: []const u8, - cache_dir: []const u8, ) !*Compilation { const loop = event_loop_local.loop; const comp = try event_loop_local.loop.allocator.create(Compilation{ @@ -341,7 +339,6 @@ pub const Compilation = struct { .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, .zig_std_dir = undefined, - .cache_dir = cache_dir, .tmp_dir = event.Future(BuildError![]u8).init(loop), .name = undefined, @@ -777,7 +774,8 @@ pub const Compilation = struct { defer decls.base.deref(self); var decl_group = event.Group(BuildError!void).init(self.loop); - errdefer decl_group.cancelAll(); + // TODO https://github.com/ziglang/zig/issues/1261 + //errdefer decl_group.cancelAll(); var it = tree.root_node.decls.iterator(0); while (it.next()) |decl_ptr| { diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 5c27e5f57e..d7e52bc404 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -138,7 +138,6 @@ const usage_build_generic = \\Compile Options: \\ --libc [file] Provide a file which specifies libc paths \\ --assembly [source] Add assembly file to build - \\ --cache-dir [path] Override the cache directory \\ --emit [filetype] Emit a specific file format as compilation output \\ --enable-timing-info Print timing diagnostics \\ --name [name] Override output name @@ -204,7 +203,6 @@ const args_build_generic = []Flag{ }), Flag.ArgMergeN("--assembly", 1), - Flag.Arg1("--cache-dir"), Flag.Option("--emit", []const []const u8{ "asm", "bin", @@ -373,13 +371,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co os.exit(1); } - const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..]; - const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch { - try stderr.print("invalid cache dir: {}\n", rel_cache_dir); - os.exit(1); - }; - defer allocator.free(full_cache_dir); - const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1); defer allocator.free(zig_lib_dir); @@ -401,7 +392,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co build_mode, is_static, zig_lib_dir, - full_cache_dir, ); defer comp.destroy(); @@ -472,7 +462,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.emit_file_type = emit_type; comp.assembly_files = assembly_files; - comp.link_out_file = flags.single("out-file"); + comp.link_out_file = flags.single("output"); comp.link_objects = link_objects; try comp.build(); diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index d3541c73b6..1c76bc9e11 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -8,13 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic; const errmsg = @import("errmsg.zig"); const EventLoopLocal = @import("compilation.zig").EventLoopLocal; -test "compile errors" { - var ctx: TestContext = undefined; +var ctx: TestContext = undefined; + +test "stage2" { try ctx.init(); defer ctx.deinit(); try @import("../test/stage2/compile_errors.zig").addCases(&ctx); - //try @import("../test/stage2/compare_output.zig").addCases(&ctx); + try @import("../test/stage2/compare_output.zig").addCases(&ctx); try ctx.run(); } @@ -26,7 +27,6 @@ pub const TestContext = struct { loop: std.event.Loop, event_loop_local: EventLoopLocal, zig_lib_dir: []u8, - zig_cache_dir: []u8, file_index: std.atomic.Int(usize), group: std.event.Group(error!void), any_err: error!void, @@ -39,7 +39,6 @@ pub const TestContext = struct { .loop = undefined, .event_loop_local = undefined, .zig_lib_dir = undefined, - .zig_cache_dir = undefined, .group = undefined, .file_index = std.atomic.Int(usize).init(0), }; @@ -56,16 +55,12 @@ pub const TestContext = struct { self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); errdefer allocator.free(self.zig_lib_dir); - self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator); - errdefer allocator.free(self.zig_cache_dir); - try std.os.makePath(allocator, tmp_dir_name); errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {}; } fn deinit(self: *TestContext) void { std.os.deleteTree(allocator, tmp_dir_name) catch {}; - allocator.free(self.zig_cache_dir); allocator.free(self.zig_lib_dir); self.event_loop_local.deinit(); self.loop.deinit(); @@ -110,7 +105,6 @@ pub const TestContext = struct { builtin.Mode.Debug, true, // is_static self.zig_lib_dir, - self.zig_cache_dir, ); errdefer comp.destroy(); @@ -119,6 +113,83 @@ pub const TestContext = struct { try self.group.call(getModuleEvent, comp, source, path, line, column, msg); } + fn testCompareOutputLibC( + self: *TestContext, + source: []const u8, + expected_output: []const u8, + ) !void { + var file_index_buf: [20]u8 = undefined; + const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); + const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1); + + const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt()); + if (std.os.path.dirname(file1_path)) |dirname| { + try std.os.makePath(allocator, dirname); + } + + // TODO async I/O + try std.io.writeFile(allocator, file1_path, source); + + var comp = try Compilation.create( + &self.event_loop_local, + "test", + file1_path, + Target.Native, + Compilation.Kind.Exe, + builtin.Mode.Debug, + false, + self.zig_lib_dir, + ); + errdefer comp.destroy(); + + _ = try comp.addLinkLib("c", true); + comp.link_out_file = output_file; + try comp.build(); + + try self.group.call(getModuleEventSuccess, comp, output_file, expected_output); + } + + async fn getModuleEventSuccess( + comp: *Compilation, + exe_file: []const u8, + expected_output: []const u8, + ) !void { + // TODO this should not be necessary + const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file); + + defer comp.destroy(); + const build_event = await (async comp.events.get() catch unreachable); + + switch (build_event) { + Compilation.Event.Ok => { + const argv = []const []const u8{exe_file_2}; + // TODO use event loop + const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024); + switch (child.term) { + std.os.ChildProcess.Term.Exited => |code| { + if (code != 0) { + return error.BadReturnCode; + } + }, + else => { + return error.Crashed; + }, + } + if (!mem.eql(u8, child.stdout, expected_output)) { + return error.OutputMismatch; + } + }, + Compilation.Event.Error => |err| return err, + Compilation.Event.Fail => |msgs| { + var stderr = try std.io.getStdErr(); + try stderr.write("build incorrectly failed:\n"); + for (msgs) |msg| { + try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto); + } + }, + } + } + async fn getModuleEvent( comp: *Compilation, source: []const u8, diff --git a/src/windows_sdk.h b/src/windows_sdk.h index 080ed55bed..2d531ad372 100644 --- a/src/windows_sdk.h +++ b/src/windows_sdk.h @@ -14,6 +14,8 @@ #define ZIG_EXTERN_C #endif +#include + struct ZigWindowsSDK { const char *path10_ptr; size_t path10_len; diff --git a/std/os/test.zig b/std/os/test.zig index 52e6ffdc1c..9e795e8ad2 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" { test "access file" { try os.makePath(a, "os_test_tmp"); - if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| { - unreachable; + if (os.File.access(a, "os_test_tmp/file.txt")) |ok| { + @panic("expected error"); } else |err| { assert(err == error.NotFound); } try io.writeFile(a, "os_test_tmp/file.txt", ""); - assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true); + try os.File.access(a, "os_test_tmp/file.txt"); try os.deleteTree(a, "os_test_tmp"); } diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig new file mode 100644 index 0000000000..35adcbb96b --- /dev/null +++ b/test/stage2/compare_output.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const TestContext = @import("../../src-self-hosted/test.zig").TestContext; + +pub fn addCases(ctx: *TestContext) !void { + try ctx.testCompareOutputLibC( + \\extern fn puts([*]const u8) void; + \\export fn main() c_int { + \\ puts(c"Hello, world!"); + \\ return 0; + \\} + , "Hello, world!" ++ std.cstr.line_sep); +} -- cgit v1.2.3 From 2614ef056a83842ec9501bf2e4f21ae840f981f8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 23 Jul 2018 17:38:03 -0400 Subject: self-hosted: basic linker code for macos --- src-self-hosted/compilation.zig | 1 + src-self-hosted/libc_installation.zig | 6 +- src-self-hosted/link.zig | 243 +++++++++++++++++++++++++++++++++- src-self-hosted/target.zig | 33 +++++ src/link.cpp | 2 +- 5 files changed, 277 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index f1886bd93d..6abb650a62 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -275,6 +275,7 @@ pub const Compilation = struct { LinkFailed, LibCRequiredButNotProvidedOrFound, LibCMissingDynamicLinker, + InvalidDarwinVersionString, }; pub const Event = union(enum) { diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 5a9b7561fa..c9c631a7fb 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -170,7 +170,7 @@ pub const LibCInstallation = struct { try group.call(findNativeDynamicLinker, self, loop); }, builtin.Os.macosx => { - try group.call(findNativeIncludeDirMacOS, self, loop); + self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); }, else => @compileError("unimplemented: find libc for this OS"), } @@ -254,10 +254,6 @@ pub const LibCInstallation = struct { @panic("TODO"); } - async fn findNativeIncludeDirMacOS(self: *LibCInstallation, loop: *event.Loop) !void { - self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); - } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void { // TODO //ZigWindowsSDK *sdk = get_windows_sdk(g); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 16d9939fff..0a83743ef8 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -1,10 +1,12 @@ const std = @import("std"); +const mem = std.mem; const c = @import("c.zig"); const builtin = @import("builtin"); const ObjectFormat = builtin.ObjectFormat; const Compilation = @import("compilation.zig").Compilation; const Target = @import("target.zig").Target; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const assert = std.debug.assert; const Context = struct { comp: *Compilation, @@ -315,8 +317,163 @@ fn constructLinkerArgsCoff(ctx: *Context) void { @panic("TODO"); } -fn constructLinkerArgsMachO(ctx: *Context) void { - @panic("TODO"); +fn constructLinkerArgsMachO(ctx: *Context) !void { + try ctx.args.append(c"-demangle"); + + if (ctx.comp.linker_rdynamic) { + try ctx.args.append(c"-export_dynamic"); + } + + const is_lib = ctx.comp.kind == Compilation.Kind.Lib; + const shared = !ctx.comp.is_static and is_lib; + if (ctx.comp.is_static) { + try ctx.args.append(c"-static"); + } else { + try ctx.args.append(c"-dynamic"); + } + + //if (is_lib) { + // if (!g->is_static) { + // lj->args.append("-dylib"); + + // Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); + // lj->args.append("-compatibility_version"); + // lj->args.append(buf_ptr(compat_vers)); + + // Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, + // g->version_major, g->version_minor, g->version_patch); + // lj->args.append("-current_version"); + // lj->args.append(buf_ptr(cur_vers)); + + // // TODO getting an error when running an executable when doing this rpath thing + // //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // // buf_ptr(g->root_out_name), g->version_major); + // //lj->args.append("-install_name"); + // //lj->args.append(buf_ptr(dylib_install_name)); + + // if (buf_len(&lj->out_file) == 0) { + // buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + // } + // } + //} + + try ctx.args.append(c"-arch"); + const darwin_arch_str = try std.cstr.addNullByte( + &ctx.arena.allocator, + ctx.comp.target.getDarwinArchString(), + ); + try ctx.args.append(darwin_arch_str.ptr); + + const platform = try DarwinPlatform.get(ctx.comp); + switch (platform.kind) { + DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"), + DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"), + DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"), + } + const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro); + try ctx.args.append(ver_str.ptr); + + if (ctx.comp.kind == Compilation.Kind.Exe) { + if (ctx.comp.is_static) { + try ctx.args.append(c"-no_pie"); + } else { + try ctx.args.append(c"-pie"); + } + } + + try ctx.args.append(c"-o"); + try ctx.args.append(ctx.out_file_path.ptr()); + + //for (size_t i = 0; i < g->rpath_list.length; i += 1) { + // Buf *rpath = g->rpath_list.at(i); + // add_rpath(lj, rpath); + //} + //add_rpath(lj, &lj->out_file); + + if (shared) { + try ctx.args.append(c"-headerpad_max_install_names"); + } else if (ctx.comp.is_static) { + try ctx.args.append(c"-lcrt0.o"); + } else { + switch (platform.kind) { + DarwinPlatform.Kind.MacOS => { + if (platform.versionLessThan(10, 5)) { + try ctx.args.append(c"-lcrt1.o"); + } else if (platform.versionLessThan(10, 6)) { + try ctx.args.append(c"-lcrt1.10.5.o"); + } else if (platform.versionLessThan(10, 8)) { + try ctx.args.append(c"-lcrt1.10.6.o"); + } + }, + DarwinPlatform.Kind.IPhoneOS => { + if (ctx.comp.target.getArch() == builtin.Arch.aarch64) { + // iOS does not need any crt1 files for arm64 + } else if (platform.versionLessThan(3, 1)) { + try ctx.args.append(c"-lcrt1.o"); + } else if (platform.versionLessThan(6, 0)) { + try ctx.args.append(c"-lcrt1.3.1.o"); + } + }, + DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed + } + } + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append("-L"); + // lj->args.append(lib_dir); + //} + + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + //// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce + //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) { + // Buf *compiler_rt_o_path = build_compiler_rt(g); + // lj->args.append(buf_ptr(compiler_rt_o_path)); + //} + + if (ctx.comp.target == Target.Native) { + for (ctx.comp.link_libs_list.toSliceConst()) |lib| { + if (mem.eql(u8, lib.name, "c")) { + // on Darwin, libSystem has libc in it, but also you have to use it + // to make syscalls because the syscall numbers are not documented + // and change between versions. + // so we always link against libSystem + try ctx.args.append(c"-lSystem"); + } else { + if (mem.indexOfScalar(u8, lib.name, '/') == null) { + const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name); + try ctx.args.append(arg.ptr); + } else { + const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); + try ctx.args.append(arg.ptr); + } + } + } + } else { + try ctx.args.append(c"-undefined"); + try ctx.args.append(c"dynamic_lookup"); + } + + if (platform.kind == DarwinPlatform.Kind.MacOS) { + if (platform.versionLessThan(10, 5)) { + try ctx.args.append(c"-lgcc_s.10.4"); + } else if (platform.versionLessThan(10, 6)) { + try ctx.args.append(c"-lgcc_s.10.5"); + } + } else { + @panic("TODO"); + } + + //for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { + // lj->args.append("-framework"); + // lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); + //} } fn constructLinkerArgsWasm(ctx: *Context) void { @@ -341,3 +498,85 @@ fn addFnObjects(ctx: *Context) !void { it = node.next; } } + +const DarwinPlatform = struct { + kind: Kind, + major: u32, + minor: u32, + micro: u32, + + const Kind = enum { + MacOS, + IPhoneOS, + IPhoneOSSimulator, + }; + + fn get(comp: *Compilation) !DarwinPlatform { + var result: DarwinPlatform = undefined; + const ver_str = switch (comp.darwin_version_min) { + Compilation.DarwinVersionMin.MacOS => |ver| blk: { + result.kind = Kind.MacOS; + break :blk ver; + }, + Compilation.DarwinVersionMin.Ios => |ver| blk: { + result.kind = Kind.IPhoneOS; + break :blk ver; + }, + Compilation.DarwinVersionMin.None => blk: { + assert(comp.target.getOs() == builtin.Os.macosx); + result.kind = Kind.MacOS; + break :blk "10.10"; + }, + }; + + var had_extra: bool = undefined; + try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); + if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { + return error.InvalidDarwinVersionString; + } + + if (result.kind == Kind.IPhoneOS) { + switch (comp.target.getArch()) { + builtin.Arch.i386, + builtin.Arch.x86_64, + => result.kind = Kind.IPhoneOSSimulator, + else => {}, + } + } + return result; + } + + fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool { + if (self.major < major) + return true; + if (self.major > major) + return false; + if (self.minor < minor) + return true; + return false; + } +}; + +/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the +/// grouped values as integers. Numbers which are not provided are set to 0. +/// return true if the entire string was parsed (9.2), or all groups were +/// parsed (10.3.5extrastuff). +fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void { + major.* = 0; + minor.* = 0; + micro.* = 0; + had_extra.* = false; + + if (str.len == 0) + return error.InvalidDarwinVersionString; + + var start_pos: usize = 0; + for ([]*u32{major, minor, micro}) |v| { + const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); + const end_pos = dot_pos orelse str.len; + v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; + start_pos = (dot_pos orelse return) + 1; + if (start_pos == str.len) return; + } + had_extra.* = true; +} diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index dccf937a47..0cc8d02a62 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -526,4 +526,37 @@ pub const Target = union(enum) { => @panic("TODO specify the C integer type sizes for this OS"), } } + + pub fn getDarwinArchString(self: Target) []const u8 { + const arch = self.getArch(); + switch (arch) { + builtin.Arch.aarch64 => return "arm64", + builtin.Arch.thumb, + builtin.Arch.armv8_3a, + builtin.Arch.armv8_2a, + builtin.Arch.armv8_1a, + builtin.Arch.armv8, + builtin.Arch.armv8r, + builtin.Arch.armv8m_baseline, + builtin.Arch.armv8m_mainline, + builtin.Arch.armv7, + builtin.Arch.armv7em, + builtin.Arch.armv7m, + builtin.Arch.armv7s, + builtin.Arch.armv7k, + builtin.Arch.armv7ve, + builtin.Arch.armv6, + builtin.Arch.armv6m, + builtin.Arch.armv6k, + builtin.Arch.armv6t2, + builtin.Arch.armv5, + builtin.Arch.armv5te, + builtin.Arch.armv4t, + => return "arm", + builtin.Arch.powerpc => return "ppc", + builtin.Arch.powerpc64 => return "ppc64", + builtin.Arch.powerpc64le => return "ppc64le", + else => return @tagName(arch), + } + } }; diff --git a/src/link.cpp b/src/link.cpp index 2d9a79585f..f65c072bac 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) { if (strchr(buf_ptr(link_lib->name), '/') == nullptr) { Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); lj->args.append(buf_ptr(arg)); - } else { + } else { lj->args.append(buf_ptr(link_lib->name)); } } -- cgit v1.2.3 From 72599d420b1bebb37efb2179a91d8256287f7c28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 00:06:34 -0400 Subject: self-hosted: find all libc paths; windows linker code --- src-self-hosted/compilation.zig | 1 + src-self-hosted/libc_installation.zig | 187 +++++++++++++++++++++++----------- src-self-hosted/link.zig | 146 +++++++++++++++++++++++++- src/windows_sdk.cpp | 10 +- std/os/file.zig | 4 +- 5 files changed, 282 insertions(+), 66 deletions(-) (limited to 'src') diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 6abb650a62..093aab21da 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -276,6 +276,7 @@ pub const Compilation = struct { LibCRequiredButNotProvidedOrFound, LibCMissingDynamicLinker, InvalidDarwinVersionString, + UnsupportedLinkArchitecture, }; pub const Event = union(enum) { diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index c9c631a7fb..3938c0d90c 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -20,8 +20,10 @@ pub const LibCInstallation = struct { CCompilerExitCode, CCompilerCrashed, CCompilerCannotFindHeaders, - CCompilerCannotFindCRuntime, + LibCRuntimeNotFound, LibCStdLibHeaderNotFound, + LibCKernel32LibNotFound, + UnsupportedArchitecture, }; pub fn parse( @@ -111,7 +113,7 @@ pub const LibCInstallation = struct { \\lib_dir={} \\ \\# The directory that contains `crtbegin.o`. - \\# On Linux, can be found with `cc -print-file-name=crt1.o`. + \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`. \\# Not needed when targeting MacOS or Windows. \\static_lib_dir={} \\ @@ -142,21 +144,22 @@ pub const LibCInstallation = struct { self.initEmpty(); var group = event.Group(FindError!void).init(loop); errdefer group.cancelAll(); + var windows_sdk: ?*c.ZigWindowsSDK = null; + errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + switch (builtin.os) { builtin.Os.windows => { var sdk: *c.ZigWindowsSDK = undefined; switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) { c.ZigFindWindowsSdkError.None => { - defer c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + windows_sdk = sdk; - errdefer if (self.msvc_lib_dir) |s| loop.allocator.free(s); if (sdk.msvc_lib_dir_ptr) |ptr| { self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); } - //try group.call(findNativeIncludeDirWindows, self, loop); - //try group.call(findNativeLibDirWindows, self, loop); - //try group.call(findNativeMsvcLibDir, self, loop); - //try group.call(findNativeKernel32LibDir, self, loop); + try group.call(findNativeKernel32LibDir, self, loop, sdk); + try group.call(findNativeIncludeDirWindows, self, loop, sdk); + try group.call(findNativeLibDirWindows, self, loop, sdk); }, c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, c.ZigFindWindowsSdkError.NotFound => return error.NotFound, @@ -230,61 +233,64 @@ pub const LibCInstallation = struct { const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h"); defer loop.allocator.free(stdlib_path); - if (std.os.File.access(loop.allocator, stdlib_path)) |_| { + if (try fileExists(loop.allocator, stdlib_path)) { self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path); return; - } else |err| switch (err) { - error.NotFound, error.PermissionDenied => continue, - error.OutOfMemory => return error.OutOfMemory, - else => return error.FileSystem, } } return error.LibCStdLibHeaderNotFound; } - async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop) !void { - // TODO - //ZigWindowsSDK *sdk = get_windows_sdk(g); - //g->libc_include_dir = buf_alloc(); - //if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { - // fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); - // exit(1); - //} - @panic("TODO"); + async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void { + var search_buf: [2]Search = undefined; + const searches = fillSearch(&search_buf, sdk); + + var result_buf = try std.Buffer.initSize(loop.allocator, 0); + defer result_buf.deinit(); + + for (searches) |search| { + result_buf.shrink(0); + const stream = &std.io.BufferOutStream.init(&result_buf).stream; + try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version); + + const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h"); + defer loop.allocator.free(stdlib_path); + + if (try fileExists(loop.allocator, stdlib_path)) { + self.include_dir = result_buf.toOwnedSlice(); + return; + } + } + + return error.LibCStdLibHeaderNotFound; } - async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) FindError!void { - // TODO - //ZigWindowsSDK *sdk = get_windows_sdk(g); - - //if (g->msvc_lib_dir == nullptr) { - // Buf* vc_lib_dir = buf_alloc(); - // if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { - // fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); - // exit(1); - // } - // g->msvc_lib_dir = vc_lib_dir; - //} - - //if (g->libc_lib_dir == nullptr) { - // Buf* ucrt_lib_path = buf_alloc(); - // if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - // fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); - // exit(1); - // } - // g->libc_lib_dir = ucrt_lib_path; - //} - - //if (g->kernel32_lib_dir == nullptr) { - // Buf* kern_lib_path = buf_alloc(); - // if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - // fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); - // exit(1); - // } - // g->kernel32_lib_dir = kern_lib_path; - //} - @panic("TODO"); + async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { + var search_buf: [2]Search = undefined; + const searches = fillSearch(&search_buf, sdk); + + var result_buf = try std.Buffer.initSize(loop.allocator, 0); + defer result_buf.deinit(); + + for (searches) |search| { + result_buf.shrink(0); + const stream = &std.io.BufferOutStream.init(&result_buf).stream; + try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version); + switch (builtin.arch) { + builtin.Arch.i386 => try stream.write("x86"), + builtin.Arch.x86_64 => try stream.write("x64"), + builtin.Arch.aarch64 => try stream.write("arm"), + else => return error.UnsupportedArchitecture, + } + const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib"); + defer loop.allocator.free(ucrt_lib_path); + if (try fileExists(loop.allocator, ucrt_lib_path)) { + self.lib_dir = result_buf.toOwnedSlice(); + return; + } + } + return error.LibCRuntimeNotFound; } async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void { @@ -330,17 +336,37 @@ pub const LibCInstallation = struct { dyn_test.result = result; return; } else |err| switch (err) { - error.CCompilerCannotFindCRuntime => return, + error.LibCRuntimeNotFound => return, else => return err, } } - async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { - @panic("TODO"); - } - async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void { - @panic("TODO"); + async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void { + var search_buf: [2]Search = undefined; + const searches = fillSearch(&search_buf, sdk); + + var result_buf = try std.Buffer.initSize(loop.allocator, 0); + defer result_buf.deinit(); + + for (searches) |search| { + result_buf.shrink(0); + const stream = &std.io.BufferOutStream.init(&result_buf).stream; + try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version); + switch (builtin.arch) { + builtin.Arch.i386 => try stream.write("x86\\"), + builtin.Arch.x86_64 => try stream.write("x64\\"), + builtin.Arch.aarch64 => try stream.write("arm\\"), + else => return error.UnsupportedArchitecture, + } + const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib"); + defer loop.allocator.free(kernel32_path); + if (try fileExists(loop.allocator, kernel32_path)) { + self.kernel32_lib_dir = result_buf.toOwnedSlice(); + return; + } + } + return error.LibCKernel32LibNotFound; } fn initEmpty(self: *LibCInstallation) void { @@ -386,8 +412,8 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo }, } var it = std.mem.split(exec_result.stdout, "\n\r"); - const line = it.next() orelse return error.CCompilerCannotFindCRuntime; - const dirname = std.os.path.dirname(line) orelse return error.CCompilerCannotFindCRuntime; + const line = it.next() orelse return error.LibCRuntimeNotFound; + const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound; if (want_dirname) { return std.mem.dupe(loop.allocator, u8, dirname); @@ -395,3 +421,42 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo return std.mem.dupe(loop.allocator, u8, line); } } + +const Search = struct { + path: []const u8, + version: []const u8, +}; + +fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { + var search_end: usize = 0; + if (sdk.path10_ptr) |path10_ptr| { + if (sdk.version10_ptr) |ver10_ptr| { + search_buf[search_end] = Search{ + .path = path10_ptr[0..sdk.path10_len], + .version = ver10_ptr[0..sdk.version10_len], + }; + search_end += 1; + } + } + if (sdk.path81_ptr) |path81_ptr| { + if (sdk.version81_ptr) |ver81_ptr| { + search_buf[search_end] = Search{ + .path = path81_ptr[0..sdk.path81_len], + .version = ver81_ptr[0..sdk.version81_len], + }; + search_end += 1; + } + } + return search_buf[0..search_end]; +} + + +fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool { + if (std.os.File.access(allocator, path)) |_| { + return true; + } else |err| switch (err) { + error.NotFound, error.PermissionDenied => return false, + error.OutOfMemory => return error.OutOfMemory, + else => return error.FileSystem, + } +} diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 0a83743ef8..b9eefa9d4f 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -313,8 +313,150 @@ fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { try ctx.args.append(full_path_with_null.ptr); } -fn constructLinkerArgsCoff(ctx: *Context) void { - @panic("TODO"); +fn constructLinkerArgsCoff(ctx: *Context) !void { + try ctx.args.append(c"-NOLOGO"); + + if (!ctx.comp.strip) { + try ctx.args.append(c"-DEBUG"); + } + + switch (ctx.comp.target.getArch()) { + builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"), + builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"), + builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"), + else => return error.UnsupportedLinkArchitecture, + } + + if (ctx.comp.windows_subsystem_windows) { + try ctx.args.append(c"/SUBSYSTEM:windows"); + } else if (ctx.comp.windows_subsystem_console) { + try ctx.args.append(c"/SUBSYSTEM:console"); + } + + const is_library = ctx.comp.kind == Compilation.Kind.Lib; + + const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst()); + try ctx.args.append(out_arg.ptr); + + if (ctx.comp.haveLibC()) { + try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr); + try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr); + try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr); + } + + if (ctx.link_in_crt) { + const lib_str = if (ctx.comp.is_static) "lib" else ""; + const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else ""; + + if (ctx.comp.is_static) { + const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str); + try ctx.args.append(cmt_lib_name.ptr); + } else { + const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str); + try ctx.args.append(msvcrt_lib_name.ptr); + } + + const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str); + try ctx.args.append(vcruntime_lib_name.ptr); + + const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str); + try ctx.args.append(crt_lib_name.ptr); + + // Visual C++ 2015 Conformance Changes + // https://msdn.microsoft.com/en-us/library/bb531344.aspx + try ctx.args.append(c"legacy_stdio_definitions.lib"); + + // msvcrt depends on kernel32 + try ctx.args.append(c"kernel32.lib"); + } else { + try ctx.args.append(c"-NODEFAULTLIB"); + if (!is_library) { + try ctx.args.append(c"-ENTRY:WinMainCRTStartup"); + // TODO + //if (g->have_winmain) { + // lj->args.append("-ENTRY:WinMain"); + //} else { + // lj->args.append("-ENTRY:WinMainCRTStartup"); + //} + } + } + + if (is_library and !ctx.comp.is_static) { + try ctx.args.append(c"-DLL"); + } + + //for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + // const char *lib_dir = g->lib_dirs.at(i); + // lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); + //} + + for (ctx.comp.link_objects) |link_object| { + const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); + try ctx.args.append(link_obj_with_null.ptr); + } + try addFnObjects(ctx); + + switch (ctx.comp.kind) { + Compilation.Kind.Exe, Compilation.Kind.Lib => { + if (!ctx.comp.haveLibC()) { + @panic("TODO"); + //Buf *builtin_o_path = build_o(g, "builtin"); + //lj->args.append(buf_ptr(builtin_o_path)); + } + + // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage + // TODO + //Buf *compiler_rt_o_path = build_compiler_rt(g); + //lj->args.append(buf_ptr(compiler_rt_o_path)); + }, + Compilation.Kind.Obj => {}, + } + + //Buf *def_contents = buf_alloc(); + //ZigList gen_lib_args = {0}; + //for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { + // LinkLib *link_lib = g->link_libs_list.at(lib_i); + // if (buf_eql_str(link_lib->name, "c")) { + // continue; + // } + // if (link_lib->provided_explicitly) { + // if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) { + // Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); + // lj->args.append(buf_ptr(arg)); + // } + // else { + // lj->args.append(buf_ptr(link_lib->name)); + // } + // } else { + // buf_resize(def_contents, 0); + // buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); + // for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { + // Buf *symbol_name = link_lib->symbols.at(exp_i); + // buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); + // } + // buf_appendf(def_contents, "\n"); + + // Buf *def_path = buf_alloc(); + // os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path); + // os_write_file(def_path, def_contents); + + // Buf *generated_lib_path = buf_alloc(); + // os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path); + + // gen_lib_args.resize(0); + // gen_lib_args.append("link"); + + // coff_append_machine_arg(g, &gen_lib_args); + // gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); + // gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); + // Buf diag = BUF_INIT; + // if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) { + // fprintf(stderr, "%s\n", buf_ptr(&diag)); + // exit(1); + // } + // lj->args.append(buf_ptr(generated_lib_path)); + // } + //} } fn constructLinkerArgsMachO(ctx: *Context) !void { diff --git a/src/windows_sdk.cpp b/src/windows_sdk.cpp index 059bdee4e9..0f9d0fc301 100644 --- a/src/windows_sdk.cpp +++ b/src/windows_sdk.cpp @@ -287,7 +287,10 @@ ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { } rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len); if (rc == ERROR_SUCCESS) { - priv->base.path10_len = tmp_buf_len; + priv->base.path10_len = tmp_buf_len - 1; + if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') { + priv->base.path10_len -= 1; + } } else { free((void*)priv->base.path10_ptr); priv->base.path10_ptr = nullptr; @@ -302,7 +305,10 @@ ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) { } rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len); if (rc == ERROR_SUCCESS) { - priv->base.path81_len = tmp_buf_len; + priv->base.path81_len = tmp_buf_len - 1; + if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') { + priv->base.path81_len -= 1; + } } else { free((void*)priv->base.path81_ptr); priv->base.path81_ptr = nullptr; diff --git a/std/os/file.zig b/std/os/file.zig index 52bc590f77..6998ba00d1 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -139,7 +139,9 @@ pub const File = struct { const err = windows.GetLastError(); switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.NotFound, + windows.ERROR.FILE_NOT_FOUND, + windows.ERROR.PATH_NOT_FOUND, + => return error.NotFound, windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, else => return os.unexpectedErrorWindows(err), } -- cgit v1.2.3 From 29e19ace362e7a1910b9f105257f2bce2491e32b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 10:13:40 -0400 Subject: fix logic for determining whether param requires comptime closes #778 closes #1213 --- src/analyze.cpp | 14 +++++++++----- test/compile_errors.zig | 11 +++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index 6bbe5f6037..f399ab8305 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1585,10 +1585,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdMetaType: - add_node_error(g, param_node->data.param_decl.type, - buf_sprintf("parameter of type '%s' must be declared comptime", - buf_ptr(&type_entry->name))); - return g->builtin_types.entry_invalid; case TypeTableEntryIdVoid: case TypeTableEntryIdBool: case TypeTableEntryIdInt: @@ -1603,6 +1599,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdPromise: + type_ensure_zero_bits_known(g, type_entry); + if (type_requires_comptime(type_entry)) { + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of type '%s' must be declared comptime", + buf_ptr(&type_entry->name))); + return g->builtin_types.entry_invalid; + } break; } FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; @@ -5019,9 +5022,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { } else { return type_requires_comptime(type_entry->data.pointer.child_type); } + case TypeTableEntryIdFn: + return type_entry->data.fn.is_generic; case TypeTableEntryIdEnum: case TypeTableEntryIdErrorSet: - case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: case TypeTableEntryIdFloat: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d5582b1584..b7bd39f29e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,17 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "generic fn as parameter without comptime keyword", + \\fn f(_: fn (var) void) void {} + \\fn g(_: var) void {} + \\export fn entry() void { + \\ f(g); + \\} + , + ".tmp_source.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime", + ); + cases.add( "optional pointer to void in extern struct", \\comptime { -- cgit v1.2.3 From 2ea08561cf69dabc99722ffc24cb0e4327605506 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jul 2018 14:20:49 -0400 Subject: self-hosted: function types use table lookup --- src-self-hosted/codegen.zig | 3 +- src-self-hosted/compilation.zig | 69 +++++++- src-self-hosted/ir.zig | 8 +- src-self-hosted/type.zig | 338 +++++++++++++++++++++++++++++++++------- src/analyze.cpp | 8 +- 5 files changed, 356 insertions(+), 70 deletions(-) (limited to 'src') diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index ad3dce061e..88293c845e 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -168,6 +168,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) //} const fn_type = fn_val.base.typ.cast(Type.Fn).?; + const fn_type_normal = &fn_type.key.data.Normal; try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); //add_uwtable_attr(g, fn_table_entry->llvm_value); @@ -209,7 +210,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); //} - const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; + const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; // build all basic blocks for (code.basic_block_list.toSlice()) |bb| { diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 093aab21da..8d41e2439b 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -220,12 +220,14 @@ pub const Compilation = struct { int_type_table: event.Locked(IntTypeTable), array_type_table: event.Locked(ArrayTypeTable), ptr_type_table: event.Locked(PtrTypeTable), + fn_type_table: event.Locked(FnTypeTable), c_int_types: [CInt.list.len]*Type.Int, const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); + const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql); const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); const CompileErrList = std.ArrayList(*Msg); @@ -384,6 +386,7 @@ pub const Compilation = struct { .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)), .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)), + .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)), .c_int_types = undefined, .meta_type = undefined, @@ -414,6 +417,7 @@ pub const Compilation = struct { comp.int_type_table.private_data.deinit(); comp.array_type_table.private_data.deinit(); comp.ptr_type_table.private_data.deinit(); + comp.fn_type_table.private_data.deinit(); comp.arena_allocator.deinit(); comp.loop.allocator.destroy(comp); } @@ -1160,10 +1164,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; symbol_name_consumed = true; + // Define local parameter variables + //for (size_t i = 0; i < fn_type_id->param_count; i += 1) { + // FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; + // AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); + // Buf *param_name; + // bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args; + // if (param_decl_node && !is_var_args) { + // param_name = param_decl_node->data.param_decl.name; + // } else { + // param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i); + // } + // if (param_name == nullptr) { + // continue; + // } + + // TypeTableEntry *param_type = param_info->type; + // bool is_noalias = param_info->is_noalias; + + // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { + // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); + // } + + // VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, + // param_name, true, create_const_runtime(param_type), nullptr); + // var->src_arg_index = i; + // fn_table_entry->child_scope = var->child_scope; + // var->shadowable = var->shadowable || is_var_args; + + // if (type_has_bits(param_type)) { + // fn_table_entry->variable_list.append(var); + // } + + // if (fn_type->data.fn.gen_param_info) { + // var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; + // } + //} + const analyzed_code = try await (async comp.genAndAnalyzeCode( &fndef_scope.base, body_node, - fn_type.return_type, + fn_type.key.data.Normal.return_type, ) catch unreachable); errdefer analyzed_code.destroy(comp.gpa()); @@ -1199,14 +1240,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn var params = ArrayList(Type.Fn.Param).init(comp.gpa()); var params_consumed = false; - defer if (params_consumed) { + defer if (!params_consumed) { for (params.toSliceConst()) |param| { param.typ.base.deref(comp); } params.deinit(); }; - const is_var_args = false; { var it = fn_proto.params.iterator(0); while (it.next()) |param_node_ptr| { @@ -1219,8 +1259,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn }); } } - const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args); + + const key = Type.Fn.Key{ + .alignment = null, + .data = Type.Fn.Key.Data{ + .Normal = Type.Fn.Normal{ + .return_type = return_type, + .params = params.toOwnedSlice(), + .is_var_args = false, // TODO + .cc = Type.Fn.CallingConvention.Auto, // TODO + }, + }, + }; params_consumed = true; + var key_consumed = false; + defer if (!key_consumed) { + for (key.data.Normal.params) |param| { + param.typ.base.deref(comp); + } + comp.gpa().free(key.data.Normal.params); + }; + + const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable); + key_consumed = true; errdefer fn_type.base.base.deref(comp); return fn_type; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c34f06753d..45355bbf2c 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -281,11 +281,13 @@ pub const Inst = struct { return error.SemanticAnalysisFailed; }; - if (fn_type.params.len != self.params.args.len) { + const fn_type_param_count = fn_type.paramCount(); + + if (fn_type_param_count != self.params.args.len) { try ira.addCompileError( self.base.span, "expected {} arguments, found {}", - fn_type.params.len, + fn_type_param_count, self.params.args.len, ); return error.SemanticAnalysisFailed; @@ -299,7 +301,7 @@ pub const Inst = struct { .fn_ref = fn_ref, .args = args, }); - new_inst.val = IrVal{ .KnownType = fn_type.return_type }; + new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type }; return new_inst; } diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 217c1d50a7..3b57260447 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -221,57 +221,267 @@ pub const Type = struct { pub const Fn = struct { base: Type, - return_type: *Type, - params: []Param, - is_var_args: bool, + key: Key, + garbage_node: std.atomic.Stack(*Fn).Node, + + pub const Key = struct { + data: Data, + alignment: ?u32, + + pub const Data = union(enum) { + Generic: Generic, + Normal: Normal, + }; + + pub fn hash(self: *const Key) u32 { + var result: u32 = 0; + result +%= hashAny(self.alignment, 0); + switch (self.data) { + Data.Generic => |generic| { + result +%= hashAny(generic.param_count, 1); + switch (generic.cc) { + CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2), + else => result +%= hashAny(CallingConvention(generic.cc), 3), + } + }, + Data.Normal => |normal| { + result +%= hashAny(normal.return_type, 4); + result +%= hashAny(normal.is_var_args, 5); + result +%= hashAny(normal.cc, 6); + for (normal.params) |param| { + result +%= hashAny(param.is_noalias, 7); + result +%= hashAny(param.typ, 8); + } + }, + } + return result; + } + + pub fn eql(self: *const Key, other: *const Key) bool { + if ((self.alignment == null) != (other.alignment == null)) return false; + if (self.alignment) |self_align| { + if (self_align != other.alignment.?) return false; + } + if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false; + switch (self.data) { + Data.Generic => |*self_generic| { + const other_generic = &other.data.Generic; + if (self_generic.param_count != other_generic.param_count) return false; + if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false; + switch (self_generic.cc) { + CallingConvention.Async => |self_allocator_type| { + const other_allocator_type = other_generic.cc.Async; + if (self_allocator_type != other_allocator_type) return false; + }, + else => {}, + } + }, + Data.Normal => |*self_normal| { + const other_normal = &other.data.Normal; + if (self_normal.cc != other_normal.cc) return false; + if (self_normal.is_var_args != other_normal.is_var_args) return false; + if (self_normal.return_type != other_normal.return_type) return false; + for (self_normal.params) |*self_param, i| { + const other_param = &other_normal.params[i]; + if (self_param.is_noalias != other_param.is_noalias) return false; + if (self_param.typ != other_param.typ) return false; + } + }, + } + return true; + } + + pub fn deref(key: Key, comp: *Compilation) void { + switch (key.data) { + Key.Data.Generic => |generic| { + switch (generic.cc) { + CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp), + else => {}, + } + }, + Key.Data.Normal => |normal| { + normal.return_type.base.deref(comp); + for (normal.params) |param| { + param.typ.base.deref(comp); + } + }, + } + } + + pub fn ref(key: Key) void { + switch (key.data) { + Key.Data.Generic => |generic| { + switch (generic.cc) { + CallingConvention.Async => |allocator_type| allocator_type.base.ref(), + else => {}, + } + }, + Key.Data.Normal => |normal| { + normal.return_type.base.ref(); + for (normal.params) |param| { + param.typ.base.ref(); + } + }, + } + } + }; + + pub const Normal = struct { + params: []Param, + return_type: *Type, + is_var_args: bool, + cc: CallingConvention, + }; + + pub const Generic = struct { + param_count: usize, + cc: CC, + + pub const CC = union(CallingConvention) { + Auto, + C, + Cold, + Naked, + Stdcall, + Async: *Type, // allocator type + }; + }; + + pub const CallingConvention = enum { + Auto, + C, + Cold, + Naked, + Stdcall, + Async, + }; pub const Param = struct { is_noalias: bool, typ: *Type, }; - pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { - const result = try comp.gpa().create(Fn{ + fn ccFnTypeStr(cc: CallingConvention) []const u8 { + return switch (cc) { + CallingConvention.Auto => "", + CallingConvention.C => "extern ", + CallingConvention.Cold => "coldcc ", + CallingConvention.Naked => "nakedcc ", + CallingConvention.Stdcall => "stdcallcc ", + CallingConvention.Async => unreachable, + }; + } + + pub fn paramCount(self: *Fn) usize { + return switch (self.key.data) { + Key.Data.Generic => |generic| generic.param_count, + Key.Data.Normal => |normal| normal.params.len, + }; + } + + /// takes ownership of key.Normal.params on success + pub async fn get(comp: *Compilation, key: Key) !*Fn { + { + const held = await (async comp.fn_type_table.acquire() catch unreachable); + defer held.release(); + + if (held.value.get(&key)) |entry| { + entry.value.base.base.ref(); + return entry.value; + } + } + + key.ref(); + errdefer key.deref(comp); + + const self = try comp.gpa().create(Fn{ .base = undefined, - .return_type = return_type, - .params = params, - .is_var_args = is_var_args, + .key = key, + .garbage_node = undefined, }); - errdefer comp.gpa().destroy(result); + errdefer comp.gpa().destroy(self); - result.base.init(comp, Id.Fn, "TODO fn type name"); + var name_buf = try std.Buffer.initSize(comp.gpa(), 0); + defer name_buf.deinit(); + + const name_stream = &std.io.BufferOutStream.init(&name_buf).stream; + + switch (key.data) { + Key.Data.Generic => |generic| { + switch (generic.cc) { + CallingConvention.Async => |async_allocator_type| { + try name_stream.print("async<{}> ", async_allocator_type.name); + }, + else => { + const cc_str = ccFnTypeStr(generic.cc); + try name_stream.write(cc_str); + }, + } + try name_stream.write("fn("); + var param_i: usize = 0; + while (param_i < generic.param_count) : (param_i += 1) { + const arg = if (param_i == 0) "var" else ", var"; + try name_stream.write(arg); + } + try name_stream.write(")"); + if (key.alignment) |alignment| { + try name_stream.print(" align<{}>", alignment); + } + try name_stream.write(" var"); + }, + Key.Data.Normal => |normal| { + const cc_str = ccFnTypeStr(normal.cc); + try name_stream.print("{}fn(", cc_str); + for (normal.params) |param, i| { + if (i != 0) try name_stream.write(", "); + if (param.is_noalias) try name_stream.write("noalias "); + try name_stream.write(param.typ.name); + } + if (normal.is_var_args) { + if (normal.params.len != 0) try name_stream.write(", "); + try name_stream.write("..."); + } + try name_stream.write(")"); + if (key.alignment) |alignment| { + try name_stream.print(" align<{}>", alignment); + } + try name_stream.print(" {}", normal.return_type.name); + }, + } + + self.base.init(comp, Id.Fn, name_buf.toOwnedSlice()); - result.return_type.base.ref(); - for (result.params) |param| { - param.typ.base.ref(); + { + const held = await (async comp.fn_type_table.acquire() catch unreachable); + defer held.release(); + + _ = try held.value.put(&self.key, self); } - return result; + return self; } pub fn destroy(self: *Fn, comp: *Compilation) void { - self.return_type.base.deref(comp); - for (self.params) |param| { - param.typ.base.deref(comp); - } + self.key.deref(comp); comp.gpa().destroy(self); } pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { - const llvm_return_type = switch (self.return_type.id) { + const normal = &self.key.data.Normal; + const llvm_return_type = switch (normal.return_type.id) { Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, - else => try self.return_type.getLlvmType(allocator, llvm_context), + else => try normal.return_type.getLlvmType(allocator, llvm_context), }; - const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len); + const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len); defer allocator.free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { - llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context); + llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context); } return llvm.FunctionType( llvm_return_type, llvm_param_types.ptr, @intCast(c_uint, llvm_param_types.len), - @boolToInt(self.is_var_args), + @boolToInt(normal.is_var_args), ) orelse error.OutOfMemory; } }; @@ -347,8 +557,10 @@ pub const Type = struct { is_signed: bool, pub fn hash(self: *const Key) u32 { - const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 }; - return rands[@boolToInt(self.is_signed)] *% self.bit_count; + var result: u32 = 0; + result +%= hashAny(self.is_signed, 0); + result +%= hashAny(self.bit_count, 1); + return result; } pub fn eql(self: *const Key, other: *const Key) bool { @@ -443,15 +655,16 @@ pub const Type = struct { alignment: Align, pub fn hash(self: *const Key) u32 { - const align_hash = switch (self.alignment) { + var result: u32 = 0; + result +%= switch (self.alignment) { Align.Abi => 0xf201c090, - Align.Override => |x| x, + Align.Override => |x| hashAny(x, 0), }; - return hash_usize(@ptrToInt(self.child_type)) *% - hash_enum(self.mut) *% - hash_enum(self.vol) *% - hash_enum(self.size) *% - align_hash; + result +%= hashAny(self.child_type, 1); + result +%= hashAny(self.mut, 2); + result +%= hashAny(self.vol, 3); + result +%= hashAny(self.size, 4); + return result; } pub fn eql(self: *const Key, other: *const Key) bool { @@ -605,7 +818,10 @@ pub const Type = struct { len: usize, pub fn hash(self: *const Key) u32 { - return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len); + var result: u32 = 0; + result +%= hashAny(self.elem_type, 0); + result +%= hashAny(self.len, 1); + return result; } pub fn eql(self: *const Key, other: *const Key) bool { @@ -818,27 +1034,37 @@ pub const Type = struct { }; }; -fn hash_usize(x: usize) u32 { - return switch (@sizeOf(usize)) { - 4 => x, - 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d), - else => @compileError("implement this hash function"), - }; -} - -fn hash_enum(x: var) u32 { - const rands = []u32{ - 0x85ebf64f, - 0x3fcb3211, - 0x240a4e8e, - 0x40bb0e3c, - 0x78be45af, - 0x1ca98e37, - 0xec56053a, - 0x906adc48, - 0xd4fe9763, - 0x54c80dac, - }; - comptime assert(@memberCount(@typeOf(x)) < rands.len); - return rands[@enumToInt(x)]; +fn hashAny(x: var, comptime seed: u64) u32 { + switch (@typeInfo(@typeOf(x))) { + builtin.TypeId.Int => |info| { + comptime var rng = comptime std.rand.DefaultPrng.init(seed); + const unsigned_x = @bitCast(@IntType(false, info.bits), x); + if (info.bits <= 32) { + return u32(unsigned_x) *% comptime rng.random.scalar(u32); + } else { + return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x))); + } + }, + builtin.TypeId.Pointer => |info| { + switch (info.size) { + builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed), + builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"), + builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"), + } + }, + builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed), + builtin.TypeId.Bool => { + comptime var rng = comptime std.rand.DefaultPrng.init(seed); + const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) }; + return vals[@boolToInt(x)]; + }, + builtin.TypeId.Optional => { + if (x) |non_opt| { + return hashAny(non_opt, seed); + } else { + return hashAny(u32(1), seed); + } + }, + else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))), + } } diff --git a/src/analyze.cpp b/src/analyze.cpp index f399ab8305..a4bfff78c3 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3941,7 +3941,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) { return nullptr; } -static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) { +static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; @@ -3979,10 +3979,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr if (fn_type->data.fn.gen_param_info) { var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; } - - if (arg_vars) { - arg_vars[i] = var; - } } } @@ -4082,7 +4078,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { if (!fn_table_entry->child_scope) fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; - define_local_param_variables(g, fn_table_entry, nullptr); + define_local_param_variables(g, fn_table_entry); TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); -- cgit v1.2.3 From 2257660916a8c92d953a5a71da6c2d4f7cc031e6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 13:12:03 -0400 Subject: fix assertion failure when some compile errors happen I don't actually know of a test case to trigger this self-hosted won't have this problem because get_pointer_to_type will return error.SemanticAnalysisFailed --- src/ir.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index fe5fb77085..fd2558c5eb 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18991,6 +18991,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdErrorUnion) { TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) { + return ira->codegen->builtin_types.entry_invalid; + } TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, -- cgit v1.2.3 From 84195467ad974f9b7201e4e1bbd6dccbd5e7ab90 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 17:08:55 -0400 Subject: add compile error for non-inline for loop on comptime type --- src/ir.cpp | 2 +- test/compile_errors.zig | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index fd2558c5eb..424987823b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12142,7 +12142,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc result_type = ira->codegen->builtin_types.entry_invalid; } else if (type_requires_comptime(result_type)) { var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { + if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, buf_sprintf("variable of type '%s' must be const or comptime", buf_ptr(&result_type->name))); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b7bd39f29e..91693e091c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,20 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "non-inline for loop on a type that requires comptime", + \\const Foo = struct { + \\ name: []const u8, + \\ T: type, + \\}; + \\export fn entry() void { + \\ const xx: [2]Foo = undefined; + \\ for (xx) |f| {} + \\} + , + ".tmp_source.zig:7:15: error: variable of type 'Foo' must be const or comptime", + ); + cases.add( "generic fn as parameter without comptime keyword", \\fn f(_: fn (var) void) void {} -- cgit v1.2.3 From fd575fe1f3b45806f2cf823a2abe3727d381d4ed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 25 Jul 2018 18:15:55 -0400 Subject: add compile error for missing parameter name of generic function --- src/ir.cpp | 1 + test/compile_errors.zig | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 424987823b..e40c129953 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12591,6 +12591,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } Buf *param_name = param_decl_node->data.param_decl.name; + if (!param_name) return false; if (!is_var_args) { VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, *child_scope, param_name, true, arg_val, nullptr); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 91693e091c..83bf715f78 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,17 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "missing parameter name of generic function", + \\fn dump(var) void {} + \\export fn entry() void { + \\ var a: u8 = 9; + \\ dump(a); + \\} + , + ".tmp_source.zig:1:9: error: missing parameter name", + ); + cases.add( "non-inline for loop on a type that requires comptime", \\const Foo = struct { -- cgit v1.2.3 From 2cbad364c1d23b64ae064f8547590c133b4f070a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 Jul 2018 18:29:07 -0400 Subject: add compile error for ignoring return value of while loop bodies closes #1049 --- src/analyze.cpp | 2 +- src/ir.cpp | 12 +++++++++--- src/ir_print.cpp | 4 ++++ test/compile_errors.zig | 22 ++++++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/analyze.cpp b/src/analyze.cpp index a4bfff78c3..aadee29fc8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4056,7 +4056,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ } if (g->verbose_ir) { - fprintf(stderr, "{ // (analyzed)\n"); + fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name)); ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4); fprintf(stderr, "}\n"); } diff --git a/src/ir.cpp b/src/ir.cpp index e40c129953..a6007852e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5251,8 +5251,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (body_result == irb->codegen->invalid_instruction) return body_result; - if (!instr_is_unreachable(body_result)) + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime)); + } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); @@ -5331,8 +5333,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (body_result == irb->codegen->invalid_instruction) return body_result; - if (!instr_is_unreachable(body_result)) + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); + } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); @@ -5392,8 +5396,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (body_result == irb->codegen->invalid_instruction) return body_result; - if (!instr_is_unreachable(body_result)) + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result)); ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime)); + } if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 6182958d0a..127afa94a5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { } static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) { + if (instruction == nullptr) { + fprintf(irp->f, "(null)"); + return; + } if (instruction->value.special != ConstValSpecialRuntime) { ir_print_const_value(irp, &instruction->value); } else { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 83bf715f78..2c4c9208eb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,28 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "while loop body expression ignored", + \\fn returns() usize { + \\ return 2; + \\} + \\export fn f1() void { + \\ while (true) returns(); + \\} + \\export fn f2() void { + \\ var x: ?i32 = null; + \\ while (x) |_| returns(); + \\} + \\export fn f3() void { + \\ var x: error!i32 = error.Bad; + \\ while (x) |_| returns() else |_| unreachable; + \\} + , + ".tmp_source.zig:5:25: error: expression value is ignored", + ".tmp_source.zig:9:26: error: expression value is ignored", + ".tmp_source.zig:13:26: error: expression value is ignored", + ); + cases.add( "missing parameter name of generic function", \\fn dump(var) void {} -- cgit v1.2.3 From b3f4182ca1756ccf84fe5bbc88594a91ead617b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 Jul 2018 22:26:00 -0400 Subject: coroutines have 3 more bits of atomic state --- src/all_types.hpp | 2 +- src/analyze.cpp | 13 ++++++--- src/ir.cpp | 80 ++++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 66 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index bcd6a04cc3..70ea629c59 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3245,7 +3245,7 @@ static const size_t stack_trace_ptr_count = 30; #define RESULT_FIELD_NAME "result" #define ASYNC_ALLOC_FIELD_NAME "allocFn" #define ASYNC_FREE_FIELD_NAME "freeFn" -#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle" +#define ATOMIC_STATE_FIELD_NAME "atomic_state" // these point to data belonging to the awaiter #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" #define RESULT_PTR_FIELD_NAME "result_ptr" diff --git a/src/analyze.cpp b/src/analyze.cpp index aadee29fc8..74d59f966a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -519,11 +519,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) return return_type->promise_frame_parent; } - TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise); + TypeTableEntry *atomic_state_type = g->builtin_types.entry_usize; TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); ZigList field_names = {}; - field_names.append(AWAITER_HANDLE_FIELD_NAME); + field_names.append(ATOMIC_STATE_FIELD_NAME); field_names.append(RESULT_FIELD_NAME); field_names.append(RESULT_PTR_FIELD_NAME); if (g->have_err_ret_tracing) { @@ -533,7 +533,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) } ZigList field_types = {}; - field_types.append(awaiter_handle_type); + field_types.append(atomic_state_type); field_types.append(return_type); field_types.append(result_ptr_type); if (g->have_err_ret_tracing) { @@ -6228,7 +6228,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { } else if (type_entry->id == TypeTableEntryIdOpaque) { return 1; } else { - return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); + uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); + // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw + if (type_entry->id == TypeTableEntryIdPromise && llvm_alignment < 8) { + return 8; + } + return llvm_alignment; } } diff --git a/src/ir.cpp b/src/ir.cpp index a6007852e0..5466e64e55 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3097,19 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return return_inst; } + IrBasicBlock *canceled_block = ir_create_basic_block(irb, scope, "Canceled"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended"); + IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended"); + ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); - IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, - get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig - IrInstruction *replacement_value = irb->exec->coro_handle; - IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node, - promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, - AtomicRmwOp_xchg, AtomicOrderSeqCst); - ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle); - IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *replacement_value = ir_build_const_usize(irb, scope, node, 0xa); // 0b1010 + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); - return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final, - is_comptime); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, canceled_block, not_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, canceled_block); + ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, inverted_ptr_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, suspended_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); + // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here + IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); + ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle); + IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, + irb->exec->coro_early_final, is_comptime); // the above blocks are rendered by ir_gen after the rest of codegen } @@ -6708,9 +6736,9 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } - Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); - IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, - awaiter_handle_field_name); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, + atomic_state_field_name); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr, @@ -6723,12 +6751,16 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var); ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); - IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, - get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node, - promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr, - AtomicRmwOp_xchg, AtomicOrderSeqCst); - IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle); + IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, parent_scope, node, irb->exec->coro_handle); + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, + usize_type_val, atomic_state_ptr, nullptr, coro_handle_addr, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_non_null = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend"); IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend"); @@ -7087,10 +7119,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); - Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - awaiter_handle_field_name); - ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value); + atomic_state_field_name); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); @@ -7108,7 +7141,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // coordinate with builtin.zig Buf *index_name = buf_create_from_str("index"); IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, index_ptr, zero); Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); -- cgit v1.2.3 From 7113f109a4111acadf0533ca84e529d229892c8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 15:50:26 -0400 Subject: update coroutine return codegen with new status bits --- src/all_types.hpp | 2 +- src/ir.cpp | 49 ++++++++++++++++++++++++------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 70ea629c59..3ac7afe474 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -60,7 +60,7 @@ struct IrExecutable { ZigList tld_list; IrInstruction *coro_handle; - IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise + IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise IrInstruction *coro_result_ptr_field_ptr; IrInstruction *coro_result_field_ptr; IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise diff --git a/src/ir.cpp b/src/ir.cpp index 5466e64e55..8ebac847ac 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3097,31 +3097,26 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return return_inst; } - IrBasicBlock *canceled_block = ir_create_basic_block(irb, scope, "Canceled"); - IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended"); + IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter"); + IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled"); + + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 + IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *replacement_value = ir_build_const_usize(irb, scope, node, 0xa); // 0b1010 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, - usize_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); - IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 - IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); - IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, canceled_block, not_canceled_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, canceled_block); - ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); - IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 - IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, inverted_ptr_mask, false); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); @@ -3129,16 +3124,20 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); - IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); - IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here + IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle); - IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, - irb->exec->coro_early_final, is_comptime); - // the above blocks are rendered by ir_gen after the rest of codegen + ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, check_canceled_block); + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime); } static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { @@ -7120,10 +7119,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); - irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, zero); + ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero); Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); -- cgit v1.2.3 From 10764ee0e66e5d9a815073340d8f16a58e225422 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:00:41 -0400 Subject: resume clears suspend bit --- src/ir.cpp | 137 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 52 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 8ebac847ac..2eff986694 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6686,20 +6686,53 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode return ir_build_cancel(irb, parent_scope, node, target_inst); } -static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); - IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, parent_scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - return ir_build_coro_resume(irb, parent_scope, node, target_inst); + IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + + IrInstruction *inverted_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 + IrInstruction *mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_mask); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, + get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); + + // TODO relies on Zig not re-ordering fields + IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + atomic_state_field_name); + + // clear the is_suspended bit + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, atomic_state_ptr, nullptr, mask, nullptr, + AtomicRmwOp_and, AtomicOrderSeqCst); + + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + ir_build_coro_resume(irb, scope, node, target_inst); + ir_build_br(irb, scope, node, done_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, done_block); + return ir_build_const_void(irb, scope, node); } -static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); - IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -6713,7 +6746,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast return irb->codegen->invalid_instruction; } - ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); + ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); if (scope_defer_expr) { if (!scope_defer_expr->reported_err) { add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression")); @@ -6724,85 +6757,85 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast Scope *outer_scope = irb->exec->begin_scope; - IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, parent_scope, node, target_inst); + IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name); + IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); if (irb->codegen->have_err_ret_tracing) { - IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull); + IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); - IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); - ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); + IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); + ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); - IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); - IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); - VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr, + IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); + VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); - IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node); - IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst); - IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type); - ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type); - ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value); - IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var); - ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr); - IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle); - IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, parent_scope, node, irb->exec->coro_handle); - IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, + IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); + IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); + IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); + ir_build_await_bookkeeping(irb, scope, node, promise_result_type); + ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); + IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); + ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); + IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, coro_handle_addr, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); - IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 - IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 - IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); - IrInstruction *is_non_null = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); - IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend"); - IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend"); - ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); + IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); + IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); + ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); - IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name); - IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull); - ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); + IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name); + IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); + ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr); } Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); + IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, // because we're about to destroy the memory. So we store it into our result variable. - IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); - ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result); - ir_build_cancel(irb, parent_scope, node, target_inst); - ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); + IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr); + ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result); + ir_build_cancel(irb, scope, node, target_inst); + ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); - IrInstruction *suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false); - IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); - IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); + IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); + IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); + IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrInstructionSwitchBrCase *cases = allocate(2); - cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0); + cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; - cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1); + cases[1].value = ir_build_const_u8(irb, scope, node, 1); cases[1].block = cleanup_block; - ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, + ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); - ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); - ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_gen_defers_for_block(irb, scope, outer_scope, true); + ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); - ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); + ir_build_br(irb, scope, node, merge_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, merge_block); - return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); + return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); } static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { -- cgit v1.2.3 From 442e244b4dd371d674436a163d62efdcd4e17a00 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:16:00 -0400 Subject: suspend sets suspend bit --- src/ir.cpp | 19 ++++++++++++++++++- test/cases/coroutines.zig | 4 ++-- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 2eff986694..cd791fb189 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6874,9 +6874,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); + IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); + IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); - IrInstruction *suspend_code; IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); + IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 + IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); + + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, suspended_block); + ir_build_unreachable(irb, parent_scope, node); + + ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); + IrInstruction *suspend_code; if (node->data.suspend.block == nullptr) { suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); } else { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index f7f2af62a6..72a4ed0b38 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -244,8 +244,8 @@ test "break from suspend" { std.debug.assert(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { - s: suspend |p| { - break :s; + suspend |p| { + resume p; } my_result.* += 1; suspend; -- cgit v1.2.3 From 02c5bda704d30e95e6af23804f9a552e9d8ca2d7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:27:03 -0400 Subject: remove ability to break from suspend blocks closes #803 --- doc/langref.html.in | 2 +- src/all_types.hpp | 2 -- src/analyze.cpp | 1 - src/ir.cpp | 17 ++--------------- src/parser.cpp | 25 ++----------------------- 5 files changed, 5 insertions(+), 42 deletions(-) (limited to 'src') diff --git a/doc/langref.html.in b/doc/langref.html.in index 60ba09d391..d91fb6e8fb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7336,7 +7336,7 @@ Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) diff --git a/src/all_types.hpp b/src/all_types.hpp index 3ac7afe474..2f09e70301 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -898,7 +898,6 @@ struct AstNodeAwaitExpr { }; struct AstNodeSuspend { - Buf *name; AstNode *block; AstNode *promise_symbol; }; @@ -1929,7 +1928,6 @@ struct ScopeLoop { struct ScopeSuspend { Scope base; - Buf *name; IrBasicBlock *resume_block; bool reported_err; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 74d59f966a..03cfa5b67b 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeSuspend); ScopeSuspend *scope = allocate(1); init_scope(&scope->base, ScopeIdSuspend, node, parent); - scope->name = node->data.suspend.name; return scope; } diff --git a/src/ir.cpp b/src/ir.cpp index cd791fb189..799d7e3bc5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6186,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop return ir_build_br(irb, break_scope, node, dest_block, is_comptime); } -static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) { - IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false); - - IrBasicBlock *dest_block = suspend_scope->resume_block; - ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false); - - return ir_build_br(irb, break_scope, node, dest_block, is_comptime); -} - static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { assert(node->type == NodeTypeBreak); @@ -6235,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); } } else if (search_scope->id == ScopeIdSuspend) { - ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope; - if (node->data.break_expr.name != nullptr && - (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name))) - { - return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope); - } + add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block")); + return irb->codegen->invalid_instruction; } search_scope = search_scope->parent; } diff --git a/src/parser.cpp b/src/parser.cpp index adb1633f5d..a93d8de830 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; - Token *name_token = nullptr; - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdSymbol) { - *token_index += 1; - Token *colon_token = &pc->tokens->at(*token_index); - if (colon_token->id == TokenIdColon) { - *token_index += 1; - name_token = token; - token = &pc->tokens->at(*token_index); - } else if (mandatory) { - ast_expect_token(pc, colon_token, TokenIdColon); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } - } - - Token *suspend_token = token; + Token *suspend_token = &pc->tokens->at(*token_index); if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; } else if (mandatory) { @@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b } AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - if (name_token != nullptr) { - node->data.suspend.name = token_buf(name_token); - } node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); -- cgit v1.2.3 From 0b7a9c072203c2f9999ddcc2231a42334cd028e3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:42:09 -0400 Subject: cancel sets the cancel bit --- src/ir.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 799d7e3bc5..4a381a26fa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6663,14 +6663,45 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo async_allocator_type_value, is_var_args); } -static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeCancel); - IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope); + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - return ir_build_cancel(irb, parent_scope, node, target_inst); + IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, + get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); + + // TODO relies on Zig not re-ordering fields + IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); + IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, + atomic_state_field_name); + + // set the is_canceled bit + IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr, + AtomicRmwOp_and, AtomicOrderSeqCst); + + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + ir_build_cancel(irb, scope, node, target_inst); + ir_build_br(irb, scope, node, done_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, done_block); + return ir_build_const_void(irb, scope, node); } static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { -- cgit v1.2.3 From 341bd0dfa42a729aefff56e48a67a21cb3ea0822 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 17:47:15 -0400 Subject: await sets the await bit --- src/ir.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 4a381a26fa..a933106884 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6791,9 +6791,15 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n atomic_state_field_name); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); + IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); + IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 + VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); - IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); @@ -6801,14 +6807,12 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); - IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle); + IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false); IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, - usize_type_val, atomic_state_ptr, nullptr, coro_handle_addr, nullptr, + usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); - IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 - IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); -- cgit v1.2.3 From e491c381896b6f36366bf554f149f79d5be8f9dd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 18:01:26 -0400 Subject: resume detects resuming when not suspended --- src/ir.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index a933106884..bc3ef11481 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6713,13 +6713,15 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended"); + IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended"); - IrInstruction *inverted_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 - IrInstruction *mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_mask); + IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 + IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask); IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); - IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); @@ -6732,7 +6734,7 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) // clear the is_suspended bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, - usize_type_val, atomic_state_ptr, nullptr, mask, nullptr, + usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr, AtomicRmwOp_and, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); @@ -6740,6 +6742,14 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, suspended_block); ir_build_coro_resume(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); -- cgit v1.2.3 From e5beca886ddf3138351765e1239b626e7bd612c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 18:07:30 -0400 Subject: suspend checks the cancel bit --- src/ir.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index bc3ef11481..c94efdf61f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6907,16 +6907,24 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); + + IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); -- cgit v1.2.3 From f0c049d02b68a8740096df633a41b78d90fd34cd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Jul 2018 18:37:30 -0400 Subject: detect double await --- src/ir.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index c94efdf61f..2f5ba86627 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6796,6 +6796,9 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr); } + IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited"); + IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited"); + Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); @@ -6823,6 +6826,15 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); + + IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); + IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, already_awaited_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); -- cgit v1.2.3 From 6fed777637daafc077ec2c079b0557f3c8bdce51 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 01:22:51 -0400 Subject: cancel detects if the target handle has already returned --- src/ir.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 2f5ba86627..6d6e1430fe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6672,6 +6672,8 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn"); + IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); @@ -6679,6 +6681,9 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 + IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 // TODO relies on Zig not re-ordering fields IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); @@ -6697,6 +6702,16 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); + IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); + ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, do_cancel_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, post_return_block); + IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); + IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); -- cgit v1.2.3 From c6f9a4c0445000e59bca16838cc413140d399f35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 01:26:11 -0400 Subject: cancel detects suspend bit --- src/ir.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 6d6e1430fe..48148e6768 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6672,6 +6672,7 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn"); IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); @@ -6684,6 +6685,7 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); @@ -6704,13 +6706,18 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false); - ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, do_cancel_block, is_comptime); + ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, post_return_block); IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + ir_set_cursor_at_end_and_append_block(irb, pre_return_block); + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, done_block, is_comptime); -- cgit v1.2.3 From 60cda3713fdc2d8bce01bfc229109a3c8b3efb6f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:04:09 -0400 Subject: suspend cancels awaiter when it gets canceled --- src/ir.cpp | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 48148e6768..4b0080d562 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6663,13 +6663,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo async_allocator_type_value, is_var_args); } -static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeCancel); - - IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); - if (target_inst == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - +static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *target_inst) { IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); @@ -6726,6 +6720,16 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_const_void(irb, scope, node); } +static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeCancel); + + IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); + if (target_inst == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_gen_cancel_target(irb, scope, node, target_inst); +} + static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); @@ -6941,14 +6945,19 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended"); + IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled"); IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended"); + IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter"); + IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); + IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 + IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, @@ -6956,7 +6965,17 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, canceled_block); + IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); + IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); + ir_gen_cancel_target(irb, parent_scope, node, await_handle); + ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); @@ -6995,7 +7014,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0)); cases[0].block = resume_block; cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); - cases[1].block = cleanup_block; + cases[1].block = canceled_block; ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr)); -- cgit v1.2.3 From 0ba2bc38d76537a83bd6c27143779857dbb711cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:23:47 -0400 Subject: await checks the cancel bit --- src/ir.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 4b0080d562..c246b75d78 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6824,6 +6824,12 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited"); IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited"); + IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); + IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); + IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); + IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); + IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); + IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6836,6 +6842,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 + IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); @@ -6861,11 +6868,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); + IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); + IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); - IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend"); - IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend"); - IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); @@ -6886,8 +6895,6 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); - IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); - IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_build_const_u8(irb, scope, node, 0); -- cgit v1.2.3 From dd272d13161d7a7069af78669d891d280c65529f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 12:36:02 -0400 Subject: await cancels the await target when it is canceled --- src/ir.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index c246b75d78..f298cff24b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6830,11 +6830,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend"); IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); + IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, atomic_state_field_name); + IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise); IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false); IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node); IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); @@ -6900,10 +6902,15 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); - cases[1].block = cleanup_block; + cases[1].block = cancel_target_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); + ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); + IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); + ir_gen_cancel_target(irb, scope, node, await_handle); + ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); + ir_set_cursor_at_end_and_append_block(irb, cleanup_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); -- cgit v1.2.3 From 0d79e0381601404b844b4912d15d20f4b529e837 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 28 Jul 2018 13:52:48 -0400 Subject: canceling an await also cancels things awaiting it --- src/ir.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index f298cff24b..7134b4d9ac 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6831,6 +6831,8 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup"); IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume"); IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); + IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); + IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6912,6 +6914,20 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); + IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); + IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, ptr_mask, false); + IrInstruction *have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_await_handle_addr, zero, false); + ir_build_cond_br(irb, scope, node, have_my_await_handle, do_cancel_block, do_defers_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); + IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); + ir_gen_cancel_target(irb, scope, node, my_await_handle); + ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false)); + + ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); -- cgit v1.2.3 From 09304aab77a7b6af5693600fa6b3a35322f7469d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 29 Jul 2018 23:25:40 -0400 Subject: fix cancel and await semantics --- src/ir.cpp | 61 +++++++++++++++++++++++++++++++++-------------------- std/debug/index.zig | 14 ++++++++++-- 2 files changed, 50 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 7134b4d9ac..e91fa14170 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6663,7 +6663,9 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo async_allocator_type_value, is_var_args); } -static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *target_inst) { +static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node, + IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited) +{ IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn"); @@ -6691,7 +6693,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode // set the is_canceled bit IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr, - AtomicRmwOp_and, AtomicOrderSeqCst); + AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); @@ -6703,14 +6705,26 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, post_return_block); - IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); - IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); - ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + if (cancel_awaited) { + ir_build_br(irb, scope, node, do_cancel_block, is_comptime); + } else { + IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false); + IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false); + ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime); + } ir_set_cursor_at_end_and_append_block(irb, pre_return_block); - IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); - IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); - ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + if (cancel_awaited) { + if (cancel_non_suspended) { + ir_build_br(irb, scope, node, do_cancel_block, is_comptime); + } else { + IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); + IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime); + } + } else { + ir_build_br(irb, scope, node, done_block, is_comptime); + } ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); ir_build_cancel(irb, scope, node, target_inst); @@ -6727,7 +6741,7 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - return ir_gen_cancel_target(irb, scope, node, target_inst); + return ir_gen_cancel_target(irb, scope, node, target_inst, false, true); } static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -6872,15 +6886,19 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_unreachable(irb, scope, node); ir_set_cursor_at_end_and_append_block(irb, not_awaited_block); + IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); + IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); - ir_build_cond_br(irb, scope, node, is_canceled_bool, cleanup_block, not_canceled_block, const_bool_false); + ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); - IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); - IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); + ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); + ir_build_cancel(irb, scope, node, target_inst); + ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); + ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); if (irb->codegen->have_err_ret_tracing) { Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); @@ -6897,6 +6915,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_build_cancel(irb, scope, node, target_inst); ir_build_br(irb, scope, node, merge_block, const_bool_false); + ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); @@ -6904,32 +6923,28 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); - cases[1].block = cancel_target_block; + cases[1].block = cleanup_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); - ir_set_cursor_at_end_and_append_block(irb, cancel_target_block); - IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr); - ir_gen_cancel_target(irb, scope, node, await_handle); - ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); - ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, ptr_mask, false); - IrInstruction *have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_await_handle_addr, zero, false); - ir_build_cond_br(irb, scope, node, have_my_await_handle, do_cancel_block, do_defers_block, const_bool_false); + IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); + IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); + ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, do_cancel_block); IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr); - ir_gen_cancel_target(irb, scope, node, my_await_handle); + ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false); ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, do_defers_block); ir_gen_defers_for_block(irb, scope, outer_scope, true); - ir_mark_gen(ir_build_br(irb, scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); ir_build_br(irb, scope, node, merge_block, const_bool_false); @@ -7004,7 +7019,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); - ir_gen_cancel_target(irb, parent_scope, node, await_handle); + ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false); ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); diff --git a/std/debug/index.zig b/std/debug/index.zig index 3070e0b40b..ab50d79db3 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void { const stderr = getStderrStream() catch return; stderr.print(fmt, args) catch return; } -fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { +pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { if (stderr_stream) |st| { return st; } else { @@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, } } +pub inline fn getReturnAddress(frame_count: usize) usize { + var fp = @ptrToInt(@frameAddress()); + var i: usize = 0; + while (fp != 0 and i < frame_count) { + fp = @intToPtr(*const usize, fp).*; + i += 1; + } + return @intToPtr(*const usize, fp + @sizeOf(usize)).*; +} + pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { const AddressState = union(enum) { NotLookingForStartAddress, @@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ } } -fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { +pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { switch (builtin.os) { builtin.Os.windows => return error.UnsupportedDebugInfo, builtin.Os.macosx => { -- cgit v1.2.3 From 6fd6bc94f57b815774f9717d52bacbf304a496be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Jul 2018 12:22:54 -0400 Subject: await sets suspend bit; return clears suspend bit --- src/ir.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index e91fa14170..699baa152e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6744,13 +6744,9 @@ static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_cancel_target(irb, scope, node, target_inst, false, true); } -static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeResume); - - IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); - if (target_inst == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - +static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node, + IrInstruction *target_inst) +{ IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended"); @@ -6797,6 +6793,16 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_const_void(irb, scope, node); } +static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeResume); + + IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); + if (target_inst == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_gen_resume_target(irb, scope, node, target_inst); +} + static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeAwaitExpr); @@ -6847,6 +6853,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget"); IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel"); IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers"); + IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock"); + IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended"); + IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended"); + IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend"); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6861,6 +6871,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 + IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false); @@ -6917,22 +6928,42 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); + IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, + AtomicRmwOp_or, AtomicOrderSeqCst); + IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false); + IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false); + ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, my_suspended_block); + ir_build_unreachable(irb, scope, node); + + ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block); + IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false); + IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false); + ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false); + + ir_set_cursor_at_end_and_append_block(irb, do_suspend_block); IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false); IrInstructionSwitchBrCase *cases = allocate(2); cases[0].value = ir_build_const_u8(irb, scope, node, 0); cases[0].block = resume_block; cases[1].value = ir_build_const_u8(irb, scope, node, 1); - cases[1].block = cleanup_block; + cases[1].block = destroy_block; ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr); + ir_set_cursor_at_end_and_append_block(irb, destroy_block); + ir_gen_cancel_target(irb, scope, node, target_inst, false, true); + ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); + ir_set_cursor_at_end_and_append_block(irb, cleanup_block); IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false); - IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, + IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr, AtomicRmwOp_or, AtomicOrderSeqCst); - IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, ptr_mask, false); + IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false); IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false); IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false); ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false); @@ -6996,6 +7027,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter"); IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise); + IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true); IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 @@ -7015,11 +7047,13 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod ir_set_cursor_at_end_and_append_block(irb, canceled_block); IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); + IrBasicBlock *post_canceled_block = irb->current_basic_block; ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false); + IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); @@ -7064,8 +7098,15 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod 2, cases, const_bool_false, nullptr)); ir_set_cursor_at_end_and_append_block(irb, cleanup_block); + IrBasicBlock **incoming_blocks = allocate(2); + IrInstruction **incoming_values = allocate(2); + incoming_blocks[0] = post_canceled_block; + incoming_values[0] = const_bool_true; + incoming_blocks[1] = post_cancel_awaiter_block; + incoming_values[1] = const_bool_false; + IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); - ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); + ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false)); ir_set_cursor_at_end_and_append_block(irb, resume_block); return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); @@ -7450,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); ir_set_cursor_at_end_and_append_block(irb, resume_block); - ir_build_coro_resume(irb, scope, node, awaiter_handle); + ir_gen_resume_target(irb, scope, node, awaiter_handle); ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false); } -- cgit v1.2.3 From e79c913cbcb03834fcc04e2258ed5da7d533c9db Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:16:31 +0900 Subject: src/all_types.hpp: add enums for Handle Builtin; Tracking Issue #1296 ; --- src/all_types.hpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 2f09e70301..f03a250aea 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1358,6 +1358,7 @@ enum BuiltinFnId { BuiltinFnIdBreakpoint, BuiltinFnIdReturnAddress, BuiltinFnIdFrameAddress, + BuiltinFnIdHandle, BuiltinFnIdEmbedFile, BuiltinFnIdCmpxchgWeak, BuiltinFnIdCmpxchgStrong, @@ -2076,6 +2077,7 @@ enum IrInstructionId { IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, + IrInstructionIdHandle, IrInstructionIdAlignOf, IrInstructionIdOverflowOp, IrInstructionIdTestErr, @@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress { IrInstruction base; }; +struct IrInstructionHandle { + IrInstruction base; +}; + enum IrOverflowOp { IrOverflowOpAdd, IrOverflowOpSub, -- cgit v1.2.3 From a9ea22d4f9816998cccfca4df2ef56d5069e1814 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:17:38 +0900 Subject: src/ir.cpp: wire-up IR for handle builtin; Tracking Issue #1296 ; --- src/ir.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 699baa152e..50c8c70290 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) return IrInstructionIdFrameAddress; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { + return IrInstructionIdHandle; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { return IrInstructionIdAlignOf; } @@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction return new_instruction; } +static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionHandle *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *result_ptr, TypeTableEntry *result_ptr_type) @@ -4475,6 +4490,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); + case BuiltinFnIdHandle: + return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -19007,6 +19024,13 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn return u8_ptr_const; } +static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { + ir_build_handle_from(&ira->new_irb, &instruction->base); + + TypeTableEntry *promise_type = get_promise_type(ira->codegen, nullptr); + return promise_type; +} + static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) @@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdHandle: + return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction); case IrInstructionIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: @@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: + case IrInstructionIdHandle: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: case IrInstructionIdOptionalWrap: -- cgit v1.2.3 From cd18186715bf532715c03d8e67096606fe0f2bee Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:18:29 +0900 Subject: src/codegen.cpp: base handle builtin on `@frameAddress()`; Tracking Issue #1296 ; --- src/codegen.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 7420da9797..336aded82c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4146,6 +4146,15 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); } +static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, + IrInstructionHandle *instruction) +{ + // @andrewrk, not sure what to place here ? + // `get_promise_frame_type` ? + LLVMValueRef handle = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + return LLVMBuildRet(g->builder, handle); +} + static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { TypeTableEntry *int_type = instruction->result_ptr_type; assert(int_type->id == TypeTableEntryIdInt); @@ -4910,6 +4919,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdHandle: + return ir_render_handle(g, executable, (IrInstructionHandle *)instruction); case IrInstructionIdOverflowOp: return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: @@ -6344,6 +6355,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); + create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0); create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); -- cgit v1.2.3 From da5f3d5c4c79b1d0d0919a0226f5304081b2f049 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:19:16 +0900 Subject: src/ir_print.cpp: support `@handle()`; Tracking Issue #1296 ; --- src/ir_print.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 127afa94a5..77c7ef47b6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst fprintf(irp->f, "@frameAddress()"); } +static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) { + fprintf(irp->f, "@handle()"); +} + static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { fprintf(irp->f, "@returnAddress()"); } @@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFrameAddress: ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); break; + case IrInstructionIdHandle: + ir_print_handle(irp, (IrInstructionHandle *)instruction); + break; case IrInstructionIdAlignOf: ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); break; -- cgit v1.2.3 From a2e5691228c61e5c56220fc8f5f72e47b0611000 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 11:46:31 +0900 Subject: src/codegen.cpp: return null if calling convention is not async; Tracking Issue #1296 ; --- src/codegen.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 336aded82c..cbd1955839 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,9 +4149,15 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - // @andrewrk, not sure what to place here ? - // `get_promise_frame_type` ? - LLVMValueRef handle = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + + bool is_async = executable->fn_entry != nullptr && + executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + + if (!is_async || !executable->coro_handle) { + return LLVMConstNull(g->builtin_types.entry_promise->type_ref); + } + + LLVMValueRef handle = ir_llvm_value(g, executable->coro_handle); return LLVMBuildRet(g->builder, handle); } -- cgit v1.2.3 From 81f463626ad09dd09e525cda140fb63baf11bc73 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 14:13:26 +0900 Subject: src/codegen.cpp: add/throw error for @handle() in a non async context; Tracking Issue #1296 ; I removed/commented-out the assert checking for no errors since we now have some errors rendered. --- src/codegen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index cbd1955839..43e2a0b694 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4154,6 +4154,7 @@ static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; if (!is_async || !executable->coro_handle) { + add_node_error(g, instruction->base.source_node, buf_sprintf("@handle() in non-async function")); return LLVMConstNull(g->builtin_types.entry_promise->type_ref); } @@ -6022,7 +6023,8 @@ static void do_code_gen(CodeGen *g) { ir_render(g, fn_table_entry); } - assert(!g->errors.length); + + //assert(!g->errors.length); if (buf_len(&g->global_asm) != 0) { LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); -- cgit v1.2.3 From 0ee65025623c0440ed655d68b579f17e6d6e5f5c Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:20:56 +0900 Subject: src/codegen.cpp: remove `add_node_error` from `ir_render_handle`; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/codegen.cpp | 12 +----------- test/cases/coroutines.zig | 3 +++ 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 43e2a0b694..8a18a3f8dd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,17 +4149,7 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - - bool is_async = executable->fn_entry != nullptr && - executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; - - if (!is_async || !executable->coro_handle) { - add_node_error(g, instruction->base.source_node, buf_sprintf("@handle() in non-async function")); - return LLVMConstNull(g->builtin_types.entry_promise->type_ref); - } - - LLVMValueRef handle = ir_llvm_value(g, executable->coro_handle); - return LLVMBuildRet(g->builder, handle); + return LLVMConstNull(g->builtin_types.entry_promise->type_ref); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 53c5c3f906..deb4aa1b24 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -57,6 +57,9 @@ test "coroutine suspend with block" { resume a_promise; std.debug.assert(result); cancel p; + + assert( @handle() ); + } var a_promise: promise = undefined; -- cgit v1.2.3 From db362bec185d2eeb5a4dd018deb2a988479f0f1a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:22:00 +0900 Subject: src/codegen.cpp: reassert that there are no generated errors in codegen; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 8a18a3f8dd..54effb9480 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6014,7 +6014,7 @@ static void do_code_gen(CodeGen *g) { } - //assert(!g->errors.length); + assert(!g->errors.length); if (buf_len(&g->global_asm) != 0) { LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); -- cgit v1.2.3 From c1a3b0cb0af3c0639ce09b2a17a3cc90977346fe Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:23:09 +0900 Subject: src/ir.cpp: add/throw error for @handle() in a non async context; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/ir.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 50c8c70290..1e9d1bdb7e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3858,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } + bool is_async = exec_is_async(irb->exec); + switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); @@ -4491,6 +4493,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: + if (!is_async) { + add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); + return irb->codegen->invalid_instruction; + } return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { -- cgit v1.2.3 From fcf53b31fc2899cba682c4c9a8cade40c6e0ab9e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:38:22 +0900 Subject: src/ir.cpp: return promise->T instead of promise; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 1e9d1bdb7e..9712cb0bc2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19033,8 +19033,9 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { ir_build_handle_from(&ira->new_irb, &instruction->base); - TypeTableEntry *promise_type = get_promise_type(ira->codegen, nullptr); - return promise_type; + FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); + assert(fn_entry != nullptr); + return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type); } static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { -- cgit v1.2.3 From a8ea2360958354ba8310c7cea388351299e72e44 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 17:35:50 +0900 Subject: src/ir.cpp: don't allow `@handle()` outside of a function; Tracking Issue #1296 ; --- src/ir.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 9712cb0bc2..966d8e9f33 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4493,6 +4493,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: + if (!irb->exec->fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); + return irb->codegen->invalid_instruction; + } if (!is_async) { add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); return irb->codegen->invalid_instruction; -- cgit v1.2.3 From 104bdb03d6b5906716efeb84045079a424bf650a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:29:40 +0900 Subject: src/codegen.cpp: return promise instead of null promise; Tracking Issue #1296 ; --- src/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/codegen.cpp b/src/codegen.cpp index 54effb9480..bd708e3824 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,7 +4149,8 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - return LLVMConstNull(g->builtin_types.entry_promise->type_ref); + LLVMValueRef ptr = ir_llvm_value(g, executable->fn_entry->ir_executable.coro_handle->other); + return LLVMBuildBitCast(g->builder, ptr, g->builtin_types.entry_promise->type_ref, ""); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { -- cgit v1.2.3 From 92cb330e160388bca7ddd7ceecbd7157ce92247b Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 12:27:50 +0900 Subject: src/codegen.cpp: @handle(): replace hacky ref chain with llvm intrinsic; Tracking Issue #1296 ; --- src/all_types.hpp | 1 + src/codegen.cpp | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index f03a250aea..8d55a75f9f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1717,6 +1717,7 @@ struct CodeGen { LLVMValueRef coro_save_fn_val; LLVMValueRef coro_promise_fn_val; LLVMValueRef coro_alloc_helper_fn_val; + LLVMValueRef coro_frame_fn_val; LLVMValueRef merge_err_ret_traces_fn_val; LLVMValueRef add_error_return_trace_addr_fn_val; LLVMValueRef stacksave_fn_val; diff --git a/src/codegen.cpp b/src/codegen.cpp index bd708e3824..539356ef2f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4146,11 +4146,24 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); } +static LLVMValueRef get_handle_fn_val(CodeGen *g) { + if (g->coro_frame_fn_val) + return g->coro_frame_fn_val; + + LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0) + , nullptr, 0, false); + Buf *name = buf_sprintf("llvm.coro.frame"); + g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); + assert(LLVMGetIntrinsicID(g->coro_frame_fn_val)); + + return g->coro_frame_fn_val; +} + static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, executable->fn_entry->ir_executable.coro_handle->other); - return LLVMBuildBitCast(g->builder, ptr, g->builtin_types.entry_promise->type_ref, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, ""); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { -- cgit v1.2.3 From ca1b3563372dbf4442b5c3ff0fecaa174aea06b7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:09:00 +0900 Subject: src/all_types.hpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/all_types.hpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index 8d55a75f9f..b1e8a3746d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -899,7 +899,6 @@ struct AstNodeAwaitExpr { struct AstNodeSuspend { AstNode *block; - AstNode *promise_symbol; }; struct AstNodePromiseType { -- cgit v1.2.3 From 5e5685c1174350b00ebf3e9ac49e244fa38fc0ac Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:09:39 +0900 Subject: src/ast_render.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/ast_render.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2ace00885d..984b4230b1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { fprintf(ar->f, "suspend"); if (node->data.suspend.block != nullptr) { - fprintf(ar->f, " |"); - render_node_grouped(ar, node->data.suspend.promise_symbol); - fprintf(ar->f, "| "); render_node_grouped(ar, node->data.suspend.block); } break; -- cgit v1.2.3 From b3cd65d56e2efc3e0f67461dbad75747b2db3aa7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:10:48 +0900 Subject: src/ir.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/ir.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 966d8e9f33..7d2881744d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7096,19 +7096,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod if (node->data.suspend.block == nullptr) { suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); } else { - assert(node->data.suspend.promise_symbol != nullptr); - assert(node->data.suspend.promise_symbol->type == NodeTypeSymbol); - Buf *promise_symbol_name = node->data.suspend.promise_symbol->data.symbol_expr.symbol; Scope *child_scope; - if (!buf_eql_str(promise_symbol_name, "_")) { - VariableTableEntry *promise_var = ir_create_var(irb, node, parent_scope, promise_symbol_name, - true, true, false, const_bool_false); - ir_build_var_decl(irb, parent_scope, node, promise_var, nullptr, nullptr, irb->exec->coro_handle); - child_scope = promise_var->child_scope; - } else { - child_scope = parent_scope; - } - ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope); + ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope); suspend_scope->resume_block = resume_block; child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); -- cgit v1.2.3 From d3f628907a6b9e5b863c9d5235e6dadf57f42ca2 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:11:43 +0900 Subject: src/parser.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/parser.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/parser.cpp b/src/parser.cpp index a93d8de830..e2a818a56c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,35 +648,37 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; + Token *token = &pc->tokens->at(*token_index); + Token *suspend_token = nullptr; - Token *suspend_token = &pc->tokens->at(*token_index); if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; + suspend_token = token; + token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); + ast_expect_token(pc, token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - Token *bar_token = &pc->tokens->at(*token_index); - if (bar_token->id == TokenIdBinOr) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdBinOr); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; + //guessing that semicolon is checked elsewhere? + if (token->id != TokenIdLBrace) { + if (mandatory) { + ast_expect_token(pc, token, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index = orig_token_index; + return nullptr; + } } + //Expect that we have a block; AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); - ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); return node; @@ -3134,7 +3136,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.await_expr.expr, visit, context); break; case NodeTypeSuspend: - visit_field(&node->data.suspend.promise_symbol, visit, context); visit_field(&node->data.suspend.block, visit, context); break; } -- cgit v1.2.3 From 5de92425d57338ba6c94a90360f96eb2fa8efdda Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:11:37 +0900 Subject: src/parser.cpp: fix typo from rebase; --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/parser.cpp b/src/parser.cpp index e2a818a56c..84ccdbeea8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -655,7 +655,7 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b Token *token = &pc->tokens->at(*token_index); Token *suspend_token = nullptr; - if (suspend_token->id == TokenIdKeywordSuspend) { + if (token->id == TokenIdKeywordSuspend) { *token_index += 1; suspend_token = token; token = &pc->tokens->at(*token_index); -- cgit v1.2.3 From 895f262a55b9951647efef4528c17cf64d6b7c07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Aug 2018 14:15:31 -0400 Subject: pull request fixups * clean up parser code * fix stage2 parse and render code * remove redundant test * make stage1 compile tests leaner --- src/parser.cpp | 36 +++++++++++++-------------------- std/zig/ast.zig | 12 ----------- std/zig/parse.zig | 33 +++++++++++++----------------- std/zig/parser_test.zig | 6 +++--- std/zig/render.zig | 16 +-------------- test/cases/coroutines.zig | 16 --------------- test/compile_errors.zig | 51 +++++++++++++++++++---------------------------- 7 files changed, 53 insertions(+), 117 deletions(-) (limited to 'src') diff --git a/src/parser.cpp b/src/parser.cpp index 84ccdbeea8..453ab7ce2c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -651,37 +651,29 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - Token *token = &pc->tokens->at(*token_index); - Token *suspend_token = nullptr; + Token *suspend_token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordSuspend) { + if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; - suspend_token = token; - token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordSuspend); + ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - //guessing that semicolon is checked elsewhere? - if (token->id != TokenIdLBrace) { - if (mandatory) { - ast_expect_token(pc, token, TokenIdLBrace); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } + Token *lbrace = &pc->tokens->at(*token_index); + if (lbrace->id == TokenIdLBrace) { + AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); + node->data.suspend.block = ast_parse_block(pc, token_index, true); + return node; + } else if (mandatory) { + ast_expect_token(pc, lbrace, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index -= 1; + return nullptr; } - - //Expect that we have a block; - AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.block = ast_parse_block(pc, token_index, true); - - return node; } /* diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 004f9278b9..95e899fb92 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1778,19 +1778,12 @@ pub const Node = struct { pub const Suspend = struct { base: Node, - label: ?TokenIndex, suspend_token: TokenIndex, - payload: ?*Node, body: ?*Node, pub fn iterate(self: *Suspend, index: usize) ?*Node { var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; - i -= 1; - } - if (self.body) |body| { if (i < 1) return body; i -= 1; @@ -1800,7 +1793,6 @@ pub const Node = struct { } pub fn firstToken(self: *Suspend) TokenIndex { - if (self.label) |label| return label; return self.suspend_token; } @@ -1809,10 +1801,6 @@ pub const Node = struct { return body.lastToken(); } - if (self.payload) |payload| { - return payload.lastToken(); - } - return self.suspend_token; } }; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 73d51e7870..fb49d2a2ba 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }) catch unreachable; continue; }, - Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ - .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = ctx.label, - .suspend_token = token_index, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); - continue; - }, Token.Id.Keyword_inline => { stack.append(State{ .Inline = InlineCtx{ @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + const token = nextToken(&tok_it, &tree); + switch (token.ptr.id) { + Token.Id.Semicolon => { + prevToken(&tok_it, &tree); + continue; + }, + Token.Id.LBrace => { + prevToken(&tok_it, &tree); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + continue; + }, + else => { + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = token.index } }; + }, } - continue; }, State.AsyncAllocator => |async_node| { if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { @@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con Token.Id.Keyword_suspend => { const node = try arena.create(ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = null, .suspend_token = token_index, - .payload = null, .body = null, }); ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 32cdc8121f..582bffdf3d 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" { ); } -test "zig fmt: labeled suspend" { +test "zig fmt: resume from suspend block" { try testCanonical( \\fn foo() void { - \\ s: suspend |p| { - \\ break :s; + \\ suspend { + \\ resume @handle(); \\ } \\} \\ diff --git a/std/zig/render.zig b/std/zig/render.zig index bc45768fa3..868902a0d1 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -323,21 +323,7 @@ fn renderExpression( ast.Node.Id.Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try renderToken(tree, stream, label, indent, start_col, Space.None); - try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); - } - - if (suspend_node.payload) |payload| { - if (suspend_node.body) |body| { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, body, space); - } else { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, payload, space); - } - } else if (suspend_node.body) |body| { + if (suspend_node.body) |body| { try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, body, space); } else { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index a955eeac37..bd6b6abf6f 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -256,19 +256,3 @@ async fn testBreakFromSuspend(my_result: *i32) void { suspend; my_result.* += 1; } - -test "suspend resume @handle()" { - var buf: [500]u8 = undefined; - var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; - var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); - std.debug.assert(my_result == 2); -} -async fn testSuspendResumeAtHandle() void { - suspend { - resume @handle(); - } - my_result.* += 1; - suspend; - my_result.* += 1; -} \ No newline at end of file diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f4b289f70d..948d212e58 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@handle() called outside of function definition", + \\var handle_undef: promise = undefined; + \\var handle_dummy: promise = @handle(); + \\export fn entry() bool { + \\ return handle_undef == handle_dummy; + \\} + , + ".tmp_source.zig:2:29: error: @handle() called outside of function definition", + ); + + cases.add( + "@handle() in non-async function", + \\export fn entry() bool { + \\ var handle_undef: promise = undefined; + \\ return handle_undef == @handle(); + \\} + , + ".tmp_source.zig:3:28: error: @handle() in non-async function", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { @@ -4738,34 +4759,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); - - cases.add( - "@handle() called outside of function definition", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\var handle_undef: promise = undefined; - \\var handle_dummy: promise = @handle(); - \\ - \\pub fn main() void { - \\ if (handle_undef == handle_dummy) return 0; - \\} - , - ".tmp_source.zig:6:29: error: @handle() called outside of function definition", - ); - - cases.add( - "@handle() in non-async function", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\pub fn main() void { - \\ var handle_undef: promise = undefined; - \\ if (handle_undef == @handle()) return 0; - \\} - , - ".tmp_source.zig:7:25: error: @handle() in non-async function", - ); } -- cgit v1.2.3 From 298abbcff86629273f24891d243fb6e503392e8f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:55:31 +0900 Subject: better support for `_` identifier * disallow variable declaration of `_` * prevent `_` from shadowing itself * prevent read access of `_` closes #1204 closes #1320 --- src/ir.cpp | 16 ++++++++++++- test/behavior.zig | 1 + test/cases/underscore.zig | 28 +++++++++++++++++++++++ test/compile_errors.zig | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/cases/underscore.zig (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 7d2881744d..8dd4bb41db 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3332,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) { - VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime); + bool is_underscored = name ? buf_eql_str(name, "_") : false; + VariableTableEntry *var = create_local_var( irb->codegen + , node + , scope + , (is_underscored ? nullptr : name) + , src_is_const + , gen_is_const + , (is_underscored ? true : is_shadowable) + , is_comptime ); if (is_comptime != nullptr || gen_is_const) { var->mem_slot_index = exec_next_mem_slot(irb->exec); var->owner_exec = irb->exec; @@ -5186,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; + if (buf_eql_str(variable_declaration->symbol, "_")) { + add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); + return irb->codegen->invalid_instruction; + } + IrInstruction *type_instruction; if (variable_declaration->type != nullptr) { type_instruction = ir_gen_node(irb, variable_declaration->type, scope); @@ -5198,6 +5211,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; + IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol, diff --git a/test/behavior.zig b/test/behavior.zig index b03336eb71..e993d7e0dc 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -60,6 +60,7 @@ comptime { _ = @import("cases/try.zig"); _ = @import("cases/type_info.zig"); _ = @import("cases/undefined.zig"); + _ = @import("cases/underscore.zig"); _ = @import("cases/union.zig"); _ = @import("cases/var_args.zig"); _ = @import("cases/void.zig"); diff --git a/test/cases/underscore.zig b/test/cases/underscore.zig new file mode 100644 index 0000000000..44451e3723 --- /dev/null +++ b/test/cases/underscore.zig @@ -0,0 +1,28 @@ +const std = @import("std"); +const assert = std.debug.assert; + +test "ignore lval with underscore" { + _ = false; +} + +test "ignore lval with underscore (for loop)" { + for ([]void{}) |_, i| { + for ([]void{}) |_, j| { + break; + } + break; + } +} + +test "ignore lval with underscore (while loop)" { + while (optionalReturnError()) |_| { + while (optionalReturnError()) |_| { + break; + } else |_| { } + break; + } else |_| { } +} + +fn optionalReturnError() !?u32 { + return error.optionalReturnError; +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 948d212e58..56b2c51d74 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -22,6 +22,64 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:28: error: @handle() in non-async function", ); + cases.add( + "`_` is not a declarable symbol", + \\export fn f1() usize { + \\ var _: usize = 2; + \\ return _; + \\} + , + ".tmp_source.zig:2:5: error: `_` is not a declarable symbol", + ".tmp_source.zig:3:12: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside for", + \\export fn returns() void { + \\ for ([]void{}) |_, i| { + \\ for ([]void{}) |_, j| { + \\ return _; + \\ } + \\ } + \\} + , + ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside while", + \\export fn returns() void { + \\ while (optionalReturn()) |_| { + \\ while (optionalReturn()) |_| { + \\ return _; + \\ } + \\ } + \\} + \\fn optionalReturn() ?u32 { + \\ return 1; + \\} + , + ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", + ); + + cases.add( + "`_` should not be usable inside while else", + \\export fn returns() void { + \\ while (optionalReturnError()) |_| { + \\ while (optionalReturnError()) |_| { + \\ return; + \\ } else |_| { + \\ if (_ == error.optionalReturnError) return; + \\ } + \\ } + \\} + \\fn optionalReturnError() !?u32 { + \\ return error.optionalReturnError; + \\} + , + ".tmp_source.zig:6:17: error: use of undeclared identifier '_'", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { -- cgit v1.2.3 From 9bd8b01650f9cf21e601117951711b21aa5fd216 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Aug 2018 15:21:08 -0400 Subject: fix tagged union initialization with a runtime void closes #1328 --- src/ir.cpp | 11 ++++++++++- test/cases/union.zig | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ir.cpp b/src/ir.cpp index 8dd4bb41db..3e423487aa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9628,6 +9628,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un case ConstValSpecialStatic: return &value->value; case ConstValSpecialRuntime: + if (!type_has_bits(value->value.type)) { + return &value->value; + } ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression")); return nullptr; case ConstValSpecialUndef: @@ -16129,8 +16132,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir if (casted_field_value == ira->codegen->invalid_instruction) return ira->codegen->builtin_types.entry_invalid; + type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type); + if (type_is_invalid(casted_field_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); - if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) { + if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || + !type_has_bits(casted_field_value->value.type)) + { ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk); if (!field_val) return ira->codegen->builtin_types.entry_invalid; diff --git a/test/cases/union.zig b/test/cases/union.zig index 78b2dc8dd7..08969e64fe 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -297,3 +297,17 @@ test "access a member of tagged union with conflicting enum tag name" { comptime assert(Bar.A == u8); } + +test "tagged union initialization with runtime void" { + assert(testTaggedUnionInit({})); +} + +const TaggedUnionWithAVoid = union(enum) { + A, + B: i32, +}; + +fn testTaggedUnionInit(x: var) bool { + const y = TaggedUnionWithAVoid{ .A = x }; + return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; +} -- cgit v1.2.3 From aa232089f2beaa273458a9fa75b2ba5c70f71805 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:06:39 -0400 Subject: translate-c: fix while loop with no body --- src/translate_c.cpp | 9 +++++++-- test/translate_c.zig | 10 ++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 267a716a9d..ceb6f2a0bc 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3015,9 +3015,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt)); case Stmt::DeclStmtClass: return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); - case Stmt::WhileStmtClass: + case Stmt::WhileStmtClass: { + AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt); + if (while_node->data.while_expr.body == nullptr) { + while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } return wrap_stmt(out_node, out_child_scope, scope, - trans_while_loop(c, scope, (const WhileStmt *)stmt)); + while_node); + } case Stmt::IfStmtClass: return wrap_stmt(out_node, out_child_scope, scope, trans_if_statement(c, scope, (const IfStmt *)stmt)); diff --git a/test/translate_c.zig b/test/translate_c.zig index 417171d2c2..1d1e0ee1c6 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("while with empty body", + \\void foo(void) { + \\ while (1); + \\} + , + \\pub fn foo() void { + \\ while (1 != 0) {} + \\} + ); + cases.add("double define struct", \\typedef struct Bar Bar; \\typedef struct Foo Foo; -- cgit v1.2.3 From c420b234cc5698d44b11cf313f14a9608328f111 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:18:24 -0400 Subject: translate-c: handle for loop with empty body --- src/translate_c.cpp | 13 ++++++++----- test/translate_c.zig | 10 ++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/translate_c.cpp b/src/translate_c.cpp index ceb6f2a0bc..bf31dc7c3d 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -3020,8 +3020,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, if (while_node->data.while_expr.body == nullptr) { while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); } - return wrap_stmt(out_node, out_child_scope, scope, - while_node); + return wrap_stmt(out_node, out_child_scope, scope, while_node); } case Stmt::IfStmtClass: return wrap_stmt(out_node, out_child_scope, scope, @@ -3048,9 +3047,13 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, case Stmt::DoStmtClass: return wrap_stmt(out_node, out_child_scope, scope, trans_do_loop(c, scope, (const DoStmt *)stmt)); - case Stmt::ForStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_for_loop(c, scope, (const ForStmt *)stmt)); + case Stmt::ForStmtClass: { + AstNode *while_node = trans_for_loop(c, scope, (const ForStmt *)stmt); + if (while_node->data.while_expr.body == nullptr) { + while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } + return wrap_stmt(out_node, out_child_scope, scope, while_node); + } case Stmt::StringLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, trans_string_literal(c, scope, (const StringLiteral *)stmt)); diff --git a/test/translate_c.zig b/test/translate_c.zig index 1d1e0ee1c6..8840ce1413 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,16 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("for with empty body", + \\void foo(void) { + \\ for (;;); + \\} + , + \\pub fn foo() void { + \\ while (true) {} + \\} + ); + cases.add("while with empty body", \\void foo(void) { \\ while (1); -- cgit v1.2.3 From 387fab60a61611c6cb48a5d9dd9d356911733210 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:32:38 -0400 Subject: translate-c: fix do while with empty body --- src/translate_c.cpp | 15 +++++++++++---- test/translate_c.zig | 12 ++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/translate_c.cpp b/src/translate_c.cpp index bf31dc7c3d..eaa6621147 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -2707,7 +2707,9 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt AstNode *child_statement; child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement); if (child_scope == nullptr) return nullptr; - body_node->data.block.statements.append(child_statement); + if (child_statement != nullptr) { + body_node->data.block.statements.append(child_statement); + } } // if (!cond) break; @@ -2717,6 +2719,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node); terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak); + assert(terminator_node != nullptr); body_node->data.block.statements.append(terminator_node); while_scope->node->data.while_expr.body = body_node; @@ -3044,9 +3047,13 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, case Stmt::UnaryExprOrTypeTraitExprClass: return wrap_stmt(out_node, out_child_scope, scope, trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); - case Stmt::DoStmtClass: - return wrap_stmt(out_node, out_child_scope, scope, - trans_do_loop(c, scope, (const DoStmt *)stmt)); + case Stmt::DoStmtClass: { + AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt); + if (while_node->data.while_expr.body == nullptr) { + while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } + return wrap_stmt(out_node, out_child_scope, scope, while_node); + } case Stmt::ForStmtClass: { AstNode *while_node = trans_for_loop(c, scope, (const ForStmt *)stmt); if (while_node->data.while_expr.body == nullptr) { diff --git a/test/translate_c.zig b/test/translate_c.zig index 8840ce1413..c14d75c254 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("do while with empty body", + \\void foo(void) { + \\ do ; while (1); + \\} + , // TODO this should be if (1 != 0) break + \\pub fn foo() void { + \\ while (true) { + \\ if (!1) break; + \\ } + \\} + ); + cases.add("for with empty body", \\void foo(void) { \\ for (;;); -- cgit v1.2.3 From 63a23e848a62d5f167f8d5478de9766cb24aa6eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 5 Aug 2018 18:40:14 -0400 Subject: translate-c: fix for loops with var init and empty body --- src/translate_c.cpp | 16 ++++++++++------ test/translate_c.zig | 13 +++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/translate_c.cpp b/src/translate_c.cpp index eaa6621147..735a671bcc 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -2783,7 +2783,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement); if (body_scope == nullptr) return nullptr; - while_scope->node->data.while_expr.body = body_statement; + + if (body_statement == nullptr) { + while_scope->node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); + } else { + while_scope->node->data.while_expr.body = body_statement; + } return loop_block_node; } @@ -3020,6 +3025,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); case Stmt::WhileStmtClass: { AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt); + assert(while_node->type == NodeTypeWhileExpr); if (while_node->data.while_expr.body == nullptr) { while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); } @@ -3049,17 +3055,15 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); case Stmt::DoStmtClass: { AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt); + assert(while_node->type == NodeTypeWhileExpr); if (while_node->data.while_expr.body == nullptr) { while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); } return wrap_stmt(out_node, out_child_scope, scope, while_node); } case Stmt::ForStmtClass: { - AstNode *while_node = trans_for_loop(c, scope, (const ForStmt *)stmt); - if (while_node->data.while_expr.body == nullptr) { - while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); - } - return wrap_stmt(out_node, out_child_scope, scope, while_node); + AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt); + return wrap_stmt(out_node, out_child_scope, scope, node); } case Stmt::StringLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, diff --git a/test/translate_c.zig b/test/translate_c.zig index c14d75c254..b31e515aa2 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("for loop with var init but empty body", + \\void foo(void) { + \\ for (int x = 0; x < 10; x++); + \\} + , + \\pub fn foo() void { + \\ { + \\ var x: c_int = 0; + \\ while (x < 10) : (x += 1) {} + \\ } + \\} + ); + cases.add("do while with empty body", \\void foo(void) { \\ do ; while (1); -- cgit v1.2.3