Merge pull request #1134 from ziglang/no-explicit-casting

remove "cast harder" casting syntax; add new casting builtins
master
Andrew Kelley 2018-06-19 11:40:21 -04:00 committed by GitHub
commit 9f2324389d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 697 additions and 222 deletions

View File

@ -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);
@ -1901,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.
@ -1913,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.
@ -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 safety-protected {#link|Undefined Behavior#}.
</p>
{#header_close#}
{#header_open|@cDefine#}
<pre><code class="zig">@cDefine(comptime name: []u8, value)</code></pre>
<p>
@ -4919,12 +4930,23 @@ 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|@enumToInt#}
<pre><code class="zig">@enumToInt(enum_value: var) var</code></pre>
<p>
Creates a symbol in the output object file.
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>
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 safety-protected {#link|Undefined Behavior#}.
</p>
{#header_close#}
{#header_open|@errorName#}
<pre><code class="zig">@errorName(err: error) []u8</code></pre>
<p>
@ -4941,6 +4963,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>
@ -4949,6 +4972,33 @@ test "main" {
stack trace object. Otherwise returns `null`.
</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>
Creates a symbol in the output object file.
</p>
{#header_close#}
{#header_open|@fence#}
<pre><code class="zig">@fence(order: AtomicOrder)</code></pre>
<p>
@ -5049,10 +5099,38 @@ 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|@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>
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#}
<pre><code class="zig">@intToFloat(comptime DestType: type, int: var) DestType</code></pre>
<p>
@ -5456,8 +5534,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>
@ -5465,6 +5544,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>
@ -5817,10 +5905,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>
@ -6091,8 +6179,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>
@ -6101,6 +6189,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 +6202,7 @@ comptime {
<p>TODO</p>
{#header_close#}
{#header_close#}
{#header_open|Memory#}
<p>TODO: explain no default allocator in zig</p>
@ -6793,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 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 enumToInt intToEnum",
literal: "true false null undefined"
},
n = [e, t.CLCM, t.CBCM, s, r];

View File

@ -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;
};
@ -1359,9 +1370,16 @@ enum BuiltinFnId {
BuiltinFnIdTruncate,
BuiltinFnIdIntCast,
BuiltinFnIdFloatCast,
BuiltinFnIdErrSetCast,
BuiltinFnIdToBytes,
BuiltinFnIdFromBytes,
BuiltinFnIdIntToFloat,
BuiltinFnIdFloatToInt,
BuiltinFnIdBoolToInt,
BuiltinFnIdErrToInt,
BuiltinFnIdIntToErr,
BuiltinFnIdEnumToInt,
BuiltinFnIdIntToEnum,
BuiltinFnIdIntType,
BuiltinFnIdSetCold,
BuiltinFnIdSetRuntimeSafety,
@ -2076,6 +2094,7 @@ enum IrInstructionId {
IrInstructionIdIntToPtr,
IrInstructionIdPtrToInt,
IrInstructionIdIntToEnum,
IrInstructionIdEnumToInt,
IrInstructionIdIntToErr,
IrInstructionIdErrToInt,
IrInstructionIdCheckSwitchProngs,
@ -2121,6 +2140,9 @@ enum IrInstructionId {
IrInstructionIdMergeErrRetTraces,
IrInstructionIdMarkErrRetTracePtr,
IrInstructionIdSqrt,
IrInstructionIdErrSetCast,
IrInstructionIdToBytes,
IrInstructionIdFromBytes,
};
struct IrInstruction {
@ -2656,6 +2678,26 @@ struct IrInstructionFloatCast {
IrInstruction *target;
};
struct IrInstructionErrSetCast {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionToBytes {
IrInstruction base;
IrInstruction *target;
};
struct IrInstructionFromBytes {
IrInstruction base;
IrInstruction *dest_child_type;
IrInstruction *target;
};
struct IrInstructionIntToFloat {
IrInstruction base;
@ -2866,6 +2908,13 @@ struct IrInstructionIntToPtr {
struct IrInstructionIntToEnum {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionEnumToInt {
IrInstruction base;
IrInstruction *target;
};

View File

@ -4727,6 +4727,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdIntToFloat:
case IrInstructionIdFloatToInt:
case IrInstructionIdBoolToInt:
case IrInstructionIdErrSetCast:
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
zig_unreachable();
case IrInstructionIdReturn:
@ -6320,6 +6324,10 @@ 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, 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
@ -6356,6 +6364,9 @@ 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);
create_builtin_fn(g, BuiltinFnIdToBytes, "sliceToBytes", 1);
create_builtin_fn(g, BuiltinFnIdFromBytes, "bytesToSlice", 2);
}
static const char *bool_to_str(bool b) {

View File

@ -468,6 +468,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) {
return IrInstructionIdFloatCast;
}
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;
}
@ -588,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;
}
@ -1941,6 +1957,37 @@ 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_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;
@ -2335,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);
@ -4054,6 +4117,46 @@ 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 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);
@ -4084,6 +4187,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);
@ -4605,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();
}
@ -9073,11 +9221,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);
@ -9631,6 +9774,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;
@ -9851,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;
}
@ -10073,7 +10218,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);
}
@ -10104,13 +10249,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;
@ -10142,7 +10280,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) &&
@ -10191,52 +10329,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 &&
@ -10438,31 +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 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);
}
// 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);
@ -17593,6 +17660,137 @@ 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_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))
@ -17627,6 +17825,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))
@ -20066,13 +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 IrInstructionIdIntToErr:
case IrInstructionIdErrToInt:
case IrInstructionIdStructInit:
case IrInstructionIdUnionInit:
case IrInstructionIdStructFieldPtr:
@ -20193,6 +20474,12 @@ 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 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:
@ -20327,6 +20614,14 @@ 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);
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();
}
@ -20544,9 +20839,13 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdAtomicLoad:
case IrInstructionIdIntCast:
case IrInstructionIdFloatCast:
case IrInstructionIdErrSetCast:
case IrInstructionIdIntToFloat:
case IrInstructionIdFloatToInt:
case IrInstructionIdBoolToInt:
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
return false;
case IrInstructionIdAsm:

View File

@ -664,6 +664,28 @@ 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_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);
@ -906,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, ")");
}
@ -1461,6 +1494,15 @@ 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 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;
@ -1686,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");
}

View File

@ -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| {

View File

@ -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,

View File

@ -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));
}
};

View File

@ -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..]);

View File

@ -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]);

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
@ -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);
}

View File

@ -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" {

View File

@ -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);
}
@ -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);
}
@ -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");
}
}

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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(
@ -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(
@ -2086,10 +2095,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(
@ -2611,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 {
@ -3239,18 +3238,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 {
@ -3722,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) {
@ -3749,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(
@ -4033,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",
);

View File

@ -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];
\\}
);