From 5d705fc6e35e75a604d3dbbb377ab01bf2b2b575 Mon Sep 17 00:00:00 2001
From: Andrew Kelley <superjoe30@gmail.com>
Date: Mon, 18 Jun 2018 15:01:42 -0400
Subject: [PATCH 1/5] 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(-)

diff --git a/doc/langref.html.in b/doc/langref.html.in
index f1ae2bafaa44..48f525fedcba 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4919,12 +4919,15 @@ test "main" {
       </p>
       {#see_also|@import#}
       {#header_close#}
-      {#header_open|@export#}
-      <pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8</code></pre>
+
+      {#header_open|@errSetCast#}
+      <pre><code class="zig">@errSetCast(comptime T: DestType, value: var) DestType</code></pre>
       <p>
-      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#}.
       </p>
       {#header_close#}
+
       {#header_open|@errorName#}
       <pre><code class="zig">@errorName(err: error) []u8</code></pre>
       <p>
@@ -4949,6 +4952,14 @@ test "main" {
       stack trace object. Otherwise returns `null`.
       </p>
       {#header_close#}
+
+      {#header_open|@export#}
+      <pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8</code></pre>
+      <p>
+      Creates a symbol in the output object file.
+      </p>
+      {#header_close#}
+
       {#header_open|@fence#}
       <pre><code class="zig">@fence(order: AtomicOrder)</code></pre>
       <p>
@@ -5817,10 +5828,10 @@ pub fn build(b: &Builder) void {
       {#header_open|Undefined Behavior#}
       <p>
       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.
       </p>
       <p>
@@ -6101,6 +6112,11 @@ comptime {
       <p>TODO</p>
 
       {#header_close#}
+
+      {#header_open|Invalid Error Set Cast#}
+      <p>TODO</p>
+      {#header_close#}
+
       {#header_open|Incorrect Pointer Alignment#}
       <p>TODO</p>
 
@@ -6109,6 +6125,7 @@ comptime {
       <p>TODO</p>
 
       {#header_close#}
+
       {#header_close#}
       {#header_open|Memory#}
       <p>TODO: explain no default allocator in zig</p>
diff --git a/src/all_types.hpp b/src/all_types.hpp
index d94dfa0fcbe2..732af239e269 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 84335d4e0672..585881a9a5ad 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 c75a3ae7c1fb..e5ba4114cf9c 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<IrInstructionErrSetCast>(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<IrInstructionIntToFloat>(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 cb917201805a..2667c246a599 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 693631fe2d79..95890d838467 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);
 }
 

From 1aafbae5be518309b4c2194cdc24e22642514519 Mon Sep 17 00:00:00 2001
From: Andrew Kelley <superjoe30@gmail.com>
Date: Mon, 18 Jun 2018 17:25:29 -0400
Subject: [PATCH 2/5] 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(-)

diff --git a/doc/langref.html.in b/doc/langref.html.in
index 48f525fedcba..9a3d5e5f1701 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 {
       </p>
       {#header_close#}
 
+      {#header_open|@bytesToSlice#}
+      <pre><code class="zig">@bytesToSlice(comptime Element: type, bytes: []u8) []Element</code></pre>
+      <p>
+      Converts a slice of bytes or array of bytes into a slice of <code>Element</code>.
+      The resulting slice has the same {#link|pointer|Pointers#} properties as the parameter.
+      </p>
+      <p>
+      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#}.
+      </p>
+      {#header_close#}
+
       {#header_open|@cDefine#}
       <pre><code class="zig">@cDefine(comptime name: []u8, value)</code></pre>
       <p>
@@ -5467,8 +5478,9 @@ pub const FloatMode = enum {
       </p>
       {#see_also|@shlExact|@shlWithOverflow#}
       {#header_close#}
+
       {#header_open|@sizeOf#}
-      <pre><code class="zig">@sizeOf(comptime T: type) (number literal)</code></pre>
+      <pre><code class="zig">@sizeOf(comptime T: type) comptime_int</code></pre>
       <p>
       This function returns the number of bytes it takes to store <code>T</code> in memory.
       </p>
@@ -5476,6 +5488,15 @@ pub const FloatMode = enum {
       The result is a target-specific compile time constant.
       </p>
       {#header_close#}
+
+      {#header_open|@sliceToBytes#}
+      <pre><code class="zig">@sliceToBytes(value: var) []u8</code></pre>
+      <p>
+      Converts a slice or array to a slice of <code>u8</code>. The resulting slice has the same
+      {#link|pointer|Pointers#} properties as the parameter.
+      </p>
+      {#header_close#}
+
       {#header_open|@sqrt#}
       <pre><code class="zig">@sqrt(comptime T: type, value: T) T</code></pre>
       <p>
@@ -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 732af239e269..d8432232c2b7 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 585881a9a5ad..1bc9a1780470 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 e5ba4114cf9c..c5ca12af1e0e 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<IrInstructionToBytes>(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<IrInstructionFromBytes>(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<IrInstructionIntToFloat>(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 2667c246a599..1b35ecf57f54 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 2a2c8c0b5979..c948818e3d63 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 64f78ae4a397..fe5409ad4dc3 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 b02589b0dd34..55844b88db86 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 f21611ff9168..8c1aeb92d717 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 cb4788ba17cf..45b205451d35 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 682c185e8602..64f0788efc01 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 f1e49c6d1f3e..0b79f0df487a 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 beb0d6d45636..89c441e7f917 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 94a2ba633651..2941ecb56ad6 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 8c5abaaccc10..2f4a22553bd3 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 {

From a430853a48a5e4dbcd0094c632957e28898159f3 Mon Sep 17 00:00:00 2001
From: Andrew Kelley <superjoe30@gmail.com>
Date: Mon, 18 Jun 2018 17:43:01 -0400
Subject: [PATCH 3/5] standard library fixes

---
 src/ir.cpp       | 2 +-
 std/cstr.zig     | 2 +-
 std/os/index.zig | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/ir.cpp b/src/ir.cpp
index c5ca12af1e0e..3ed9f66947d0 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 d9106769c192..e83d5a39e9e8 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 f1c3ab2128d9..dd0d4e2ea106 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);
 

From 626b73e8beeaae1cab23f883f877d89d64bbfa39 Mon Sep 17 00:00:00 2001
From: Andrew Kelley <superjoe30@gmail.com>
Date: Mon, 18 Jun 2018 18:48:29 -0400
Subject: [PATCH 4/5] 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(-)

diff --git a/doc/langref.html.in b/doc/langref.html.in
index 9a3d5e5f1701..24bf6e1b162e 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4658,7 +4658,7 @@ comptime {
       </p>
       <p>
       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#}.
       </p>
       {#header_close#}
 
@@ -4935,7 +4935,7 @@ test "main" {
       <pre><code class="zig">@errSetCast(comptime T: DestType, value: var) DestType</code></pre>
       <p>
       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#}.
       </p>
       {#header_close#}
 
@@ -4955,6 +4955,7 @@ test "main" {
       error name table will be generated.
       </p>
       {#header_close#}
+
       {#header_open|@errorReturnTrace#}
       <pre><code class="zig">@errorReturnTrace() ?*builtin.StackTrace</code></pre>
       <p>
@@ -4964,6 +4965,25 @@ test "main" {
       </p>
       {#header_close#}
 
+      {#header_open|@errorToInt#}
+      <pre><code class="zig">@errorToInt(err: var) @IntType(false, @sizeOf(error) * 8)</code></pre>
+      <p>
+      Supports the following types:
+      </p>
+      <ul>
+          <li>error unions</li>
+          <li><code>E!void</code></li>
+      </ul>
+      <p>
+      Converts an error to the integer representation of an error.
+      </p>
+      <p>
+      It is generally recommended to avoid this
+      cast, as the integer representation of an error is not stable across source code changes.
+      </p>
+      {#see_also|@intToError#}
+      {#header_close#}
+
       {#header_open|@export#}
       <pre><code class="zig">@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) []const u8</code></pre>
       <p>
@@ -5071,8 +5091,24 @@ fn add(a: i32, b: i32) i32 { return a + b; }
       <p>
       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#}.
+      </p>
+      {#header_close#}
+
+      {#header_open|@intToError#}
+      <pre><code class="zig">@intToError(value: @IntType(false, @sizeOf(error) * 8)) error</code></pre>
+      <p>
+      Converts from the integer representation of an error into the global error set type.
+      </p>
+      <p>
+      It is generally recommended to avoid this
+      cast, as the integer representation of an error is not stable across source code changes.
+      </p>
+      <p>
+      Attempting to convert an integer that does not correspond to any error results in
+      safety-protected {#link|Undefined Behavior#}.
       </p>
+      {#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#}
       <p>At runtime crashes with the message <code>invalid error code</code> and a stack trace.</p>
@@ -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 d8432232c2b7..e1a4ed751087 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 1bc9a1780470..c9b4ade4c60c 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 3ed9f66947d0..350c80017efe 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 3a0fa7f46134..da5e708555c8 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 0b79f0df487a..156835bcb3c7 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 95890d838467..45971fd40d20 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 1bc58b14e1e5..46fb3852f39d 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 2f4a22553bd3..866b30308282 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 96384066e504..ea5beafe8d88 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];
         \\}
     );

From a3ddd0826bd9c799768c0c707de72c21befa742a Mon Sep 17 00:00:00 2001
From: Andrew Kelley <superjoe30@gmail.com>
Date: Tue, 19 Jun 2018 03:50:38 -0400
Subject: [PATCH 5/5] 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(-)

diff --git a/doc/langref.html.in b/doc/langref.html.in
index 24bf6e1b162e..19f023f6e1cd 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#}
+      <pre><code class="zig">@enumToInt(enum_value: var) var</code></pre>
+      <p>
+      Converts an enumeration value into its integer tag type.
+      </p>
+      {#see_also|@intToEnum#}
+      {#header_close#}
+
       {#header_open|@errSetCast#}
       <pre><code class="zig">@errSetCast(comptime T: DestType, value: var) DestType</code></pre>
       <p>
@@ -5095,6 +5103,18 @@ fn add(a: i32, b: i32) i32 { return a + b; }
       </p>
       {#header_close#}
 
+      {#header_open|@intToEnum#}
+      <pre><code class="zig">@intToEnum(comptime DestType: type, int_value: @TagType(DestType)) DestType</code></pre>
+      <p>
+      Converts an integer into an {#link|enum#} value.
+      </p>
+      <p>
+      Attempting to convert an integer which represents no value in the chosen enum type invokes
+      safety-checked {#link|Undefined Behavior#}.
+      </p>
+      {#see_also|@enumToInt#}
+      {#header_close#}
+
       {#header_open|@intToError#}
       <pre><code class="zig">@intToError(value: @IntType(false, @sizeOf(error) * 8)) error</code></pre>
       <p>
@@ -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 e1a4ed751087..41c9fe18c32d 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 c9b4ade4c60c..21520e0dd039 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 350c80017efe..a8599e7aae04 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<IrInstructionIntToEnum>(
             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<IrInstructionEnumToInt>(
+            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 1b35ecf57f54..5e5a71382cdd 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 2930cd21bb19..8986034fb457 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 6a02a4778418..50edfda53689 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 bdcbbdb452b7..78b2dc8dd7f7 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 866b30308282..609e3f103f6f 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",
     );