Date: Wed, 30 Jan 2019 23:36:52 -0500
Subject: introduce vector type for SIMD
See #903
* create with `@Vector(len, ElemType)`
* only wrapping addition is implemented
This feature is far from complete; this is only the beginning.
---
doc/langref.html.in | 34 ++++++
src-self-hosted/type.zig | 16 +++
src/all_types.hpp | 26 +++++
src/analyze.cpp | 91 ++++++++++++++--
src/analyze.hpp | 1 +
src/codegen.cpp | 47 +++++++-
src/ir.cpp | 213 +++++++++++++++++++++++++++++--------
src/ir_print.cpp | 11 ++
src/zig_llvm.cpp | 13 +++
src/zig_llvm.h | 3 +
std/hash_map.zig | 2 +
test/stage1/behavior/type_info.zig | 16 ++-
12 files changed, 419 insertions(+), 54 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 909c0f5817..e192dbbf76 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1531,6 +1531,29 @@ test "array initialization with function calls" {
{#code_end#}
{#see_also|for|Slices#}
{#header_close#}
+
+ {#header_open|Vectors#}
+
+ A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on
+ in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin
+ function {#link|@Vector#}.
+
+
+ TODO talk about C ABI interop
+
+ {#header_open|SIMD#}
+
+ TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the
+ docs with:
+ * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector?
+ * How to convert to/from vectors/arrays
+ * How to access individual elements from vectors, how to loop over the elements
+ * "shuffle"
+ * Advice on writing high perf software, how to abstract the best way
+
+ {#header_close#}
+ {#header_close#}
+
{#header_open|Pointers#}
Zig has two kinds of pointers:
@@ -6607,6 +6630,17 @@ pub const TypeInfo = union(TypeId) {
expression passed as an argument. The expression is evaluated.
+ {#header_close#}
+
+ {#header_open|@Vector#}
+ {#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}
+
+ This function returns a vector type for {#link|SIMD#}.
+
+
+ {#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a
+ {#link|pointer|Pointers#}.
+
{#header_close#}
{#header_close#}
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
index aa00bb876d..fa31343902 100644
--- a/src-self-hosted/type.zig
+++ b/src-self-hosted/type.zig
@@ -44,6 +44,7 @@ pub const Type = struct {
Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp),
Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp),
Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp),
+ Id.Vector => @fieldParentPtr(Vector, "base", base).destroy(comp),
}
}
@@ -77,6 +78,7 @@ pub const Type = struct {
Id.ArgTuple => unreachable,
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
+ Id.Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context),
}
}
@@ -103,6 +105,7 @@ pub const Type = struct {
Id.Enum,
Id.Fn,
Id.Promise,
+ Id.Vector,
=> return false,
Id.Struct => @panic("TODO"),
@@ -135,6 +138,7 @@ pub const Type = struct {
Id.Float,
Id.Fn,
Id.Promise,
+ Id.Vector,
=> return true,
Id.Pointer => {
@@ -902,6 +906,18 @@ pub const Type = struct {
}
};
+ pub const Vector = struct {
+ base: Type,
+
+ pub fn destroy(self: *Vector, comp: *Compilation) void {
+ comp.gpa().destroy(self);
+ }
+
+ pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ @panic("TODO");
+ }
+ };
+
pub const ComptimeFloat = struct {
base: Type,
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 4b134361a3..f6fe72891d 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -252,6 +252,10 @@ struct ConstArgTuple {
size_t end_index;
};
+struct ConstVector {
+ ConstExprValue *elements;
+};
+
enum ConstValSpecial {
ConstValSpecialRuntime,
ConstValSpecialStatic,
@@ -318,6 +322,7 @@ struct ConstExprValue {
ConstPtrValue x_ptr;
ImportTableEntry *x_import;
ConstArgTuple x_arg_tuple;
+ ConstVector x_vector;
// populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union;
@@ -1210,6 +1215,12 @@ struct ZigTypePromise {
ZigType *result_type;
};
+struct ZigTypeVector {
+ // The type must be a pointer, integer, or float
+ ZigType *elem_type;
+ uint32_t len;
+};
+
enum ZigTypeId {
ZigTypeIdInvalid,
ZigTypeIdMetaType,
@@ -1236,6 +1247,7 @@ enum ZigTypeId {
ZigTypeIdArgTuple,
ZigTypeIdOpaque,
ZigTypeIdPromise,
+ ZigTypeIdVector,
};
struct ZigType {
@@ -1262,6 +1274,7 @@ struct ZigType {
ZigTypeFn fn;
ZigTypeBoundFn bound_fn;
ZigTypePromise promise;
+ ZigTypeVector vector;
} data;
// use these fields to make sure we don't duplicate type table entries for the same type
@@ -1415,6 +1428,7 @@ enum BuiltinFnId {
BuiltinFnIdEnumToInt,
BuiltinFnIdIntToEnum,
BuiltinFnIdIntType,
+ BuiltinFnIdVectorType,
BuiltinFnIdSetCold,
BuiltinFnIdSetRuntimeSafety,
BuiltinFnIdSetFloatMode,
@@ -1505,6 +1519,10 @@ struct TypeId {
ZigType *err_set_type;
ZigType *payload_type;
} error_union;
+ struct {
+ ZigType *elem_type;
+ uint32_t len;
+ } vector;
} data;
};
@@ -2139,6 +2157,7 @@ enum IrInstructionId {
IrInstructionIdFloatToInt,
IrInstructionIdBoolToInt,
IrInstructionIdIntType,
+ IrInstructionIdVectorType,
IrInstructionIdBoolNot,
IrInstructionIdMemset,
IrInstructionIdMemcpy,
@@ -2807,6 +2826,13 @@ struct IrInstructionIntType {
IrInstruction *bit_count;
};
+struct IrInstructionVectorType {
+ IrInstruction base;
+
+ IrInstruction *len;
+ IrInstruction *elem_type;
+};
+
struct IrInstructionBoolNot {
IrInstruction base;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 194888068c..99378eb7a8 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -250,6 +250,7 @@ AstNode *type_decl_node(ZigType *type_entry) {
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return nullptr;
}
zig_unreachable();
@@ -311,6 +312,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return true;
}
zig_unreachable();
@@ -1055,11 +1057,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
}
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
- if (abi_class == X64CABIClass_MEMORY) {
- return true;
- }
- zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481",
- buf_ptr(&fn_type_id->return_type->name));
+ return abi_class == X64CABIClass_MEMORY;
} else if (target_is_arm(&g->zig_target)) {
return type_size(g, fn_type_id->return_type) > 16;
}
@@ -1424,6 +1422,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdFn:
+ case ZigTypeIdVector:
return true;
case ZigTypeIdStruct:
return type_entry->data.structure.layout == ContainerLayoutPacked;
@@ -1472,6 +1471,8 @@ static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
default:
return false;
}
+ case ZigTypeIdVector:
+ return type_allowed_in_extern(g, type_entry->data.vector.elem_type);
case ZigTypeIdFloat:
return true;
case ZigTypeIdArray:
@@ -1625,6 +1626,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
switch (type_requires_comptime(g, type_entry)) {
case ReqCompTimeNo:
break;
@@ -1720,6 +1722,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
switch (type_requires_comptime(g, fn_type_id.return_type)) {
case ReqCompTimeInvalid:
return g->builtin_types.entry_invalid;
@@ -3577,6 +3580,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return type_entry;
}
zig_unreachable();
@@ -3943,6 +3947,7 @@ static bool is_container(ZigType *type_entry) {
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return false;
}
zig_unreachable();
@@ -4002,6 +4007,7 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
zig_unreachable();
}
}
@@ -4451,6 +4457,34 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
return new_entry;
}
+ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
+ TypeId type_id = {};
+ type_id.id = ZigTypeIdVector;
+ type_id.data.vector.len = len;
+ type_id.data.vector.elem_type = elem_type;
+
+ {
+ auto entry = g->type_table.maybe_get(type_id);
+ if (entry)
+ return entry->value;
+ }
+
+ ZigType *entry = new_type_table_entry(ZigTypeIdVector);
+ entry->zero_bits = (len == 0) || !type_has_bits(elem_type);
+ entry->type_ref = entry->zero_bits ? LLVMVoidType() : LLVMVectorType(elem_type->type_ref, len);
+ entry->data.vector.len = len;
+ entry->data.vector.elem_type = elem_type;
+
+ buf_resize(&entry->name, 0);
+ buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
+
+ entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, len,
+ LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type);
+
+ g->type_table.put(type_id, entry);
+ return entry;
+}
+
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
return &g->builtin_types.entry_c_int[c_int_type];
}
@@ -4482,6 +4516,7 @@ bool handle_is_ptr(ZigType *type_entry) {
case ZigTypeIdFn:
case ZigTypeIdEnum:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
return false;
case ZigTypeIdArray:
case ZigTypeIdStruct:
@@ -4914,6 +4949,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
return hash_const_val_error_set(const_val);
case ZigTypeIdNamespace:
return hash_ptr(const_val->data.x_import);
+ case ZigTypeIdVector:
+ // TODO better hashing algorithm
+ return 3647867726;
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -4966,6 +5004,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
case ZigTypeIdBool:
case ZigTypeIdUnreachable:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
@@ -5049,6 +5088,7 @@ static bool return_type_is_cacheable(ZigType *return_type) {
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdPointer:
+ case ZigTypeIdVector:
return true;
case ZigTypeIdArray:
@@ -5201,6 +5241,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes;
case ZigTypeIdPointer:
return type_has_one_possible_value(g, type_entry->data.pointer.child_type);
@@ -5251,6 +5292,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
case ZigTypeIdErrorSet:
case ZigTypeIdBool:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdVoid:
case ZigTypeIdUnreachable:
@@ -5777,7 +5819,7 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
- for (size_t i = 0; i < len; ++i) {
+ for (size_t i = 0; i < len; i += 1) {
if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
return false;
}
@@ -5811,6 +5853,20 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
+ case ZigTypeIdVector: {
+ assert(a->type->data.vector.len == b->type->data.vector.len);
+
+ size_t len = a->type->data.vector.len;
+ ConstExprValue *a_elems = a->data.x_vector.elements;
+ ConstExprValue *b_elems = b->data.x_vector.elements;
+
+ for (size_t i = 0; i < len; i += 1) {
+ if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
+ return false;
+ }
+
+ return true;
+ }
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -6042,6 +6098,18 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
}
zig_unreachable();
+ case ZigTypeIdVector: {
+ buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
+ uint64_t len = type_entry->data.vector.len;
+ for (uint32_t i = 0; i < len; i += 1) {
+ if (i != 0)
+ buf_appendf(buf, ",");
+ ConstExprValue *child_value = &const_val->data.x_vector.elements[i];
+ render_const_value(g, buf, child_value);
+ }
+ buf_appendf(buf, "}");
+ return;
+ }
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
@@ -6200,6 +6268,8 @@ uint32_t type_id_hash(TypeId x) {
case ZigTypeIdInt:
return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) +
(((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557);
+ case ZigTypeIdVector:
+ return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681);
}
zig_unreachable();
}
@@ -6248,6 +6318,9 @@ bool type_id_eql(TypeId a, TypeId b) {
case ZigTypeIdInt:
return a.data.integer.is_signed == b.data.integer.is_signed &&
a.data.integer.bit_count == b.data.integer.bit_count;
+ case ZigTypeIdVector:
+ return a.data.vector.elem_type == b.data.vector.elem_type &&
+ a.data.vector.len == b.data.vector.len;
}
zig_unreachable();
}
@@ -6382,6 +6455,7 @@ static const ZigTypeId all_type_ids[] = {
ZigTypeIdArgTuple,
ZigTypeIdOpaque,
ZigTypeIdPromise,
+ ZigTypeIdVector,
};
ZigTypeId type_id_at_index(size_t index) {
@@ -6447,6 +6521,8 @@ size_t type_id_index(ZigType *entry) {
return 22;
case ZigTypeIdPromise:
return 23;
+ case ZigTypeIdVector:
+ return 24;
}
zig_unreachable();
}
@@ -6503,6 +6579,8 @@ const char *type_id_name(ZigTypeId id) {
return "Opaque";
case ZigTypeIdPromise:
return "Promise";
+ case ZigTypeIdVector:
+ return "Vector";
}
zig_unreachable();
}
@@ -6658,6 +6736,7 @@ X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
case ZigTypeIdBool:
return X64CABIClass_INTEGER;
case ZigTypeIdFloat:
+ case ZigTypeIdVector:
return X64CABIClass_SSE;
case ZigTypeIdStruct: {
// "If the size of an object is larger than four eightbytes, or it contains unaligned
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 1bac15ebcc..da5265a594 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -20,6 +20,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
uint64_t type_size(CodeGen *g, ZigType *type_entry);
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
+ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type);
ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 2f360735dd..e2576f5335 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1980,7 +1980,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
break;
}
- if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
+ if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector ||
ty->id == ZigTypeIdInt // TODO investigate if we need to change this
) {
switch (fn_walk->id) {
@@ -2660,6 +2660,27 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
} else {
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
}
+ } else if (type_entry->id == ZigTypeIdVector) {
+ ZigType *elem_type = type_entry->data.vector.elem_type;
+ if (elem_type->id == ZigTypeIdFloat) {
+ ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
+ return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
+ } else if (elem_type->id == ZigTypeIdPointer) {
+ zig_panic("TODO codegen for pointers in vectors");
+ } else if (elem_type->id == ZigTypeIdInt) {
+ bool is_wrapping = (op_id == IrBinOpAddWrap);
+ if (is_wrapping) {
+ return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
+ } else if (want_runtime_safety) {
+ zig_panic("TODO runtime safety for vector integer addition");
+ } else if (elem_type->data.integral.is_signed) {
+ return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
+ } else {
+ return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
+ }
+ } else {
+ zig_unreachable();
+ }
} else {
zig_unreachable();
}
@@ -5211,6 +5232,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdCUndef:
case IrInstructionIdEmbedFile:
case IrInstructionIdIntType:
+ case IrInstructionIdVectorType:
case IrInstructionIdMemberCount:
case IrInstructionIdMemberType:
case IrInstructionIdMemberName:
@@ -5620,6 +5642,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
}
case ZigTypeIdArray:
zig_panic("TODO bit pack an array");
+ case ZigTypeIdVector:
+ zig_panic("TODO bit pack a vector");
case ZigTypeIdUnion:
zig_panic("TODO bit pack a union");
case ZigTypeIdStruct:
@@ -5992,6 +6016,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
}
}
}
+ case ZigTypeIdVector: {
+ uint32_t len = type_entry->data.vector.len;
+ LLVMValueRef *values = allocate(len);
+ for (uint32_t i = 0; i < len; i += 1) {
+ values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], "");
+ }
+ return LLVMConstVector(values, len);
+ }
case ZigTypeIdUnion:
{
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
@@ -6927,6 +6959,7 @@ static void define_builtin_fns(CodeGen *g) {
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
+ create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2);
create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1);
create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1);
create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1);
@@ -7152,6 +7185,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" ArgTuple: void,\n"
" Opaque: void,\n"
" Promise: Promise,\n"
+ " Vector: Vector,\n"
"\n\n"
" pub const Int = struct {\n"
" is_signed: bool,\n"
@@ -7270,6 +7304,11 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" child: ?type,\n"
" };\n"
"\n"
+ " pub const Vector = struct {\n"
+ " len: u32,\n"
+ " child: type,\n"
+ " };\n"
+ "\n"
" pub const Definition = struct {\n"
" name: []const u8,\n"
" is_pub: bool,\n"
@@ -7841,6 +7880,9 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
case ZigTypeIdArray:
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
return;
+ case ZigTypeIdVector:
+ prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type);
+ return;
case ZigTypeIdOptional:
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
return;
@@ -7972,6 +8014,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
buf_appendf(out_buf, "%s", buf_ptr(child_buf));
return;
}
+ case ZigTypeIdVector:
+ zig_panic("TODO implement get_c_type for vector types");
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
@@ -8137,6 +8181,7 @@ static void gen_h_file(CodeGen *g) {
case ZigTypeIdOptional:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
zig_unreachable();
case ZigTypeIdEnum:
if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 06eb4a47f1..d5152f3c85 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -587,6 +587,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) {
return IrInstructionIdIntType;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) {
+ return IrInstructionIdVectorType;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
return IrInstructionIdBoolNot;
}
@@ -1953,6 +1957,19 @@ static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *s
return &instruction->base;
}
+static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len,
+ IrInstruction *elem_type)
+{
+ IrInstructionVectorType *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->len = len;
+ instruction->elem_type = elem_type;
+
+ ir_ref_instruction(len, irb->current_basic_block);
+ ir_ref_instruction(elem_type, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node);
instruction->value = value;
@@ -4230,6 +4247,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, int_type, lval);
}
+ case BuiltinFnIdVectorType:
+ {
+ 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 *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
+ return ir_lval_wrap(irb, scope, vector_type, lval);
+ }
case BuiltinFnIdMemcpy:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -11617,6 +11649,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
case ZigTypeIdComptimeInt:
case ZigTypeIdInt:
case ZigTypeIdFloat:
+ case ZigTypeIdVector:
operator_allowed = true;
break;
@@ -12032,6 +12065,48 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b
return result;
}
+static bool ok_float_op(IrBinOp op) {
+ switch (op) {
+ case IrBinOpInvalid:
+ zig_unreachable();
+ case IrBinOpAdd:
+ case IrBinOpSub:
+ case IrBinOpMult:
+ case IrBinOpDivUnspecified:
+ case IrBinOpDivTrunc:
+ case IrBinOpDivFloor:
+ case IrBinOpDivExact:
+ case IrBinOpRemRem:
+ case IrBinOpRemMod:
+ return true;
+
+ case IrBinOpBoolOr:
+ case IrBinOpBoolAnd:
+ case IrBinOpCmpEq:
+ case IrBinOpCmpNotEq:
+ case IrBinOpCmpLessThan:
+ case IrBinOpCmpGreaterThan:
+ case IrBinOpCmpLessOrEq:
+ case IrBinOpCmpGreaterOrEq:
+ case IrBinOpBinOr:
+ case IrBinOpBinXor:
+ case IrBinOpBinAnd:
+ case IrBinOpBitShiftLeftLossy:
+ case IrBinOpBitShiftLeftExact:
+ case IrBinOpBitShiftRightLossy:
+ case IrBinOpBitShiftRightExact:
+ case IrBinOpAddWrap:
+ case IrBinOpSubWrap:
+ case IrBinOpMultWrap:
+ case IrBinOpRemUnspecified:
+ case IrBinOpArrayCat:
+ case IrBinOpArrayMult:
+ case IrBinOpMergeErrorSets:
+ return false;
+ }
+ zig_unreachable();
+}
+
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
@@ -12169,21 +12244,20 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
op_id = IrBinOpRemRem;
}
+ bool ok = false;
if (is_int) {
- // int
- } else if (is_float &&
- (op_id == IrBinOpAdd ||
- op_id == IrBinOpSub ||
- op_id == IrBinOpMult ||
- op_id == IrBinOpDivUnspecified ||
- op_id == IrBinOpDivTrunc ||
- op_id == IrBinOpDivFloor ||
- op_id == IrBinOpDivExact ||
- op_id == IrBinOpRemRem ||
- op_id == IrBinOpRemMod))
- {
- // float
- } else {
+ ok = true;
+ } else if (is_float && ok_float_op(op_id)) {
+ ok = true;
+ } else if (resolved_type->id == ZigTypeIdVector) {
+ ZigType *elem_type = resolved_type->data.vector.elem_type;
+ if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) {
+ ok = true;
+ } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) {
+ ok = true;
+ }
+ }
+ if (!ok) {
AstNode *source_node = instruction->base.source_node;
ir_add_error_node(ira, source_node,
buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
@@ -12817,6 +12891,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdBool:
+ case ZigTypeIdVector:
break;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
@@ -12851,6 +12926,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
+ case ZigTypeIdVector:
zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
case ZigTypeIdNamespace:
case ZigTypeIdBoundFn:
@@ -14009,6 +14085,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
+ case ZigTypeIdVector:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
@@ -15383,37 +15460,7 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio
ZigType *type_entry = expr_value->value.type;
if (type_is_invalid(type_entry))
return ira->codegen->invalid_instruction;
- switch (type_entry->id) {
- case ZigTypeIdInvalid:
- zig_unreachable(); // handled above
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- case ZigTypeIdNamespace:
- case ZigTypeIdBoundFn:
- case ZigTypeIdMetaType:
- case ZigTypeIdVoid:
- case ZigTypeIdBool:
- case ZigTypeIdUnreachable:
- case ZigTypeIdInt:
- case ZigTypeIdFloat:
- case ZigTypeIdPointer:
- case ZigTypeIdArray:
- case ZigTypeIdStruct:
- case ZigTypeIdOptional:
- case ZigTypeIdErrorUnion:
- case ZigTypeIdErrorSet:
- case ZigTypeIdEnum:
- case ZigTypeIdUnion:
- case ZigTypeIdFn:
- case ZigTypeIdArgTuple:
- case ZigTypeIdOpaque:
- case ZigTypeIdPromise:
- return ir_const_type(ira, &typeof_instruction->base, type_entry);
- }
-
- zig_unreachable();
+ return ir_const_type(ira, &typeof_instruction->base, type_entry);
}
static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
@@ -15652,6 +15699,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
case ZigTypeIdNamespace:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
{
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
@@ -15772,6 +15820,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
case ZigTypeIdNamespace:
case ZigTypeIdBoundFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
{
if ((err = ensure_complete_type(ira->codegen, child_type)))
return ira->codegen->invalid_instruction;
@@ -15838,6 +15887,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdPromise:
+ case ZigTypeIdVector:
{
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes);
@@ -16307,6 +16357,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
case ZigTypeIdBoundFn:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
+ case ZigTypeIdVector:
ir_add_error(ira, &switch_target_instruction->base,
buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
return ira->codegen->invalid_instruction;
@@ -17496,6 +17547,27 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE
break;
}
+ case ZigTypeIdVector: {
+ result = create_const_vals(1);
+ result->special = ConstValSpecialStatic;
+ result->type = ir_type_info_get_type(ira, "Vector", nullptr);
+
+ ConstExprValue *fields = create_const_vals(2);
+ result->data.x_struct.fields = fields;
+
+ // len: usize
+ ensure_field_index(result->type, "len", 0);
+ fields[0].special = ConstValSpecialStatic;
+ fields[0].type = ira->codegen->builtin_types.entry_u32;
+ bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len);
+ // child: type
+ ensure_field_index(result->type, "child", 1);
+ fields[1].special = ConstValSpecialStatic;
+ fields[1].type = ira->codegen->builtin_types.entry_type;
+ fields[1].data.x_type = type_entry->data.vector.elem_type;
+
+ break;
+ }
case ZigTypeIdOptional:
{
result = create_const_vals(1);
@@ -18671,6 +18743,30 @@ static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruct
return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count));
}
+static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) {
+ uint64_t len;
+ if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len))
+ return ira->codegen->invalid_instruction;
+
+ ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child);
+ if (type_is_invalid(elem_type))
+ return ira->codegen->invalid_instruction;
+
+ if (elem_type->id != ZigTypeIdInt &&
+ elem_type->id != ZigTypeIdFloat &&
+ get_codegen_ptr_type(elem_type) == nullptr)
+ {
+ ir_add_error(ira, instruction->elem_type,
+ buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
+ buf_ptr(&elem_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type);
+
+ return ir_const_type(ira, &instruction->base, vector_type);
+}
+
static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) {
IrInstruction *value = instruction->value->child;
if (type_is_invalid(value->value.type))
@@ -19474,6 +19570,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
+ case ZigTypeIdVector:
{
uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry);
return ir_const_unsigned(ira, &instruction->base, align_in_bytes);
@@ -20311,6 +20408,15 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
}
}
return;
+ case ZigTypeIdVector: {
+ size_t buf_i = 0;
+ for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) {
+ ConstExprValue *elem = &val->data.x_vector.elements[elem_i];
+ buf_write_value_bytes(codegen, &buf[buf_i], elem);
+ buf_i += type_size(codegen, elem->type);
+ }
+ return;
+ }
case ZigTypeIdStruct:
zig_panic("TODO buf_write_value_bytes struct type");
case ZigTypeIdOptional:
@@ -20387,6 +20493,20 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
}
zig_unreachable();
}
+ case ZigTypeIdVector: {
+ uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type);
+ uint32_t len = val->type->data.vector.len;
+
+ val->data.x_vector.elements = create_const_vals(len);
+ for (uint32_t i = 0; i < len; i += 1) {
+ ConstExprValue *elem = &val->data.x_vector.elements[i];
+ elem->special = ConstValSpecialStatic;
+ elem->type = val->type->data.vector.elem_type;
+ if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
+ return err;
+ }
+ return ErrorNone;
+ }
case ZigTypeIdEnum:
switch (val->type->data.enumeration.layout) {
case ContainerLayoutAuto:
@@ -21633,6 +21753,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction);
case IrInstructionIdIntType:
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
+ case IrInstructionIdVectorType:
+ return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction);
case IrInstructionIdBoolNot:
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
case IrInstructionIdMemset:
@@ -21943,6 +22065,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdEmbedFile:
case IrInstructionIdTruncate:
case IrInstructionIdIntType:
+ case IrInstructionIdVectorType:
case IrInstructionIdBoolNot:
case IrInstructionIdSlice:
case IrInstructionIdMemberCount:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index a3ec8e9d35..a1fd450b65 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -719,6 +719,14 @@ static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
fprintf(irp->f, ")");
}
+static void ir_print_vector_type(IrPrint *irp, IrInstructionVectorType *instruction) {
+ fprintf(irp->f, "@Vector(");
+ ir_print_other_instruction(irp, instruction->len);
+ fprintf(irp->f, ", ");
+ ir_print_other_instruction(irp, instruction->elem_type);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) {
fprintf(irp->f, "! ");
ir_print_other_instruction(irp, instruction->value);
@@ -1577,6 +1585,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdIntType:
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
break;
+ case IrInstructionIdVectorType:
+ ir_print_vector_type(irp, (IrInstructionVectorType *)instruction);
+ break;
case IrInstructionIdBoolNot:
ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction);
break;
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 3c01a0954d..65f6b5abb7 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch
return reinterpret_cast(di_type);
}
+struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
+ uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty)
+{
+ SmallVector subrange;
+ subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, Size));
+ DIType *di_type = reinterpret_cast(dibuilder)->createVectorType(
+ Size,
+ AlignInBits,
+ reinterpret_cast(Ty),
+ reinterpret_cast(dibuilder)->getOrCreateArray(subrange));
+ return reinterpret_cast(di_type);
+}
+
ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits,
uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count)
{
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index 551a4a7448..26dca1198b 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -191,6 +191,9 @@ ZIG_EXTERN_C struct ZigLLVMDISubprogram *ZigLLVMCreateFunction(struct ZigLLVMDIB
unsigned lineno, struct ZigLLVMDIType *fn_di_type, bool is_local_to_unit, bool is_definition,
unsigned scope_line, unsigned flags, bool is_optimized, struct ZigLLVMDISubprogram *decl_subprogram);
+ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
+ uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty);
+
ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubprogram *subprogram);
ZIG_EXTERN_C void ZigLLVMDIBuilderFinalize(struct ZigLLVMDIBuilder *dibuilder);
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 99237047e0..a63a549814 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -508,6 +508,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
+ builtin.TypeId.Vector => @compileError("TODO auto hash for vectors"),
builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
@@ -555,5 +556,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
+ builtin.TypeId.Vector => @compileError("TODO auto eql for vectors"),
}
}
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index 5ad80e06e1..f3bb17b282 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -171,11 +171,11 @@ fn testUnion() void {
assertOrPanic(TypeId(typeinfo_info) == TypeId.Union);
assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId);
- assertOrPanic(typeinfo_info.Union.fields.len == 24);
+ assertOrPanic(typeinfo_info.Union.fields.len == 25);
assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null);
assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
- assertOrPanic(typeinfo_info.Union.defs.len == 20);
+ assertOrPanic(typeinfo_info.Union.defs.len == 21);
const TestNoTagUnion = union {
Foo: void,
@@ -262,3 +262,15 @@ test "typeInfo with comptime parameter in struct fn def" {
};
comptime var info = @typeInfo(S);
}
+
+test "type info: vectors" {
+ testVector();
+ comptime testVector();
+}
+
+fn testVector() void {
+ const vec_info = @typeInfo(@Vector(4, i32));
+ assertOrPanic(TypeId(vec_info) == TypeId.Vector);
+ assertOrPanic(vec_info.Vector.len == 4);
+ assertOrPanic(vec_info.Vector.child == i32);
+}
--
cgit v1.2.3
From 8c6fa982cd0a02775264b616c37da9907cc603bb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 4 Feb 2019 20:30:00 -0500
Subject: SIMD: array to vector, vector to array, wrapping int add
also vectors and arrays now use the same ConstExprVal representation
See #903
---
src/all_types.hpp | 20 ++-
src/analyze.cpp | 178 ++++++++++++------------
src/analyze.hpp | 1 +
src/codegen.cpp | 98 +++++++++++---
src/ir.cpp | 291 +++++++++++++++++++++++++---------------
src/ir_print.cpp | 18 +++
test/stage1/behavior.zig | 1 +
test/stage1/behavior/vector.zig | 20 +++
8 files changed, 403 insertions(+), 224 deletions(-)
create mode 100644 test/stage1/behavior/vector.zig
(limited to 'src/analyze.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 3fc6772b31..c4c9e13cfb 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -252,10 +252,6 @@ struct ConstArgTuple {
size_t end_index;
};
-struct ConstVector {
- ConstExprValue *elements;
-};
-
enum ConstValSpecial {
ConstValSpecialRuntime,
ConstValSpecialStatic,
@@ -322,7 +318,6 @@ struct ConstExprValue {
ConstPtrValue x_ptr;
ImportTableEntry *x_import;
ConstArgTuple x_arg_tuple;
- ConstVector x_vector;
// populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union;
@@ -2239,6 +2234,8 @@ enum IrInstructionId {
IrInstructionIdToBytes,
IrInstructionIdFromBytes,
IrInstructionIdCheckRuntimeScope,
+ IrInstructionIdVectorToArray,
+ IrInstructionIdArrayToVector,
};
struct IrInstruction {
@@ -3368,6 +3365,19 @@ struct IrInstructionBitReverse {
IrInstruction *op;
};
+struct IrInstructionArrayToVector {
+ IrInstruction base;
+
+ IrInstruction *array;
+};
+
+struct IrInstructionVectorToArray {
+ IrInstruction base;
+
+ IrInstruction *vector;
+ LLVMValueRef tmp_ptr;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 99378eb7a8..ff961a7044 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4457,7 +4457,15 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) {
return new_entry;
}
+bool is_valid_vector_elem_type(ZigType *elem_type) {
+ return elem_type->id == ZigTypeIdInt ||
+ elem_type->id == ZigTypeIdFloat ||
+ get_codegen_ptr_type(elem_type) != nullptr;
+}
+
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
+ assert(is_valid_vector_elem_type(elem_type));
+
TypeId type_id = {};
type_id.id = ZigTypeIdVector;
type_id.data.vector.len = len;
@@ -5749,6 +5757,28 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
zig_unreachable();
}
+static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) {
+ assert(a->data.x_array.special != ConstArraySpecialUndef);
+ assert(b->data.x_array.special != ConstArraySpecialUndef);
+ if (a->data.x_array.special == ConstArraySpecialBuf &&
+ b->data.x_array.special == ConstArraySpecialBuf)
+ {
+ return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
+ }
+ expand_undef_array(g, a);
+ expand_undef_array(g, b);
+
+ ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
+ ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
+
+ for (size_t i = 0; i < len; i += 1) {
+ if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
+ return false;
+ }
+
+ return true;
+}
+
bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
assert(a->type->id == b->type->id);
assert(a->special == ConstValSpecialStatic);
@@ -5803,28 +5833,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdPointer:
case ZigTypeIdFn:
return const_values_equal_ptr(a, b);
+ case ZigTypeIdVector:
+ assert(a->type->data.vector.len == b->type->data.vector.len);
+ return const_values_equal_array(g, a, b, a->type->data.vector.len);
case ZigTypeIdArray: {
assert(a->type->data.array.len == b->type->data.array.len);
- assert(a->data.x_array.special != ConstArraySpecialUndef);
- assert(b->data.x_array.special != ConstArraySpecialUndef);
- if (a->data.x_array.special == ConstArraySpecialBuf &&
- b->data.x_array.special == ConstArraySpecialBuf)
- {
- return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf);
- }
- expand_undef_array(g, a);
- expand_undef_array(g, b);
-
- size_t len = a->type->data.array.len;
- ConstExprValue *a_elems = a->data.x_array.data.s_none.elements;
- ConstExprValue *b_elems = b->data.x_array.data.s_none.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
+ return const_values_equal_array(g, a, b, a->type->data.array.len);
}
case ZigTypeIdStruct:
for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) {
@@ -5853,20 +5867,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
case ZigTypeIdArgTuple:
return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index &&
a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index;
- case ZigTypeIdVector: {
- assert(a->type->data.vector.len == b->type->data.vector.len);
-
- size_t len = a->type->data.vector.len;
- ConstExprValue *a_elems = a->data.x_vector.elements;
- ConstExprValue *b_elems = b->data.x_vector.elements;
-
- for (size_t i = 0; i < len; i += 1) {
- if (!const_values_equal(g, &a_elems[i], &b_elems[i]))
- return false;
- }
-
- return true;
- }
case ZigTypeIdBoundFn:
case ZigTypeIdInvalid:
case ZigTypeIdUnreachable:
@@ -5985,6 +5985,40 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const
}
}
+static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ buf_append_str(buf, "undefined");
+ return;
+ case ConstArraySpecialBuf: {
+ Buf *array_buf = const_val->data.x_array.data.s_buf;
+ buf_append_char(buf, '"');
+ for (size_t i = 0; i < buf_len(array_buf); i += 1) {
+ uint8_t c = buf_ptr(array_buf)[i];
+ if (c == '"') {
+ buf_append_str(buf, "\\\"");
+ } else {
+ buf_append_char(buf, c);
+ }
+ }
+ buf_append_char(buf, '"');
+ return;
+ }
+ case ConstArraySpecialNone: {
+ buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name));
+ for (uint64_t i = 0; i < len; i += 1) {
+ if (i != 0)
+ buf_appendf(buf, ",");
+ ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
+ render_const_value(g, buf, child_value);
+ }
+ buf_appendf(buf, "}");
+ return;
+ }
+ }
+ zig_unreachable();
+}
+
void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -6065,51 +6099,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
case ZigTypeIdPointer:
return render_const_val_ptr(g, buf, const_val, type_entry);
+ case ZigTypeIdVector:
+ return render_const_val_array(g, buf, const_val, type_entry->data.vector.len);
case ZigTypeIdArray:
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialUndef:
- buf_append_str(buf, "undefined");
- return;
- case ConstArraySpecialBuf: {
- Buf *array_buf = const_val->data.x_array.data.s_buf;
- buf_append_char(buf, '"');
- for (size_t i = 0; i < buf_len(array_buf); i += 1) {
- uint8_t c = buf_ptr(array_buf)[i];
- if (c == '"') {
- buf_append_str(buf, "\\\"");
- } else {
- buf_append_char(buf, c);
- }
- }
- buf_append_char(buf, '"');
- return;
- }
- case ConstArraySpecialNone: {
- buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
- uint64_t len = type_entry->data.array.len;
- for (uint64_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
- render_const_value(g, buf, child_value);
- }
- buf_appendf(buf, "}");
- return;
- }
- }
- zig_unreachable();
- case ZigTypeIdVector: {
- buf_appendf(buf, "%s{", buf_ptr(&type_entry->name));
- uint64_t len = type_entry->data.vector.len;
- for (uint32_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_vector.elements[i];
- render_const_value(g, buf, child_value);
- }
- buf_appendf(buf, "}");
- return;
- }
+ return render_const_val_array(g, buf, const_val, type_entry->data.array.len);
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
@@ -6379,7 +6372,17 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
// Canonicalize the array value as ConstArraySpecialNone
void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
- assert(const_val->type->id == ZigTypeIdArray);
+ size_t elem_count;
+ ZigType *elem_type;
+ if (const_val->type->id == ZigTypeIdArray) {
+ elem_count = const_val->type->data.array.len;
+ elem_type = const_val->type->data.array.child_type;
+ } else if (const_val->type->id == ZigTypeIdVector) {
+ elem_count = const_val->type->data.vector.len;
+ elem_type = const_val->type->data.vector.elem_type;
+ } else {
+ zig_unreachable();
+ }
if (const_val->special == ConstValSpecialUndef) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_array.special = ConstArraySpecialUndef;
@@ -6389,18 +6392,14 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
return;
case ConstArraySpecialUndef: {
const_val->data.x_array.special = ConstArraySpecialNone;
- size_t elem_count = const_val->type->data.array.len;
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i];
- element_val->type = const_val->type->data.array.child_type;
+ element_val->type = elem_type;
init_const_undefined(g, element_val);
- ConstParent *parent = get_const_val_parent(g, element_val);
- if (parent != nullptr) {
- parent->id = ConstParentIdArray;
- parent->data.p_array.array_val = const_val;
- parent->data.p_array.elem_index = i;
- }
+ element_val->parent.id = ConstParentIdArray;
+ element_val->parent.data.p_array.array_val = const_val;
+ element_val->parent.data.p_array.elem_index = i;
}
return;
}
@@ -6411,7 +6410,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
g->string_literals_table.maybe_remove(buf);
const_val->data.x_array.special = ConstArraySpecialNone;
- size_t elem_count = const_val->type->data.array.len;
assert(elem_count == buf_len(buf));
const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count);
for (size_t i = 0; i < elem_count; i += 1) {
@@ -6419,6 +6417,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
this_char->special = ConstValSpecialStatic;
this_char->type = g->builtin_types.entry_u8;
bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]);
+ this_char->parent.id = ConstParentIdArray;
+ this_char->parent.data.p_array.array_val = const_val;
+ this_char->parent.data.p_array.elem_index = i;
}
return;
}
@@ -6426,6 +6427,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) {
zig_unreachable();
}
+// Deprecated. Reference the parent field directly.
ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
return &value->parent;
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index da5265a594..f558fa44b0 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -74,6 +74,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag);
bool is_ref(ZigType *type_entry);
bool is_array_ref(ZigType *type_entry);
bool is_container_ref(ZigType *type_entry);
+bool is_valid_vector_elem_type(ZigType *elem_type);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void scan_import(CodeGen *g, ImportTableEntry *import);
void preview_use_decl(CodeGen *g, AstNode *node);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index b73fda59d1..de2222afb7 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1921,9 +1921,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
}
static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
- assert(alignment > 0);
LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
- LLVMSetAlignment(result, alignment);
+ LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment);
return result;
}
@@ -3246,6 +3245,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
}
+static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) {
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return true;
+ case ConstArraySpecialBuf:
+ return false;
+ case ConstArraySpecialNone:
+ for (size_t i = 0; i < len; i += 1) {
+ if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
+ return false;
+ }
+ return true;
+ }
+ zig_unreachable();
+}
+
static bool value_is_all_undef(ConstExprValue *const_val) {
switch (const_val->special) {
case ConstValSpecialRuntime:
@@ -3260,19 +3275,9 @@ static bool value_is_all_undef(ConstExprValue *const_val) {
}
return true;
} else if (const_val->type->id == ZigTypeIdArray) {
- switch (const_val->data.x_array.special) {
- case ConstArraySpecialUndef:
- return true;
- case ConstArraySpecialBuf:
- return false;
- case ConstArraySpecialNone:
- for (size_t i = 0; i < const_val->type->data.array.len; i += 1) {
- if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i]))
- return false;
- }
- return true;
- }
- zig_unreachable();
+ return value_is_all_undef_array(const_val, const_val->type->data.array.len);
+ } else if (const_val->type->id == ZigTypeIdVector) {
+ return value_is_all_undef_array(const_val, const_val->type->data.vector.len);
} else {
return false;
}
@@ -5194,6 +5199,32 @@ static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable,
return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
}
+static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable,
+ IrInstructionVectorToArray *instruction)
+{
+ ZigType *array_type = instruction->base.value.type;
+ assert(array_type->id == ZigTypeIdArray);
+ assert(handle_is_ptr(array_type));
+ assert(instruction->tmp_ptr);
+ LLVMValueRef vector = ir_llvm_value(g, instruction->vector);
+ LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr,
+ LLVMPointerType(instruction->vector->value.type->type_ref, 0), "");
+ gen_store_untyped(g, vector, casted_ptr, 0, false);
+ return instruction->tmp_ptr;
+}
+
+static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable,
+ IrInstructionArrayToVector *instruction)
+{
+ ZigType *vector_type = instruction->base.value.type;
+ assert(vector_type->id == ZigTypeIdVector);
+ assert(!handle_is_ptr(vector_type));
+ LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array);
+ LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr,
+ LLVMPointerType(vector_type->type_ref, 0), "");
+ return gen_load_untyped(g, casted_ptr, 0, false, "");
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -5439,6 +5470,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction);
case IrInstructionIdBitReverse:
return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction);
+ case IrInstructionIdArrayToVector:
+ return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
+ case IrInstructionIdVectorToArray:
+ return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
}
zig_unreachable();
}
@@ -6016,14 +6051,32 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true);
}
}
+ zig_unreachable();
}
case ZigTypeIdVector: {
uint32_t len = type_entry->data.vector.len;
- LLVMValueRef *values = allocate(len);
- for (uint32_t i = 0; i < len; i += 1) {
- values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], "");
+ switch (const_val->data.x_array.special) {
+ case ConstArraySpecialUndef:
+ return LLVMGetUndef(type_entry->type_ref);
+ case ConstArraySpecialNone: {
+ LLVMValueRef *values = allocate(len);
+ for (uint64_t i = 0; i < len; i += 1) {
+ ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
+ values[i] = gen_const_val(g, elem_value, "");
+ }
+ return LLVMConstVector(values, len);
+ }
+ case ConstArraySpecialBuf: {
+ Buf *buf = const_val->data.x_array.data.s_buf;
+ assert(buf_len(buf) == len);
+ LLVMValueRef *values = allocate(len);
+ for (uint64_t i = 0; i < len; i += 1) {
+ values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false);
+ }
+ return LLVMConstVector(values, len);
+ }
}
- return LLVMConstVector(values, len);
+ zig_unreachable();
}
case ZigTypeIdUnion:
{
@@ -6467,6 +6520,7 @@ static void do_code_gen(CodeGen *g) {
IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i);
LLVMValueRef *slot;
ZigType *slot_type = instruction->value.type;
+ uint32_t alignment_bytes = 0;
if (instruction->id == IrInstructionIdCast) {
IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction;
slot = &cast_instruction->tmp_ptr;
@@ -6502,10 +6556,14 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdCmpxchgGen) {
IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction;
slot = &cmpxchg_instruction->tmp_ptr;
+ } else if (instruction->id == IrInstructionIdVectorToArray) {
+ IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction;
+ alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type);
+ slot = &vector_to_array_instruction->tmp_ptr;
} else {
zig_unreachable();
}
- *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type));
+ *slot = build_alloca(g, slot_type, "", alignment_bytes);
}
ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
diff --git a/src/ir.cpp b/src/ir.cpp
index 3d3c501df3..3cbbdc8103 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -168,6 +168,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
+static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -899,6 +900,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop
return IrInstructionIdCheckRuntimeScope;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) {
+ return IrInstructionIdVectorToArray;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) {
+ return IrInstructionIdArrayToVector;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2821,6 +2830,34 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope,
return &instruction->base;
}
+static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *vector, ZigType *result_type)
+{
+ IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = result_type;
+ instruction->vector = vector;
+
+ ir_ref_instruction(vector, ira->new_irb.current_basic_block);
+
+ ir_add_alloca(ira, &instruction->base, result_type);
+
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction,
+ IrInstruction *array, ZigType *result_type)
+{
+ IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value.type = result_type;
+ instruction->array = array;
+
+ ir_ref_instruction(array, ira->new_irb.current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -8270,6 +8307,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt);
bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat);
+ assert(const_val_is_int || const_val_is_float);
if (other_type->id == ZigTypeIdFloat) {
if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) {
@@ -10714,6 +10752,32 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
}
}
+static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *array, ZigType *vector_type)
+{
+ if (instr_is_comptime(array)) {
+ // arrays and vectors have the same ConstExprValue representation
+ IrInstruction *result = ir_const(ira, source_instr, vector_type);
+ copy_const_val(&result->value, &array->value, false);
+ result->value.type = vector_type;
+ return result;
+ }
+ return ir_build_array_to_vector(ira, source_instr, array, vector_type);
+}
+
+static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *vector, ZigType *array_type)
+{
+ if (instr_is_comptime(vector)) {
+ // arrays and vectors have the same ConstExprValue representation
+ IrInstruction *result = ir_const(ira, source_instr, array_type);
+ copy_const_val(&result->value, &vector->value, false);
+ result->value.type = array_type;
+ return result;
+ }
+ return ir_build_vector_to_array(ira, source_instr, vector, array_type);
+}
+
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
@@ -11102,6 +11166,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
+ // cast from @Vector(N, T) to [N]T
+ if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector &&
+ wanted_type->data.array.len == actual_type->data.vector.len &&
+ types_match_const_cast_only(ira, wanted_type->data.array.child_type,
+ actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type);
+ }
+
+ // cast from [N]T to @Vector(N, T)
+ if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector &&
+ actual_type->data.array.len == wanted_type->data.vector.len &&
+ types_match_const_cast_only(ira, actual_type->data.array.child_type,
+ wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
+ }
// cast from undefined to anything
if (actual_type->id == ZigTypeIdUndefined) {
@@ -11780,8 +11861,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
return result;
}
-static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
- IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
+static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
+ ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
{
bool is_int;
bool is_float;
@@ -11803,10 +11884,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
{
- return ErrorDivByZero;
+ return ir_add_error(ira, source_instr, buf_sprintf("division by zero"));
}
if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
- return ErrorNegativeDenominator;
+ return ir_add_error(ira, source_instr, buf_sprintf("negative denominator"));
}
switch (op_id) {
@@ -11852,7 +11933,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
BigInt orig_bigint;
bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
- return ErrorShiftedOutOneBits;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits"));
}
break;
}
@@ -11920,14 +12001,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
BigInt remainder;
bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
if (bigint_cmp_zero(&remainder) != CmpEQ) {
- return ErrorExactDivRemainder;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
} else {
float_div_trunc(out_val, op1_val, op2_val);
ConstExprValue remainder;
float_rem(&remainder, op1_val, op2_val);
if (float_cmp_zero(&remainder) != CmpEQ) {
- return ErrorExactDivRemainder;
+ return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
}
}
break;
@@ -11951,13 +12032,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val,
if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
type_entry->data.integral.is_signed))
{
- return ErrorOverflow;
+ return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow"));
}
}
out_val->type = type_entry;
out_val->special = ConstValSpecialStatic;
- return 0;
+ return nullptr;
+}
+
+// This works on operands that have already been checked to be comptime known.
+static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr,
+ ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val)
+{
+ IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
+ ConstExprValue *out_val = &result_instruction->value;
+ if (type_entry->id == ZigTypeIdVector) {
+ expand_undef_array(ira->codegen, op1_val);
+ expand_undef_array(ira->codegen, op2_val);
+ out_val->special = ConstValSpecialUndef;
+ expand_undef_array(ira->codegen, out_val);
+ size_t len = type_entry->data.vector.len;
+ ZigType *scalar_type = type_entry->data.vector.elem_type;
+ for (size_t i = 0; i < len; i += 1) {
+ ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
+ ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
+ ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
+ assert(scalar_op1_val->type == scalar_type);
+ assert(scalar_op2_val->type == scalar_type);
+ assert(scalar_out_val->type == scalar_type);
+ ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
+ scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
+ if (msg != nullptr) {
+ add_error_note(ira->codegen, msg, source_instr->source_node,
+ buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
+ return ira->codegen->invalid_instruction;
+ }
+ }
+ out_val->type = type_entry;
+ out_val->special = ConstValSpecialStatic;
+ } else {
+ if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) {
+ return ira->codegen->invalid_instruction;
+ }
+ }
+ return ir_implicit_cast(ira, result_instruction, type_entry);
}
static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
@@ -12029,24 +12148,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type);
-
- int err;
- if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) {
- if (err == ErrorOverflow) {
- ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorShiftedOutOneBits) {
- ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits"));
- return ira->codegen->invalid_instruction;
- } else {
- zig_unreachable();
- }
- return ira->codegen->invalid_instruction;
- }
-
- ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false);
- return result_instruction;
+ return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val);
} else if (op1->value.type->id == ZigTypeIdComptimeInt) {
ir_add_error(ira, &bin_op_instruction->base,
buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
@@ -12292,30 +12394,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type);
-
- int err;
- if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) {
- if (err == ErrorDivByZero) {
- ir_add_error(ira, &instruction->base, buf_sprintf("division by zero"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorOverflow) {
- ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorExactDivRemainder) {
- ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder"));
- return ira->codegen->invalid_instruction;
- } else if (err == ErrorNegativeDenominator) {
- ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator"));
- return ira->codegen->invalid_instruction;
- } else {
- zig_unreachable();
- }
- return ira->codegen->invalid_instruction;
- }
-
- ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false);
- return result_instruction;
+ return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val);
}
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
@@ -18745,10 +18824,7 @@ static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstr
if (type_is_invalid(elem_type))
return ira->codegen->invalid_instruction;
- if (elem_type->id != ZigTypeIdInt &&
- elem_type->id != ZigTypeIdFloat &&
- get_codegen_ptr_type(elem_type) == nullptr)
- {
+ if (!is_valid_vector_elem_type(elem_type)) {
ir_add_error(ira, instruction->elem_type,
buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
buf_ptr(&elem_type->name)));
@@ -20345,6 +20421,17 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
}
+static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
+ size_t buf_i = 0;
+ // TODO optimize the buf case
+ expand_undef_array(codegen, val);
+ for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
+ ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
+ buf_write_value_bytes(codegen, &buf[buf_i], elem);
+ buf_i += type_size(codegen, elem->type);
+ }
+}
+
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) {
if (val->special == ConstValSpecialUndef)
val->special = ConstValSpecialStatic;
@@ -20390,26 +20477,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
zig_unreachable();
}
case ZigTypeIdArray:
- {
- size_t buf_i = 0;
- // TODO optimize the buf case
- expand_undef_array(codegen, val);
- for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
- ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
- buf_write_value_bytes(codegen, &buf[buf_i], elem);
- buf_i += type_size(codegen, elem->type);
- }
- }
- return;
- case ZigTypeIdVector: {
- size_t buf_i = 0;
- for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) {
- ConstExprValue *elem = &val->data.x_vector.elements[elem_i];
- buf_write_value_bytes(codegen, &buf[buf_i], elem);
- buf_i += type_size(codegen, elem->type);
- }
- return;
- }
+ return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len);
+ case ZigTypeIdVector:
+ return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
case ZigTypeIdStruct:
zig_panic("TODO buf_write_value_bytes struct type");
case ZigTypeIdOptional:
@@ -20426,6 +20496,31 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
zig_unreachable();
}
+static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
+ ConstExprValue *val, ZigType *elem_type, size_t len)
+{
+ Error err;
+ uint64_t elem_size = type_size(codegen, elem_type);
+
+ switch (val->data.x_array.special) {
+ case ConstArraySpecialNone:
+ val->data.x_array.data.s_none.elements = create_const_vals(len);
+ for (size_t i = 0; i < len; i++) {
+ ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
+ elem->special = ConstValSpecialStatic;
+ elem->type = elem_type;
+ if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
+ return err;
+ }
+ return ErrorNone;
+ case ConstArraySpecialUndef:
+ zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
+ case ConstArraySpecialBuf:
+ zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
+ }
+ zig_unreachable();
+}
+
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
Error err;
assert(val->special == ConstValSpecialStatic);
@@ -20464,42 +20559,12 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
return ErrorNone;
}
- case ZigTypeIdArray: {
- uint64_t elem_size = type_size(codegen, val->type->data.array.child_type);
- size_t len = val->type->data.array.len;
-
- switch (val->data.x_array.special) {
- case ConstArraySpecialNone:
- val->data.x_array.data.s_none.elements = create_const_vals(len);
- for (size_t i = 0; i < len; i++) {
- ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
- elem->special = ConstValSpecialStatic;
- elem->type = val->type->data.array.child_type;
- if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
- return err;
- }
- return ErrorNone;
- case ConstArraySpecialUndef:
- zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
- case ConstArraySpecialBuf:
- zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
- }
- zig_unreachable();
- }
- case ZigTypeIdVector: {
- uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type);
- uint32_t len = val->type->data.vector.len;
-
- val->data.x_vector.elements = create_const_vals(len);
- for (uint32_t i = 0; i < len; i += 1) {
- ConstExprValue *elem = &val->data.x_vector.elements[i];
- elem->special = ConstValSpecialStatic;
- elem->type = val->type->data.vector.elem_type;
- if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
- return err;
- }
- return ErrorNone;
- }
+ case ZigTypeIdArray:
+ return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type,
+ val->type->data.array.len);
+ case ZigTypeIdVector:
+ return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type,
+ val->type->data.vector.len);
case ZigTypeIdEnum:
switch (val->type->data.enumeration.layout) {
case ContainerLayoutAuto:
@@ -21634,6 +21699,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
case IrInstructionIdDeclVarGen:
case IrInstructionIdPtrCastGen:
case IrInstructionIdCmpxchgGen:
+ case IrInstructionIdArrayToVector:
+ case IrInstructionIdVectorToArray:
zig_unreachable();
case IrInstructionIdReturn:
@@ -22129,6 +22196,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdFromBytes:
case IrInstructionIdToBytes:
case IrInstructionIdEnumToInt:
+ case IrInstructionIdVectorToArray:
+ case IrInstructionIdArrayToVector:
return false;
case IrInstructionIdAsm:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index a1fd450b65..e19aa6dda8 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -972,6 +972,18 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime
fprintf(irp->f, ")");
}
+static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) {
+ fprintf(irp->f, "ArrayToVector(");
+ ir_print_other_instruction(irp, instruction->array);
+ fprintf(irp->f, ")");
+}
+
+static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) {
+ fprintf(irp->f, "VectorToArray(");
+ ir_print_other_instruction(irp, instruction->vector);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
fprintf(irp->f, "inttoerr ");
ir_print_other_instruction(irp, instruction->target);
@@ -1825,6 +1837,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdDeclVarGen:
ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction);
break;
+ case IrInstructionIdArrayToVector:
+ ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction);
+ break;
+ case IrInstructionIdVectorToArray:
+ ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig
index e545a4c418..1fa00b34fd 100644
--- a/test/stage1/behavior.zig
+++ b/test/stage1/behavior.zig
@@ -74,6 +74,7 @@ comptime {
_ = @import("behavior/underscore.zig");
_ = @import("behavior/union.zig");
_ = @import("behavior/var_args.zig");
+ _ = @import("behavior/vector.zig");
_ = @import("behavior/void.zig");
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
new file mode 100644
index 0000000000..53c5d01381
--- /dev/null
+++ b/test/stage1/behavior/vector.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+const assertOrPanic = std.debug.assertOrPanic;
+
+test "implicit array to vector and vector to array" {
+ const S = struct {
+ fn doTheTest() void {
+ var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
+ const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
+ v +%= x;
+ const result: [4]i32 = v;
+ assertOrPanic(result[0] == 11);
+ assertOrPanic(result[1] == 22);
+ assertOrPanic(result[2] == 33);
+ assertOrPanic(result[3] == 44);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
--
cgit v1.2.3
From 06be65a602c2cd83bfdb9a1fa31e0ce038b0262d Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 4 Feb 2019 22:14:35 -0500
Subject: fix vector debug info tripping LLVM assertion
---
src/analyze.cpp | 5 +++--
src/zig_llvm.cpp | 6 +++---
src/zig_llvm.h | 2 +-
3 files changed, 7 insertions(+), 6 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index ff961a7044..d92d337c69 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4486,8 +4486,9 @@ ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) {
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name));
- entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, len,
- LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type);
+ entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder,
+ len * type_size_bits(g, elem_type),
+ LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type, len);
g->type_table.put(type_id, entry);
return entry;
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 65f6b5abb7..e5a4890914 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -264,12 +264,12 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch
}
struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
- uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty)
+ uint64_t SizeInBits, uint32_t AlignInBits, struct ZigLLVMDIType *Ty, uint32_t elem_count)
{
SmallVector subrange;
- subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, Size));
+ subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, elem_count));
DIType *di_type = reinterpret_cast(dibuilder)->createVectorType(
- Size,
+ SizeInBits,
AlignInBits,
reinterpret_cast(Ty),
reinterpret_cast(dibuilder)->getOrCreateArray(subrange));
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index 26dca1198b..b5b0e97b53 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -192,7 +192,7 @@ ZIG_EXTERN_C struct ZigLLVMDISubprogram *ZigLLVMCreateFunction(struct ZigLLVMDIB
unsigned scope_line, unsigned flags, bool is_optimized, struct ZigLLVMDISubprogram *decl_subprogram);
ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder,
- uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty);
+ uint64_t SizeInBits, uint32_t AlignInBits, struct ZigLLVMDIType *Ty, uint32_t elem_count);
ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubprogram *subprogram);
--
cgit v1.2.3
From b1775ca168e0bcfba6753346c5226881da49c6c4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 6 Feb 2019 13:48:04 -0500
Subject: thread local storage working for linux x86_64
---
CMakeLists.txt | 1 +
src/all_types.hpp | 13 +++--
src/analyze.cpp | 69 +++++++++++++++---------
src/analyze.hpp | 1 +
src/ast_render.cpp | 7 ++-
src/codegen.cpp | 7 +++
src/ir.cpp | 4 ++
src/parser.cpp | 22 +++++---
src/tokenizer.cpp | 2 +
src/tokenizer.hpp | 1 +
std/debug/index.zig | 1 -
std/heap.zig | 7 +--
std/index.zig | 3 +-
std/mem.zig | 20 +++++++
std/os/index.zig | 119 +++++++++++++++++++++++-------------------
std/os/startup.zig | 26 +++++++++
std/os/test.zig | 16 ++++++
std/special/bootstrap.zig | 63 ++++++++++++++++++++--
test/compile_errors.zig | 19 +++++++
test/stage1/behavior/misc.zig | 8 +++
20 files changed, 306 insertions(+), 103 deletions(-)
create mode 100644 std/os/startup.zig
(limited to 'src/analyze.cpp')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4dd6a1dcfa..a093bfbfd9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -587,6 +587,7 @@ set(ZIG_STD_FILES
"os/linux/vdso.zig"
"os/linux/x86_64.zig"
"os/path.zig"
+ "os/startup.zig"
"os/time.zig"
"os/uefi.zig"
"os/windows/advapi32.zig"
diff --git a/src/all_types.hpp b/src/all_types.hpp
index c4c9e13cfb..5af4e71157 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -544,12 +544,7 @@ struct AstNodeDefer {
};
struct AstNodeVariableDeclaration {
- VisibMod visib_mod;
Buf *symbol;
- bool is_const;
- bool is_comptime;
- bool is_export;
- bool is_extern;
// one or both of type and expr will be non null
AstNode *type;
AstNode *expr;
@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
AstNode *align_expr;
// populated if the "section(S)" is present
AstNode *section_expr;
+ Token *threadlocal_tok;
+
+ VisibMod visib_mod;
+ bool is_const;
+ bool is_comptime;
+ bool is_export;
+ bool is_extern;
};
struct AstNodeTestDecl {
@@ -1873,6 +1875,7 @@ struct ZigVar {
bool shadowable;
bool src_is_const;
bool gen_is_const;
+ bool is_thread_local;
};
struct ErrorTableEntry {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index ff961a7044..96f1faaaa9 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
-ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
- if (node->owner->c_import_node != nullptr) {
- // if this happens, then translate_c generated code that
- // failed semantic analysis, which isn't supposed to happen
- ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
- buf_sprintf("compiler bug: @cImport generated invalid zig code"));
-
- add_error_note(g, err, node, msg);
-
- g->errors.append(err);
- return err;
- }
-
- ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
- node->owner->source_code, node->owner->line_offsets, msg);
-
- g->errors.append(err);
- return err;
-}
-
-ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
- if (node->owner->c_import_node != nullptr) {
+static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
+ Buf *msg)
+{
+ if (owner->c_import_node != nullptr) {
// if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
@@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
return note;
}
- ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
- node->owner->source_code, node->owner->line_offsets, msg);
+ ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
+ owner->source_code, owner->line_offsets, msg);
err_msg_add_note(parent_msg, err);
return err;
}
+ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) {
+ if (owner->c_import_node != nullptr) {
+ // if this happens, then translate_c generated code that
+ // failed semantic analysis, which isn't supposed to happen
+ ErrorMsg *err = add_node_error(g, owner->c_import_node,
+ buf_sprintf("compiler bug: @cImport generated invalid zig code"));
+
+ add_error_note_token(g, err, owner, token, msg);
+
+ g->errors.append(err);
+ return err;
+ }
+ ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
+ owner->source_code, owner->line_offsets, msg);
+
+ g->errors.append(err);
+ return err;
+}
+
+ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
+ Token fake_token;
+ fake_token.start_line = node->line;
+ fake_token.start_column = node->column;
+ return add_token_error(g, node->owner, &fake_token, msg);
+}
+
+ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
+ Token fake_token;
+ fake_token.start_line = node->line;
+ fake_token.start_column = node->column;
+ return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
+}
+
ZigType *new_type_table_entry(ZigTypeId id) {
ZigType *entry = allocate(1);
entry->id = id;
@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
bool is_const = var_decl->is_const;
bool is_extern = var_decl->is_extern;
bool is_export = var_decl->is_export;
+ bool is_thread_local = var_decl->threadlocal_tok != nullptr;
ZigType *explicit_type = nullptr;
if (var_decl->type) {
@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
is_const, init_val, &tld_var->base, type);
tld_var->var->linkage = linkage;
+ tld_var->var->is_thread_local = is_thread_local;
if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
tld_var->var->var_type = g->builtin_types.entry_invalid;
@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
}
}
+ if (is_thread_local && is_const) {
+ add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
+ }
+
g->global_vars.append(tld_var);
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index f558fa44b0..9773782510 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -12,6 +12,7 @@
void semantic_analyze(CodeGen *g);
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
+ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
ZigType *new_type_table_entry(ZigTypeId id);
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 994ba5f5b1..34a7faa2a5 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {
return is_const ? "const" : "var";
}
+static const char *thread_local_string(Token *tok) {
+ return (tok == nullptr) ? "" : "threadlocal ";
+}
+
const char *container_string(ContainerKind kind) {
switch (kind) {
case ContainerKindEnum: return "enum";
@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
{
const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
+ const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
- fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var);
+ fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
print_symbol(ar, node->data.variable_declaration.symbol);
if (node->data.variable_declaration.type) {
diff --git a/src/codegen.cpp b/src/codegen.cpp
index de2222afb7..d8fc077efc 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6445,6 +6445,9 @@ static void do_code_gen(CodeGen *g) {
maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
LLVMSetAlignment(global_value, var->align_bytes);
LLVMSetGlobalConstant(global_value, var->gen_is_const);
+ if (var->is_thread_local && !g->is_single_threaded) {
+ LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
+ }
}
} else {
bool exported = (var->linkage == VarLinkageExport);
@@ -6470,6 +6473,9 @@ static void do_code_gen(CodeGen *g) {
}
LLVMSetGlobalConstant(global_value, var->gen_is_const);
+ if (var->is_thread_local && !g->is_single_threaded) {
+ LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
+ }
}
var->value_ref = global_value;
@@ -7520,6 +7526,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
+ g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
scan_import(g, g->compile_var_import);
diff --git a/src/ir.cpp b/src/ir.cpp
index 3cbbdc8103..02b2b12230 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
add_node_error(irb->codegen, variable_declaration->section_expr,
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
}
+ if (variable_declaration->threadlocal_tok != nullptr) {
+ add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
+ buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
+ }
// Temporarily set the name of the IrExecutable to the VariableDeclaration
// so that the struct or enum from the init expression inherits the name.
diff --git a/src/parser.cpp b/src/parser.cpp
index 81bd469d1c..1c1af87c51 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
static AstNode *ast_parse_var_decl(ParseContext *pc) {
- Token *first = eat_token_if(pc, TokenIdKeywordConst);
- if (first == nullptr)
- first = eat_token_if(pc, TokenIdKeywordVar);
- if (first == nullptr)
- return nullptr;
-
+ Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
+ Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
+ if (mut_kw == nullptr)
+ mut_kw = eat_token_if(pc, TokenIdKeywordVar);
+ if (mut_kw == nullptr) {
+ if (thread_local_kw == nullptr) {
+ return nullptr;
+ } else {
+ ast_invalid_token_error(pc, peek_token(pc));
+ }
+ }
Token *identifier = expect_token(pc, TokenIdSymbol);
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr)
@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
expect_token(pc, TokenIdSemicolon);
- AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);
- res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;
+ AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
+ res->data.variable_declaration.threadlocal_tok = thread_local_kw;
+ res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
res->data.variable_declaration.symbol = token_buf(identifier);
res->data.variable_declaration.type = type_expr;
res->data.variable_declaration.align_expr = align_expr;
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index d43bfabf6d..3acd605748 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"suspend", TokenIdKeywordSuspend},
{"switch", TokenIdKeywordSwitch},
{"test", TokenIdKeywordTest},
+ {"threadlocal", TokenIdKeywordThreadLocal},
{"true", TokenIdKeywordTrue},
{"try", TokenIdKeywordTry},
{"undefined", TokenIdKeywordUndefined},
@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordStruct: return "struct";
case TokenIdKeywordSwitch: return "switch";
case TokenIdKeywordTest: return "test";
+ case TokenIdKeywordThreadLocal: return "threadlocal";
case TokenIdKeywordTrue: return "true";
case TokenIdKeywordTry: return "try";
case TokenIdKeywordUndefined: return "undefined";
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index 1574e95571..17f36699b3 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -88,6 +88,7 @@ enum TokenId {
TokenIdKeywordSuspend,
TokenIdKeywordSwitch,
TokenIdKeywordTest,
+ TokenIdKeywordThreadLocal,
TokenIdKeywordTrue,
TokenIdKeywordTry,
TokenIdKeywordUndefined,
diff --git a/std/debug/index.zig b/std/debug/index.zig
index 838bd0c166..b4ef849509 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -37,7 +37,6 @@ const Module = struct {
var stderr_file: os.File = undefined;
var stderr_file_out_stream: os.File.OutStream = undefined;
-/// TODO multithreaded awareness
var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
var stderr_mutex = std.Mutex.init();
pub fn warn(comptime fmt: []const u8, args: ...) void {
diff --git a/std/heap.zig b/std/heap.zig
index fd2ce1e965..1403f8e831 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
};
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr);
- const rem = @rem(root_addr, alignment);
- const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
- const adjusted_addr = root_addr + march_forward_bytes;
+ const adjusted_addr = mem.alignForward(root_addr, alignment);
const record_addr = adjusted_addr + n;
@intToPtr(*align(1) usize, record_addr).* = root_addr;
return @intToPtr([*]u8, adjusted_addr)[0..n];
@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
const base_addr = @ptrToInt(old_mem.ptr);
const old_addr_end = base_addr + old_mem.len;
const new_addr_end = base_addr + new_size;
- const rem = @rem(new_addr_end, os.page_size);
- const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
+ const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
if (old_addr_end > new_addr_end_rounded) {
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
}
diff --git a/std/index.zig b/std/index.zig
index 80d1e46bb6..ef3988460f 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math/index.zig");
-pub const meta = @import("meta/index.zig");
pub const mem = @import("mem.zig");
+pub const meta = @import("meta/index.zig");
pub const net = @import("net.zig");
pub const os = @import("os/index.zig");
pub const pdb = @import("pdb.zig");
@@ -45,6 +45,7 @@ pub const unicode = @import("unicode.zig");
pub const zig = @import("zig/index.zig");
pub const lazyInit = @import("lazy_init.zig").lazyInit;
+pub const startup = @import("os/startup.zig");
test "std" {
// run tests from these
diff --git a/std/mem.zig b/std/mem.zig
index 26ae4ef089..178a5f6c6f 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -1366,3 +1366,23 @@ test "std.mem.subArrayPtr" {
sub2[1] = 'X';
debug.assert(std.mem.eql(u8, a2, "abcXef"));
}
+
+/// Round an address up to the nearest aligned address
+pub fn alignForward(addr: usize, alignment: usize) usize {
+ return (addr + alignment - 1) & ~(alignment - 1);
+}
+
+test "std.mem.alignForward" {
+ debug.assertOrPanic(alignForward(1, 1) == 1);
+ debug.assertOrPanic(alignForward(2, 1) == 2);
+ debug.assertOrPanic(alignForward(1, 2) == 2);
+ debug.assertOrPanic(alignForward(2, 2) == 2);
+ debug.assertOrPanic(alignForward(3, 2) == 4);
+ debug.assertOrPanic(alignForward(4, 2) == 4);
+ debug.assertOrPanic(alignForward(7, 8) == 8);
+ debug.assertOrPanic(alignForward(8, 8) == 8);
+ debug.assertOrPanic(alignForward(9, 8) == 16);
+ debug.assertOrPanic(alignForward(15, 8) == 16);
+ debug.assertOrPanic(alignForward(16, 8) == 16);
+ debug.assertOrPanic(alignForward(17, 8) == 24);
+}
diff --git a/std/os/index.zig b/std/os/index.zig
index 451c0a3436..68b3409757 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -8,6 +8,9 @@ const is_posix = switch (builtin.os) {
};
const os = @This();
+// See the comment in startup.zig for why this does not use the `std` global above.
+const startup = @import("std").startup;
+
test "std.os" {
_ = @import("child_process.zig");
_ = @import("darwin.zig");
@@ -667,14 +670,11 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
}
}
-pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
-pub var posix_environ_raw: [][*]u8 = undefined;
-
/// See std.elf for the constants.
pub fn linuxGetAuxVal(index: usize) usize {
if (builtin.link_libc) {
return usize(std.c.getauxval(index));
- } else if (linux_elf_aux_maybe) |auxv| {
+ } else if (startup.linux_elf_aux_maybe) |auxv| {
var i: usize = 0;
while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
if (auxv[i].a_type == index)
@@ -692,12 +692,7 @@ pub fn getBaseAddress() usize {
return base;
}
const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
- const ElfHeader = switch (@sizeOf(usize)) {
- 4 => std.elf.Elf32_Ehdr,
- 8 => std.elf.Elf64_Ehdr,
- else => @compileError("Unsupported architecture"),
- };
- return phdr - @sizeOf(ElfHeader);
+ return phdr - @sizeOf(std.elf.Ehdr);
},
builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
@@ -739,7 +734,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.setMove(key, value);
}
} else {
- for (posix_environ_raw) |ptr| {
+ for (startup.posix_environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const key = ptr[0..line_i];
@@ -761,7 +756,7 @@ test "os.getEnvMap" {
/// TODO make this go through libc when we have it
pub fn getEnvPosix(key: []const u8) ?[]const u8 {
- for (posix_environ_raw) |ptr| {
+ for (startup.posix_environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const this_key = ptr[0..line_i];
@@ -1942,14 +1937,14 @@ pub const ArgIteratorPosix = struct {
pub fn init() ArgIteratorPosix {
return ArgIteratorPosix{
.index = 0,
- .count = raw.len,
+ .count = startup.posix_argv_raw.len,
};
}
pub fn next(self: *ArgIteratorPosix) ?[]const u8 {
if (self.index == self.count) return null;
- const s = raw[self.index];
+ const s = startup.posix_argv_raw[self.index];
self.index += 1;
return cstr.toSlice(s);
}
@@ -1960,10 +1955,6 @@ pub const ArgIteratorPosix = struct {
self.index += 1;
return true;
}
-
- /// This is marked as public but actually it's only meant to be used
- /// internally by zig's startup code.
- pub var raw: [][*]u8 = undefined;
};
pub const ArgIteratorWindows = struct {
@@ -2908,14 +2899,15 @@ pub const Thread = struct {
pub const Data = if (use_pthreads)
struct {
handle: Thread.Handle,
- stack_addr: usize,
- stack_len: usize,
+ mmap_addr: usize,
+ mmap_len: usize,
}
else switch (builtin.os) {
builtin.Os.linux => struct {
handle: Thread.Handle,
- stack_addr: usize,
- stack_len: usize,
+ mmap_addr: usize,
+ mmap_len: usize,
+ tls_end_addr: usize,
},
builtin.Os.windows => struct {
handle: Thread.Handle,
@@ -2955,7 +2947,7 @@ pub const Thread = struct {
posix.EDEADLK => unreachable,
else => unreachable,
}
- assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
} else switch (builtin.os) {
builtin.Os.linux => {
while (true) {
@@ -2969,7 +2961,7 @@ pub const Thread = struct {
else => unreachable,
}
}
- assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
},
builtin.Os.windows => {
assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
@@ -3097,42 +3089,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
- const mmap_len = default_stack_size;
- const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
- if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
- errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
+ var stack_end_offset: usize = undefined;
+ var thread_start_offset: usize = undefined;
+ var context_start_offset: usize = undefined;
+ var tls_start_offset: usize = undefined;
+ const mmap_len = blk: {
+ // First in memory will be the stack, which grows downwards.
+ var l: usize = mem.alignForward(default_stack_size, os.page_size);
+ stack_end_offset = l;
+ // Above the stack, so that it can be in the same mmap call, put the Thread object.
+ l = mem.alignForward(l, @alignOf(Thread));
+ thread_start_offset = l;
+ l += @sizeOf(Thread);
+ // Next, the Context object.
+ if (@sizeOf(Context) != 0) {
+ l = mem.alignForward(l, @alignOf(Context));
+ context_start_offset = l;
+ l += @sizeOf(Context);
+ }
+ // Finally, the Thread Local Storage, if any.
+ if (!Thread.use_pthreads) {
+ if (startup.linux_tls_phdr) |tls_phdr| {
+ l = mem.alignForward(l, tls_phdr.p_align);
+ tls_start_offset = l;
+ l += tls_phdr.p_memsz;
+ }
+ }
+ break :blk l;
+ };
+ const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
+ if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
+ errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
+
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
+ thread_ptr.data.mmap_addr = mmap_addr;
+ thread_ptr.data.mmap_len = mmap_len;
- var stack_end: usize = stack_addr + mmap_len;
var arg: usize = undefined;
if (@sizeOf(Context) != 0) {
- stack_end -= @sizeOf(Context);
- stack_end -= stack_end % @alignOf(Context);
- assert(stack_end >= stack_addr);
- const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
+ arg = mmap_addr + context_start_offset;
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
context_ptr.* = context;
- arg = stack_end;
}
- stack_end -= @sizeOf(Thread);
- stack_end -= stack_end % @alignOf(Thread);
- assert(stack_end >= stack_addr);
- const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end));
-
- thread_ptr.data.stack_addr = stack_addr;
- thread_ptr.data.stack_len = mmap_len;
-
- if (builtin.os == builtin.Os.windows) {
- // use windows API directly
- @compileError("TODO support spawnThread for Windows");
- } else if (Thread.use_pthreads) {
+ if (Thread.use_pthreads) {
// use pthreads
var attr: c.pthread_attr_t = undefined;
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
defer assert(c.pthread_attr_destroy(&attr) == 0);
- // align to page
- stack_end -= stack_end % os.page_size;
- assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
+ assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
switch (err) {
@@ -3143,10 +3149,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
else => return unexpectedErrorPosix(@intCast(usize, err)),
}
} else if (builtin.os == builtin.Os.linux) {
- // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
- const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
- const newtls: usize = 0;
- const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
+ var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
+ posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
+ posix.CLONE_DETACHED;
+ var newtls: usize = undefined;
+ if (startup.linux_tls_phdr) |tls_phdr| {
+ @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), startup.linux_tls_img_src, tls_phdr.p_filesz);
+ thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
+ newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
+ flags |= posix.CLONE_SETTLS;
+ }
+ const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
const err = posix.getErrno(rc);
switch (err) {
0 => return thread_ptr,
diff --git a/std/os/startup.zig b/std/os/startup.zig
new file mode 100644
index 0000000000..c54d274c5d
--- /dev/null
+++ b/std/os/startup.zig
@@ -0,0 +1,26 @@
+// This file contains global variables that are initialized on startup from
+// std/special/bootstrap.zig. There are a few things to be aware of here.
+//
+// First, when building an object or library, and no entry point is defined
+// (such as pub fn main), std/special/bootstrap.zig is not included in the
+// compilation. And so these global variables will remain set to the values
+// you see here.
+//
+// Second, when using `zig test` to test the standard library, note that
+// `zig test` is self-hosted. This means that it uses std/special/bootstrap.zig
+// and an @import("std") from the install directory, which is distinct from
+// the standard library files that we are directly testing with `zig test`.
+// This means that these global variables would not get set. So the workaround
+// here is that references to these globals from the standard library must
+// use `@import("std").startup` rather than
+// `@import("path/to/std/index.zig").startup` (and rather than the file path of
+// this file directly). We also put "std" as a reference to itself in the
+// standard library package so that this can work.
+
+const std = @import("../index.zig");
+
+pub var linux_tls_phdr: ?*std.elf.Phdr = null;
+pub var linux_tls_img_src: [*]const u8 = undefined; // defined when linux_tls_phdr is non-null
+pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null;
+pub var posix_environ_raw: [][*]u8 = undefined;
+pub var posix_argv_raw: [][*]u8 = undefined;
diff --git a/std/os/test.zig b/std/os/test.zig
index f14cf47786..bd9148d1b1 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -105,3 +105,19 @@ test "AtomicFile" {
try os.deleteFile(test_out_file);
}
+
+test "thread local storage" {
+ if (builtin.single_threaded) return error.SkipZigTest;
+ const thread1 = try std.os.spawnThread({}, testTls);
+ const thread2 = try std.os.spawnThread({}, testTls);
+ testTls({});
+ thread1.wait();
+ thread2.wait();
+}
+
+threadlocal var x: i32 = 1234;
+fn testTls(context: void) void {
+ if (x != 1234) @panic("bad start value");
+ x += 1;
+ if (x != 1235) @panic("bad end value");
+}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 129bde913f..0e84f67481 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -4,6 +4,7 @@
const root = @import("@root");
const std = @import("std");
const builtin = @import("builtin");
+const assert = std.debug.assert;
var argc_ptr: [*]usize = undefined;
@@ -61,9 +62,23 @@ fn posixCallMainAndExit() noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
if (builtin.os == builtin.Os.linux) {
- const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
- std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);
- std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);
+ // Scan auxiliary vector.
+ const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
+ std.startup.linux_elf_aux_maybe = auxv;
+ var i: usize = 0;
+ var at_phdr: usize = 0;
+ var at_phnum: usize = 0;
+ var at_phent: usize = 0;
+ while (auxv[i].a_un.a_val != 0) : (i += 1) {
+ switch (auxv[i].a_type) {
+ std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
+ std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
+ std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
+ std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
+ else => {},
+ }
+ }
+ if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
}
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
@@ -72,8 +87,8 @@ fn posixCallMainAndExit() noreturn {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
- std.os.ArgIteratorPosix.raw = argv[0..argc];
- std.os.posix_environ_raw = envp;
+ std.startup.posix_argv_raw = argv[0..argc];
+ std.startup.posix_environ_raw = envp;
return callMain();
}
@@ -116,3 +131,41 @@ inline fn callMain() u8 {
else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
}
}
+
+var tls_end_addr: usize = undefined;
+const main_thread_tls_align = 32;
+var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
+
+fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
+ var phdr_addr = at_phdr;
+ var n = at_phnum;
+ var base: usize = 0;
+ while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
+ const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
+ // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
+ switch (phdr.p_type) {
+ std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
+ std.elf.PT_TLS => std.startup.linux_tls_phdr = phdr,
+ else => continue,
+ }
+ }
+ const tls_phdr = std.startup.linux_tls_phdr orelse return;
+ std.startup.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
+ assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
+ assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
+ @memcpy(&main_thread_tls_bytes, std.startup.linux_tls_img_src, tls_phdr.p_filesz);
+ tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
+ linuxSetThreadArea(@ptrToInt(&tls_end_addr));
+}
+
+fn linuxSetThreadArea(addr: usize) void {
+ switch (builtin.arch) {
+ builtin.Arch.x86_64 => {
+ const ARCH_SET_FS = 0x1002;
+ const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
+ // acrh_prctl is documented to never fail
+ assert(rc == 0);
+ },
+ else => @compileError("Unsupported architecture"),
+ }
+}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 30d9ca5d70..acd1eada06 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,25 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.add(
+ "threadlocal qualifier on const",
+ \\threadlocal const x: i32 = 1234;
+ \\export fn entry() i32 {
+ \\ return x;
+ \\}
+ ,
+ ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant",
+ );
+
+ cases.add(
+ "threadlocal qualifier on local variable",
+ \\export fn entry() void {
+ \\ threadlocal var x: i32 = 1234;
+ \\}
+ ,
+ ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal",
+ );
+
cases.add(
"@bitCast same size but bit count mismatch",
\\export fn entry(byte: u8) void {
diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig
index 8d2555dddd..3cc8e5f31e 100644
--- a/test/stage1/behavior/misc.zig
+++ b/test/stage1/behavior/misc.zig
@@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {
fn getNull() ?*i32 {
return null;
}
+
+test "thread local variable" {
+ const S = struct {
+ threadlocal var t: i32 = 1234;
+ };
+ S.t += 1;
+ assertOrPanic(S.t == 1235);
+}
--
cgit v1.2.3
From 0a7bdc00771dbad1dfe5eb93a7cade89059d227a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 14:44:33 -0500
Subject: implement vector addition with safety checking
this would work if @llvm.sadd.with.overflow supported
vectors, which it does in trunk. but it does not support
them in llvm 7 or even in llvm 8 release branch.
so the next commit after this will have to do a different
strategy, but when llvm 9 comes out it may be worth coming
back to this one.
---
src/all_types.hpp | 3 ++
src/analyze.cpp | 6 ++-
src/codegen.cpp | 154 +++++++++++++++++++++++++++++++-----------------------
3 files changed, 95 insertions(+), 68 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 842c9ae904..908c0e327c 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1538,6 +1538,8 @@ enum ZigLLVMFnId {
ZigLLVMFnIdBitReverse,
};
+// There are a bunch of places in code that rely on these values being in
+// exactly this order.
enum AddSubMul {
AddSubMulAdd = 0,
AddSubMulSub = 1,
@@ -1563,6 +1565,7 @@ struct ZigLLVMFnKey {
struct {
AddSubMul add_sub_mul;
uint32_t bit_count;
+ uint32_t vector_len; // 0 means not a vector
bool is_signed;
} overflow_arithmetic;
struct {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 83a576554a..0c493ebda1 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6361,7 +6361,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
case ZigLLVMFnIdOverflowArithmetic:
return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
- ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820);
+ ((uint32_t)(x.data.overflow_arithmetic.is_signed) ? 1062315172 : 314955820) +
+ x.data.overflow_arithmetic.vector_len * 1435156945;
}
zig_unreachable();
}
@@ -6387,7 +6388,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
case ZigLLVMFnIdOverflowArithmetic:
return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
(a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
- (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed);
+ (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed) &&
+ (a.data.overflow_arithmetic.vector_len == b.data.overflow_arithmetic.vector_len);
}
zig_unreachable();
}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 3bfd7cdfc5..e45280b0d1 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -715,38 +715,59 @@ static void clear_debug_source_node(CodeGen *g) {
ZigLLVMClearCurrentDebugLocation(g->builder);
}
-static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *type_entry,
+static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type,
const char *signed_name, const char *unsigned_name)
{
+ ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
char fn_name[64];
- assert(type_entry->id == ZigTypeIdInt);
- const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;
- sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, type_entry->data.integral.bit_count);
+ assert(int_type->id == ZigTypeIdInt);
+ const char *signed_str = int_type->data.integral.is_signed ? signed_name : unsigned_name;
- LLVMTypeRef return_elem_types[] = {
- type_entry->type_ref,
- LLVMInt1Type(),
- };
LLVMTypeRef param_types[] = {
- type_entry->type_ref,
- type_entry->type_ref,
+ operand_type->type_ref,
+ operand_type->type_ref,
};
- LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
- LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
- LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
- assert(LLVMGetIntrinsicID(fn_val));
- return fn_val;
+
+ if (operand_type->id == ZigTypeIdVector) {
+ sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu32 "i%" PRIu32, signed_str,
+ operand_type->data.vector.len, int_type->data.integral.bit_count);
+
+ LLVMTypeRef return_elem_types[] = {
+ operand_type->type_ref,
+ LLVMVectorType(LLVMInt1Type(), operand_type->data.vector.len),
+ };
+ LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
+ LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
+ LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
+ assert(LLVMGetIntrinsicID(fn_val));
+ return fn_val;
+ } else {
+ sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, int_type->data.integral.bit_count);
+
+ LLVMTypeRef return_elem_types[] = {
+ operand_type->type_ref,
+ LLVMInt1Type(),
+ };
+ LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
+ LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
+ LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
+ assert(LLVMGetIntrinsicID(fn_val));
+ return fn_val;
+ }
}
-static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubMul add_sub_mul) {
- assert(type_entry->id == ZigTypeIdInt);
+static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) {
+ ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
+ assert(int_type->id == ZigTypeIdInt);
ZigLLVMFnKey key = {};
key.id = ZigLLVMFnIdOverflowArithmetic;
- key.data.overflow_arithmetic.is_signed = type_entry->data.integral.is_signed;
+ key.data.overflow_arithmetic.is_signed = int_type->data.integral.is_signed;
key.data.overflow_arithmetic.add_sub_mul = add_sub_mul;
- key.data.overflow_arithmetic.bit_count = (uint32_t)type_entry->data.integral.bit_count;
+ key.data.overflow_arithmetic.bit_count = (uint32_t)int_type->data.integral.bit_count;
+ key.data.overflow_arithmetic.vector_len = (operand_type->id == ZigTypeIdVector) ?
+ operand_type->data.vector.len : 0;
auto existing_entry = g->llvm_fn_table.maybe_get(key);
if (existing_entry)
@@ -755,13 +776,13 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *type_entry, AddSubM
LLVMValueRef fn_val;
switch (add_sub_mul) {
case AddSubMulAdd:
- fn_val = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
+ fn_val = get_arithmetic_overflow_fn(g, operand_type, "sadd", "uadd");
break;
case AddSubMulSub:
- fn_val = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
+ fn_val = get_arithmetic_overflow_fn(g, operand_type, "ssub", "usub");
break;
case AddSubMulMul:
- fn_val = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
+ fn_val = get_arithmetic_overflow_fn(g, operand_type, "smul", "umul");
break;
}
@@ -1752,17 +1773,28 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
}
}
-static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *type_entry, AddSubMul op,
+static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op,
LLVMValueRef val1, LLVMValueRef val2)
{
- LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op);
+ LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op);
LLVMValueRef params[] = {
val1,
val2,
};
LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
- LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+
+ LLVMValueRef overflow_bit;
+ if (operand_type->id == ZigTypeIdVector) {
+ LLVMValueRef overflow_vector = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+ LLVMTypeRef bigger_int_type_ref = LLVMIntType(operand_type->data.vector.len);
+ LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bigger_int_type_ref, "");
+ LLVMValueRef zero = LLVMConstNull(bigger_int_type_ref);
+ overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, "");
+ } else {
+ overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
+ }
+
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block);
@@ -2608,7 +2640,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
(op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
);
- ZigType *type_entry = op1->value.type;
+ ZigType *operand_type = op1->value.type;
+ ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
bool want_runtime_safety = bin_op_instruction->safety_check_on &&
ir_want_runtime_safety(g, &bin_op_instruction->base);
@@ -2634,17 +2667,17 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpCmpGreaterThan:
case IrBinOpCmpLessOrEq:
case IrBinOpCmpGreaterOrEq:
- if (type_entry->id == ZigTypeIdFloat) {
+ if (scalar_type->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdInt) {
- LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, type_entry->data.integral.is_signed);
+ } else if (scalar_type->id == ZigTypeIdInt) {
+ LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed);
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdEnum ||
- type_entry->id == ZigTypeIdErrorSet ||
- type_entry->id == ZigTypeIdBool ||
- get_codegen_ptr_type(type_entry) != nullptr)
+ } else if (scalar_type->id == ZigTypeIdEnum ||
+ scalar_type->id == ZigTypeIdErrorSet ||
+ scalar_type->id == ZigTypeIdBool ||
+ get_codegen_ptr_type(scalar_type) != nullptr)
{
LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
@@ -2665,23 +2698,16 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
- bool is_vector = type_entry->id == ZigTypeIdVector;
bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
AddSubMul add_sub_mul =
op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
AddSubMulMul;
- // The code that is generated for vectors and scalars are the same,
- // so we can just set type_entry to the vectors elem_type an avoid
- // a lot of repeated code.
- if (is_vector)
- type_entry = type_entry->data.vector.elem_type;
-
- if (type_entry->id == ZigTypeIdPointer) {
- assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
+ if (scalar_type->id == ZigTypeIdPointer) {
+ assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown);
LLVMValueRef subscript_value;
- if (is_vector)
+ if (operand_type->id == ZigTypeIdVector)
zig_panic("TODO: Implement vector operations on pointers.");
switch (add_sub_mul) {
@@ -2697,17 +2723,15 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
// TODO runtime safety
return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
- } else if (type_entry->id == ZigTypeIdFloat) {
+ } else if (scalar_type->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
- } else if (type_entry->id == ZigTypeIdInt) {
+ } else if (scalar_type->id == ZigTypeIdInt) {
if (is_wrapping) {
return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
- if (is_vector)
- zig_panic("TODO: Implement runtime safety vector operations.");
- return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
- } else if (type_entry->data.integral.is_signed) {
+ return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value);
+ } else if (scalar_type->data.integral.is_signed) {
return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else {
return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
@@ -2725,15 +2749,14 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpBitShiftLeftLossy:
case IrBinOpBitShiftLeftExact:
{
- assert(type_entry->id == ZigTypeIdInt);
- LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type,
- type_entry, op2_value);
+ assert(scalar_type->id == ZigTypeIdInt);
+ LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value);
bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy);
if (is_sloppy) {
return LLVMBuildShl(g->builder, op1_value, op2_casted, "");
} else if (want_runtime_safety) {
- return gen_overflow_shl_op(g, type_entry, op1_value, op2_casted);
- } else if (type_entry->data.integral.is_signed) {
+ return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted);
+ } else if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, "");
} else {
return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, "");
@@ -2742,19 +2765,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpBitShiftRightLossy:
case IrBinOpBitShiftRightExact:
{
- assert(type_entry->id == ZigTypeIdInt);
- LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type,
- type_entry, op2_value);
+ assert(scalar_type->id == ZigTypeIdInt);
+ LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value.type, scalar_type, op2_value);
bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy);
if (is_sloppy) {
- if (type_entry->data.integral.is_signed) {
+ if (scalar_type->data.integral.is_signed) {
return LLVMBuildAShr(g->builder, op1_value, op2_casted, "");
} else {
return LLVMBuildLShr(g->builder, op1_value, op2_casted, "");
}
} else if (want_runtime_safety) {
- return gen_overflow_shr_op(g, type_entry, op1_value, op2_casted);
- } else if (type_entry->data.integral.is_signed) {
+ return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted);
+ } else if (scalar_type->data.integral.is_signed) {
return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, "");
} else {
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
@@ -2762,22 +2784,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
}
case IrBinOpDivUnspecified:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindFloat);
+ op1_value, op2_value, scalar_type, DivKindFloat);
case IrBinOpDivExact:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindExact);
+ op1_value, op2_value, scalar_type, DivKindExact);
case IrBinOpDivTrunc:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindTrunc);
+ op1_value, op2_value, scalar_type, DivKindTrunc);
case IrBinOpDivFloor:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, DivKindFloor);
+ op1_value, op2_value, scalar_type, DivKindFloor);
case IrBinOpRemRem:
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, RemKindRem);
+ op1_value, op2_value, scalar_type, RemKindRem);
case IrBinOpRemMod:
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
- op1_value, op2_value, type_entry, RemKindMod);
+ op1_value, op2_value, scalar_type, RemKindMod);
}
zig_unreachable();
}
--
cgit v1.2.3
From 34eb9f18acc2370973adcc7be0d010d62300e9a9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 20:41:26 -0500
Subject: fix not updating debug info type of optional error sets
There's an unfortunate footgun in the current design of error sets.
The debug info type for every error set is the same as the debug info
type of the global error set, which is essentially an enum forward
declaration. The problem is that when we "replace" the forward
declaration with the final value, once we know all the possible errors,
we have to update the pointers of every error set.
So the footgun is that if you ever copy the debug info type of the
global error set, you have to add the address of the pointer to a list
of pointers that need to be updated once we "replace" the forward
declaration. I activated the footgun when I introduced the optimization
that `?anyerror` types are the same size as `anyerror` types (using 0 as
the null value), because I introduced a pointer copy of the global error
set debug info type, but forgot to add it to the list.
I'm sure that there is a better way to code this, which does not have
the footgun, but this commit contains only a fix, not a reworking of the
logic.
closes #1937
---
src/analyze.cpp | 3 +++
test/stage1/behavior/error.zig | 5 +++++
2 files changed, 8 insertions(+)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 0c493ebda1..970d1cc382 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -594,6 +594,9 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
// function types are technically pointers
entry->type_ref = child_type->type_ref;
entry->di_type = child_type->di_type;
+ if (entry->di_type == g->builtin_types.entry_global_error_set->di_type) {
+ g->error_di_types.append(&entry->di_type);
+ }
} else {
assert(child_type->di_type);
// create a struct with a boolean whether this is the null value
diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig
index 265ddd9d6c..7d9dae7bdd 100644
--- a/test/stage1/behavior/error.zig
+++ b/test/stage1/behavior/error.zig
@@ -330,3 +330,8 @@ test "optional error set is the same size as error set" {
expect(S.returnsOptErrSet() == null);
comptime expect(S.returnsOptErrSet() == null);
}
+
+test "debug info for optional error set" {
+ const SomeError = error{Hello};
+ var a_local_variable: ?SomeError = null;
+}
--
cgit v1.2.3
From b8cbe3872e702ab8ec388e75cb711330a45825b0 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 10 Feb 2019 00:14:30 -0500
Subject: added C pointer type and implicit int-to-ptr for this type
See #1059
---
src/all_types.hpp | 1 +
src/analyze.cpp | 14 ++-
src/ir.cpp | 236 ++++++++++++++++++++++++++------------
src/parser.cpp | 8 ++
src/tokenizer.cpp | 19 ++-
src/tokenizer.hpp | 1 +
test/stage1/behavior/pointers.zig | 6 +
7 files changed, 210 insertions(+), 75 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 908c0e327c..fd66b77ad2 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1038,6 +1038,7 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
enum PtrLen {
PtrLenUnknown,
PtrLenSingle,
+ PtrLenC,
};
struct ZigTypePointer {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 970d1cc382..e561050e0d 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -417,6 +417,18 @@ ZigType *get_promise_type(CodeGen *g, ZigType *result_type) {
return entry;
}
+static const char *ptr_len_to_star_str(PtrLen ptr_len) {
+ switch (ptr_len) {
+ case PtrLenSingle:
+ return "*";
+ case PtrLenUnknown:
+ return "[*]";
+ case PtrLenC:
+ return "[*c]";
+ }
+ zig_unreachable();
+}
+
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
@@ -466,7 +478,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
ZigType *entry = new_type_table_entry(ZigTypeIdPointer);
- const char *star_str = ptr_len == PtrLenSingle ? "*" : "[*]";
+ const char *star_str = ptr_len_to_star_str(ptr_len);
const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
buf_resize(&entry->name, 0);
diff --git a/src/ir.cpp b/src/ir.cpp
index 5d4013b4b9..76277f541b 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -169,6 +169,10 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
+static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
+ ZigType *ptr_type);
+static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
+ ZigType *dest_type);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -5019,10 +5023,23 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *
return ir_build_ref(irb, scope, value->source_node, value, false, false);
}
+static PtrLen star_token_to_ptr_len(TokenId token_id) {
+ switch (token_id) {
+ case TokenIdStar:
+ case TokenIdStarStar:
+ return PtrLenSingle;
+ case TokenIdBracketStarBracket:
+ return PtrLenUnknown;
+ case TokenIdBracketStarCBracket:
+ return PtrLenC;
+ default:
+ zig_unreachable();
+ }
+}
+
static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypePointerType);
- PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
- node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
+ PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
bool is_const = node->data.pointer_type.is_const;
bool is_volatile = node->data.pointer_type.is_volatile;
AstNode *expr_node = node->data.pointer_type.op_expr;
@@ -8538,6 +8555,20 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
}
}
}
+ if (other_type->id == ZigTypeIdPointer && other_type->data.pointer.ptr_len == PtrLenC && const_val_is_int) {
+ if (!bigint_fits_in_bits(&const_val->data.x_bigint, ira->codegen->pointer_size_bytes * 8, true) &&
+ !bigint_fits_in_bits(&const_val->data.x_bigint, ira->codegen->pointer_size_bytes * 8, false))
+ {
+ Buf *val_buf = buf_alloc();
+ bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
+
+ ir_add_error(ira, instruction,
+ buf_sprintf("integer value %s outside of pointer address range",
+ buf_ptr(val_buf)));
+ return false;
+ }
+ return true;
+ }
const char *num_lit_str;
Buf *val_buf = buf_alloc();
@@ -10811,6 +10842,37 @@ static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *
return ir_build_vector_to_array(ira, source_instr, vector, array_type);
}
+static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *integer, ZigType *dest_type)
+{
+ IrInstruction *unsigned_integer;
+ if (instr_is_comptime(integer)) {
+ unsigned_integer = integer;
+ } else {
+ assert(integer->value.type->id == ZigTypeIdInt);
+
+ if (integer->value.type->data.integral.bit_count >
+ ira->codegen->builtin_types.entry_usize->data.integral.bit_count)
+ {
+ ir_add_error(ira, source_instr,
+ buf_sprintf("integer type too big for implicit @intToPtr to type '%s'", buf_ptr(&dest_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ if (integer->value.type->data.integral.is_signed) {
+ ZigType *unsigned_int_type = get_int_type(ira->codegen, false,
+ integer->value.type->data.integral.bit_count);
+ unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type);
+ if (type_is_invalid(unsigned_integer->value.type))
+ return ira->codegen->invalid_instruction;
+ } else {
+ unsigned_integer = integer;
+ }
+ }
+
+ return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type);
+}
+
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
@@ -11217,6 +11279,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
}
+ // casting to C pointers
+ if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC) {
+ // cast from integer to C pointer
+ if (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt) {
+ return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type);
+ }
+ }
+
// cast from undefined to anything
if (actual_type->id == ZigTypeIdUndefined) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
@@ -20674,32 +20744,10 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
zig_unreachable();
}
-static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
- Error err;
- IrInstruction *dest_type_value = instruction->dest_type->child;
- ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
- if (type_is_invalid(dest_type))
- return ira->codegen->invalid_instruction;
-
- IrInstruction *value = instruction->value->child;
- ZigType *src_type = value->value.type;
- if (type_is_invalid(src_type))
- return ira->codegen->invalid_instruction;
-
- if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown)))
- return ira->codegen->invalid_instruction;
-
- if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown)))
- return ira->codegen->invalid_instruction;
-
- if (get_codegen_ptr_type(src_type) != nullptr) {
- ir_add_error(ira, value,
- buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name)));
- return ira->codegen->invalid_instruction;
- }
-
- switch (src_type->id) {
+static bool type_can_bit_cast(ZigType *t) {
+ switch (t->id) {
case ZigTypeIdInvalid:
+ zig_unreachable();
case ZigTypeIdMetaType:
case ZigTypeIdOpaque:
case ZigTypeIdBoundFn:
@@ -20710,42 +20758,36 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
case ZigTypeIdComptimeInt:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
- ir_add_error(ira, dest_type_value,
- buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name)));
- return ira->codegen->invalid_instruction;
+ case ZigTypeIdPointer:
+ return false;
default:
- break;
+ // TODO list these types out explicitly, there are probably some other invalid ones here
+ return true;
}
+}
- if (get_codegen_ptr_type(dest_type) != nullptr) {
- ir_add_error(ira, dest_type_value,
- buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name)));
+static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
+ ZigType *dest_type)
+{
+ Error err;
+
+ ZigType *src_type = value->value.type;
+ assert(get_codegen_ptr_type(src_type) == nullptr);
+ assert(type_can_bit_cast(src_type));
+ assert(get_codegen_ptr_type(dest_type) == nullptr);
+ assert(type_can_bit_cast(dest_type));
+
+ if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown)))
+ return ira->codegen->invalid_instruction;
+
+ if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
- }
- switch (dest_type->id) {
- case ZigTypeIdInvalid:
- case ZigTypeIdMetaType:
- case ZigTypeIdOpaque:
- case ZigTypeIdBoundFn:
- case ZigTypeIdArgTuple:
- case ZigTypeIdNamespace:
- case ZigTypeIdUnreachable:
- case ZigTypeIdComptimeFloat:
- case ZigTypeIdComptimeInt:
- case ZigTypeIdUndefined:
- case ZigTypeIdNull:
- ir_add_error(ira, dest_type_value,
- buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
- return ira->codegen->invalid_instruction;
- default:
- break;
- }
uint64_t dest_size_bytes = type_size(ira->codegen, dest_type);
uint64_t src_size_bytes = type_size(ira->codegen, src_type);
if (dest_size_bytes != src_size_bytes) {
- ir_add_error(ira, &instruction->base,
+ ir_add_error(ira, source_instr,
buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64,
buf_ptr(&dest_type->name), dest_size_bytes,
buf_ptr(&src_type->name), src_size_bytes));
@@ -20755,7 +20797,7 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type);
uint64_t src_size_bits = type_size_bits(ira->codegen, src_type);
if (dest_size_bits != src_size_bits) {
- ir_add_error(ira, &instruction->base,
+ ir_add_error(ira, source_instr,
buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits",
buf_ptr(&dest_type->name), dest_size_bits,
buf_ptr(&src_type->name), src_size_bits));
@@ -20767,44 +20809,63 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct
if (!val)
return ira->codegen->invalid_instruction;
- IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
+ IrInstruction *result = ir_const(ira, source_instr, dest_type);
uint8_t *buf = allocate_nonzero(src_size_bytes);
buf_write_value_bytes(ira->codegen, buf, val);
- if ((err = buf_read_value_bytes(ira, ira->codegen, instruction->base.source_node, buf, &result->value)))
+ if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value)))
return ira->codegen->invalid_instruction;
return result;
}
- IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope,
- instruction->base.source_node, nullptr, value);
+ IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, nullptr, value);
result->value.type = dest_type;
return result;
}
-static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
- Error err;
+static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
IrInstruction *dest_type_value = instruction->dest_type->child;
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
if (type_is_invalid(dest_type))
return ira->codegen->invalid_instruction;
- // We explicitly check for the size, so we can use get_src_ptr_type
- if (get_src_ptr_type(dest_type) == nullptr) {
- ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
+ IrInstruction *value = instruction->value->child;
+ ZigType *src_type = value->value.type;
+ if (type_is_invalid(src_type))
+ return ira->codegen->invalid_instruction;
+
+ if (get_codegen_ptr_type(src_type) != nullptr) {
+ ir_add_error(ira, value,
+ buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
- if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
+ if (!type_can_bit_cast(src_type)) {
+ ir_add_error(ira, dest_type_value,
+ buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
- if (!type_has_bits(dest_type)) {
+ }
+
+ if (get_codegen_ptr_type(dest_type) != nullptr) {
ir_add_error(ira, dest_type_value,
- buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name)));
+ buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
- IrInstruction *target = instruction->target->child;
- if (type_is_invalid(target->value.type))
+ if (!type_can_bit_cast(dest_type)) {
+ ir_add_error(ira, dest_type_value,
+ buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
+ }
+
+ return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type);
+}
+
+static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
+ ZigType *ptr_type)
+{
+ assert(get_src_ptr_type(ptr_type) != nullptr);
+ assert(type_has_bits(ptr_type));
IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize);
if (type_is_invalid(casted_int->value.type))
@@ -20815,19 +20876,48 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru
if (!val)
return ira->codegen->invalid_instruction;
- IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
+ IrInstruction *result = ir_const(ira, source_instr, ptr_type);
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint);
return result;
}
- IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope,
- instruction->base.source_node, nullptr, casted_int);
- result->value.type = dest_type;
+ IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, nullptr, casted_int);
+ result->value.type = ptr_type;
return result;
}
+static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
+ Error err;
+ IrInstruction *dest_type_value = instruction->dest_type->child;
+ ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
+ if (type_is_invalid(dest_type))
+ return ira->codegen->invalid_instruction;
+
+ // We explicitly check for the size, so we can use get_src_ptr_type
+ if (get_src_ptr_type(dest_type) == nullptr) {
+ ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
+ return ira->codegen->invalid_instruction;
+ if (!type_has_bits(dest_type)) {
+ ir_add_error(ira, dest_type_value,
+ buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+
+ IrInstruction *target = instruction->target->child;
+ if (type_is_invalid(target->value.type))
+ return ira->codegen->invalid_instruction;
+
+ return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type);
+}
+
static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
IrInstructionDeclRef *instruction)
{
diff --git a/src/parser.cpp b/src/parser.cpp
index 1c1af87c51..160a7268b0 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -2779,6 +2779,7 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) {
// <- ASTERISK
// / ASTERISK2
// / LBRACKET ASTERISK RBRACKET
+// / LBRACKET ASTERISK C RBRACKET
static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
Token *asterisk = eat_token_if(pc, TokenIdStar);
if (asterisk != nullptr) {
@@ -2804,6 +2805,13 @@ static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
return res;
}
+ Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket);
+ if (cptr != nullptr) {
+ AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr);
+ res->data.pointer_type.star_token = cptr;
+ return res;
+ }
+
return nullptr;
}
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 3acd605748..9ff6ed3bbe 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -221,6 +221,7 @@ enum TokenizeState {
TokenizeStateError,
TokenizeStateLBracket,
TokenizeStateLBracketStar,
+ TokenizeStateLBracketStarC,
};
@@ -846,7 +847,6 @@ void tokenize(Buf *buf, Tokenization *out) {
switch (c) {
case '*':
t.state = TokenizeStateLBracketStar;
- set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
break;
default:
// reinterpret as just an lbracket
@@ -857,6 +857,21 @@ void tokenize(Buf *buf, Tokenization *out) {
}
break;
case TokenizeStateLBracketStar:
+ switch (c) {
+ case 'c':
+ t.state = TokenizeStateLBracketStarC;
+ set_token_id(&t, t.cur_tok, TokenIdBracketStarCBracket);
+ break;
+ case ']':
+ set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
+ end_token(&t);
+ t.state = TokenizeStateStart;
+ break;
+ default:
+ invalid_char_error(&t, c);
+ }
+ break;
+ case TokenizeStateLBracketStarC:
switch (c) {
case ']':
end_token(&t);
@@ -1491,6 +1506,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateLineStringContinue:
case TokenizeStateLineStringContinueC:
case TokenizeStateLBracketStar:
+ case TokenizeStateLBracketStarC:
tokenize_error(&t, "unexpected EOF");
break;
case TokenizeStateLineComment:
@@ -1528,6 +1544,7 @@ const char * token_name(TokenId id) {
case TokenIdBitShiftRightEq: return ">>=";
case TokenIdBitXorEq: return "^=";
case TokenIdBracketStarBracket: return "[*]";
+ case TokenIdBracketStarCBracket: return "[*c]";
case TokenIdCharLiteral: return "CharLiteral";
case TokenIdCmpEq: return "==";
case TokenIdCmpGreaterOrEq: return ">=";
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index 17f36699b3..62117b5779 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -29,6 +29,7 @@ enum TokenId {
TokenIdBitShiftRightEq,
TokenIdBitXorEq,
TokenIdBracketStarBracket,
+ TokenIdBracketStarCBracket,
TokenIdCharLiteral,
TokenIdCmpEq,
TokenIdCmpGreaterOrEq,
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 47b19700ee..6969e151df 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -42,3 +42,9 @@ test "double pointer parsing" {
fn PtrOf(comptime T: type) type {
return *T;
}
+
+test "assigning integer to C pointer" {
+ var x: i32 = 0;
+ var ptr: [*c]u8 = 0;
+ var ptr2: [*c]u8 = x;
+}
--
cgit v1.2.3
From d9e01be97386f008e4a4b4281658f25b50ff80f1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 14:56:59 -0500
Subject: translate-c: use C pointer type everywhere
See #1059
---
src/analyze.cpp | 9 ++++++++
src/analyze.hpp | 1 +
src/ast_render.cpp | 27 ++++++++++++-----------
src/ast_render.hpp | 3 ---
src/translate_c.cpp | 50 ++++++++++++++----------------------------
test/translate_c.zig | 62 ++++++++++++++++++++++++++--------------------------
6 files changed, 72 insertions(+), 80 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index e561050e0d..691579200e 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6872,3 +6872,12 @@ Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_no
return ErrorNone;
}
+
+const char *container_string(ContainerKind kind) {
+ switch (kind) {
+ case ContainerKindEnum: return "enum";
+ case ContainerKindStruct: return "struct";
+ case ContainerKindUnion: return "union";
+ }
+ zig_unreachable();
+}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 9773782510..1e4f2f2ce7 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -215,6 +215,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty);
bool type_is_c_abi_int(CodeGen *g, ZigType *ty);
bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id);
+const char *container_string(ContainerKind kind);
uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field);
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 34a7faa2a5..7b57841205 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -136,13 +136,19 @@ static const char *thread_local_string(Token *tok) {
return (tok == nullptr) ? "" : "threadlocal ";
}
-const char *container_string(ContainerKind kind) {
- switch (kind) {
- case ContainerKindEnum: return "enum";
- case ContainerKindStruct: return "struct";
- case ContainerKindUnion: return "union";
+static const char *token_to_ptr_len_str(Token *tok) {
+ assert(tok != nullptr);
+ switch (tok->id) {
+ case TokenIdStar:
+ case TokenIdStarStar:
+ return "*";
+ case TokenIdBracketStarBracket:
+ return "[*]";
+ case TokenIdBracketStarCBracket:
+ return "[*c]";
+ default:
+ zig_unreachable();
}
- zig_unreachable();
}
static const char *node_type_str(NodeType node_type) {
@@ -644,13 +650,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypePointerType:
{
if (!grouped) fprintf(ar->f, "(");
- const char *star = "[*]";
- if (node->data.pointer_type.star_token != nullptr &&
- (node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar))
- {
- star = "*";
- }
- fprintf(ar->f, "%s", star);
+ const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token);
+ fprintf(ar->f, "%s", ptr_len_str);
if (node->data.pointer_type.align_expr != nullptr) {
fprintf(ar->f, "align(");
render_node_grouped(ar, node->data.pointer_type.align_expr);
diff --git a/src/ast_render.hpp b/src/ast_render.hpp
index d37002d8c7..1652156eee 100644
--- a/src/ast_render.hpp
+++ b/src/ast_render.hpp
@@ -17,7 +17,4 @@ void ast_print(FILE *f, AstNode *node, int indent);
void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size);
-const char *container_string(ContainerKind kind);
-
#endif
-
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index 02fa3b24be..b06a28d12d 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -291,11 +291,22 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod
node);
}
+static TokenId ptr_len_to_token_id(PtrLen ptr_len) {
+ switch (ptr_len) {
+ case PtrLenSingle:
+ return TokenIdStar;
+ case PtrLenUnknown:
+ return TokenIdBracketStarBracket;
+ case PtrLenC:
+ return TokenIdBracketStarCBracket;
+ }
+ zig_unreachable();
+}
+
static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) {
AstNode *node = trans_create_node(c, NodeTypePointerType);
node->data.pointer_type.star_token = allocate(1);
- node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket;
- node->data.pointer_type.is_const = is_const;
+ node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len);
node->data.pointer_type.is_const = is_const;
node->data.pointer_type.is_volatile = is_volatile;
node->data.pointer_type.op_expr = child_node;
@@ -752,30 +763,6 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
}
}
-static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) {
- switch (ty->getTypeClass()) {
- case Type::Builtin: {
- const BuiltinType *builtin_ty = static_cast(ty);
- return builtin_ty->getKind() == BuiltinType::Void;
- }
- case Type::Record: {
- const RecordType *record_ty = static_cast(ty);
- return record_ty->getDecl()->getDefinition() == nullptr;
- }
- case Type::Elaborated: {
- const ElaboratedType *elaborated_ty = static_cast(ty);
- return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc);
- }
- case Type::Typedef: {
- const TypedefType *typedef_ty = static_cast(ty);
- const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
- return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc);
- }
- default:
- return false;
- }
-}
-
static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
switch (ty->getTypeClass()) {
case Type::Builtin:
@@ -925,11 +912,8 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
}
- PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown;
-
- AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
- child_qt.isVolatileQualified(), child_node, ptr_len);
- return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
+ return trans_create_node_ptr_type(c, child_qt.isConstQualified(),
+ child_qt.isVolatileQualified(), child_node, PtrLenC);
}
case Type::Typedef:
{
@@ -1113,7 +1097,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
return nullptr;
}
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
- child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown);
+ child_qt.isVolatileQualified(), child_type_node, PtrLenC);
return pointer_node;
}
case Type::BlockPointer:
@@ -4568,7 +4552,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t
} else if (first_tok->id == CTokIdAsterisk) {
*tok_i += 1;
- node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown);
+ node = trans_create_node_ptr_type(c, false, false, node, PtrLenC);
} else {
return node;
}
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 13f2a964d0..746fa60b18 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -117,11 +117,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
,
\\pub const struct_Foo = extern struct {
- \\ a: ?[*]Foo,
+ \\ a: [*c]Foo,
\\};
\\pub const Foo = struct_Foo;
\\pub const struct_Bar = extern struct {
- \\ a: ?[*]Foo,
+ \\ a: [*c]Foo,
\\};
);
@@ -202,7 +202,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("restrict -> noalias",
\\void foo(void *restrict bar, void *restrict);
,
- \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
+ \\pub extern fn foo(noalias bar: [*c]c_void, noalias arg1: [*c]c_void) void;
);
cases.add("simple struct",
@@ -213,7 +213,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\const struct_Foo = extern struct {
\\ x: c_int,
- \\ y: ?[*]u8,
+ \\ y: [*c]u8,
\\};
,
\\pub const Foo = struct_Foo;
@@ -244,7 +244,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const BarB = enum_Bar.B;
,
- \\pub extern fn func(a: ?[*]struct_Foo, b: ?[*](?[*]enum_Bar)) void;
+ \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void;
,
\\pub const Foo = struct_Foo;
,
@@ -254,7 +254,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("constant size array",
\\void func(int array[20]);
,
- \\pub extern fn func(array: ?[*]c_int) void;
+ \\pub extern fn func(array: [*c]c_int) void;
);
cases.add("self referential struct with function pointer",
@@ -263,7 +263,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
,
\\pub const struct_Foo = extern struct {
- \\ derp: ?extern fn(?[*]struct_Foo) void,
+ \\ derp: ?extern fn([*c]struct_Foo) void,
\\};
,
\\pub const Foo = struct_Foo;
@@ -275,7 +275,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const struct_Foo = @OpaqueType();
,
- \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
+ \\pub extern fn some_func(foo: [*c]struct_Foo, x: c_int) [*c]struct_Foo;
,
\\pub const Foo = struct_Foo;
);
@@ -322,11 +322,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\};
,
\\pub const struct_Bar = extern struct {
- \\ next: ?[*]struct_Foo,
+ \\ next: [*c]struct_Foo,
\\};
,
\\pub const struct_Foo = extern struct {
- \\ next: ?[*]struct_Bar,
+ \\ next: [*c]struct_Bar,
\\};
);
@@ -336,7 +336,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const Foo = c_void;
,
- \\pub extern fn fun(a: ?*Foo) Foo;
+ \\pub extern fn fun(a: [*c]Foo) Foo;
);
cases.add("generate inline func for #define global extern fn",
@@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 6;
\\}
,
- \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
+ \\pub export fn and_or_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0;
\\ if ((b != 0) and (c != null)) return 1;
\\ if ((a != 0) and (c != null)) return 2;
@@ -710,7 +710,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const struct_Foo = extern struct {
\\ field: c_int,
\\};
- \\pub export fn read_field(foo: ?[*]struct_Foo) c_int {
+ \\pub export fn read_field(foo: [*c]struct_Foo) c_int {
\\ return foo.?.field;
\\}
);
@@ -756,8 +756,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return x;
\\}
,
- \\pub export fn foo(x: ?[*]c_ushort) ?*c_void {
- \\ return @ptrCast(?*c_void, x);
+ \\pub export fn foo(x: [*c]c_ushort) [*c]c_void {
+ \\ return @ptrCast([*c]c_void, x);
\\}
);
@@ -777,7 +777,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 0;
\\}
,
- \\pub export fn foo() ?[*]c_int {
+ \\pub export fn foo() [*c]c_int {
\\ return null;
\\}
);
@@ -1086,7 +1086,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ *x = 1;
\\}
,
- \\pub export fn foo(x: ?[*]c_int) void {
+ \\pub export fn foo(x: [*c]c_int) void {
\\ x.?.* = 1;
\\}
);
@@ -1114,7 +1114,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub fn foo() c_int {
\\ var x: c_int = 1234;
- \\ var ptr: ?[*]c_int = &x;
+ \\ var ptr: [*c]c_int = &x;
\\ return ptr.?.*;
\\}
);
@@ -1124,7 +1124,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return "bar";
\\}
,
- \\pub fn foo() ?[*]const u8 {
+ \\pub fn foo() [*c]const u8 {
\\ return c"bar";
\\}
);
@@ -1253,8 +1253,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return (float *)a;
\\}
,
- \\fn ptrcast(a: ?[*]c_int) ?[*]f32 {
- \\ return @ptrCast(?[*]f32, a);
+ \\fn ptrcast(a: [*c]c_int) [*c]f32 {
+ \\ return @ptrCast([*c]f32, a);
\\}
);
@@ -1276,7 +1276,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return !c;
\\}
,
- \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
+ \\pub fn foo(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ return !(a == 0);
\\ return !(a != 0);
\\ return !(b != 0);
@@ -1297,7 +1297,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("const ptr initializer",
\\static const char *v0 = "0.0.0";
,
- \\pub var v0: ?[*]const u8 = c"0.0.0";
+ \\pub var v0: [*c]const u8 = c"0.0.0";
);
cases.add("static incomplete array inside function",
@@ -1306,17 +1306,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
,
\\pub fn foo() void {
- \\ const v2: [*]const u8 = c"2.2.2";
+ \\ const v2: [*c]const u8 = c"2.2.2";
\\}
);
cases.add("macro pointer cast",
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
,
- \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*]NRF_GPIO_Type)(NRF_GPIO_BASE);
+ \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*c]NRF_GPIO_Type)(NRF_GPIO_BASE);
);
- cases.add("if on none bool",
+ cases.add("if on non-bool",
\\enum SomeEnum { A, B, C };
\\int if_none_bool(int a, float b, void *c, enum SomeEnum d) {
\\ if (a) return 0;
@@ -1334,7 +1334,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ B,
\\ C,
\\};
- \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
+ \\pub fn if_none_bool(a: c_int, b: f32, c: [*c]c_void, d: enum_SomeEnum) c_int {
\\ if (a != 0) return 0;
\\ if (b != 0) return 1;
\\ if (c != null) return 2;
@@ -1343,7 +1343,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
);
- cases.add("while on none bool",
+ cases.add("while on non-bool",
\\int while_none_bool(int a, float b, void *c) {
\\ while (a) return 0;
\\ while (b) return 1;
@@ -1351,7 +1351,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 3;
\\}
,
- \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
+ \\pub fn while_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != null) return 2;
@@ -1359,7 +1359,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
);
- cases.add("for on none bool",
+ cases.add("for on non-bool",
\\int for_none_bool(int a, float b, void *c) {
\\ for (;a;) return 0;
\\ for (;b;) return 1;
@@ -1367,7 +1367,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 3;
\\}
,
- \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
+ \\pub fn for_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != null) return 2;
--
cgit v1.2.3
From 342bca7f4627454435e9f6c2d12b099f95a2fd47 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 15:31:09 -0500
Subject: C pointer comparison and arithmetic
See #1059
---
src/analyze.cpp | 2 +-
src/codegen.cpp | 4 ++--
src/ir.cpp | 46 ++++++++++++++++++++++++++++++++++-----
src/translate_c.cpp | 8 ++++---
test/stage1/behavior/pointers.zig | 20 +++++++++++++++++
test/translate_c.zig | 18 +++++++--------
6 files changed, 78 insertions(+), 20 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 691579200e..af6200cc82 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -434,7 +434,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
{
assert(!type_is_invalid(child_type));
- assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
+ assert(ptr_len != PtrLenUnknown || child_type->id != ZigTypeIdOpaque);
if (byte_alignment != 0) {
uint32_t abi_alignment = get_abi_alignment(g, child_type);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 4868576b49..605ea59b06 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -2657,7 +2657,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
(op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) ||
(op1->value.type->id == ZigTypeIdPointer &&
(op_id == IrBinOpAdd || op_id == IrBinOpSub) &&
- op1->value.type->data.pointer.ptr_len == PtrLenUnknown)
+ op1->value.type->data.pointer.ptr_len != PtrLenSingle)
);
ZigType *operand_type = op1->value.type;
ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type;
@@ -2716,7 +2716,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
AddSubMulMul;
if (scalar_type->id == ZigTypeIdPointer) {
- assert(scalar_type->data.pointer.ptr_len == PtrLenUnknown);
+ assert(scalar_type->data.pointer.ptr_len != PtrLenSingle);
LLVMValueRef subscript_value;
if (operand_type->id == ZigTypeIdVector)
zig_panic("TODO: Implement vector operations on pointers.");
diff --git a/src/ir.cpp b/src/ir.cpp
index 30350d75de..bc37ac9b54 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8943,7 +8943,9 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
*errors = reallocate(*errors, old_errors_count, *errors_count);
}
-static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, IrInstruction **instructions, size_t instruction_count) {
+static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type,
+ IrInstruction **instructions, size_t instruction_count)
+{
Error err;
assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0];
@@ -9260,6 +9262,19 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
continue;
}
+ if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC &&
+ (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt))
+ {
+ continue;
+ }
+
+ if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC &&
+ (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt))
+ {
+ prev_inst = cur_inst;
+ continue;
+ }
+
if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) {
continue;
}
@@ -11852,7 +11867,6 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
case ZigTypeIdBool:
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
- case ZigTypeIdPointer:
case ZigTypeIdErrorSet:
case ZigTypeIdFn:
case ZigTypeIdOpaque:
@@ -11864,6 +11878,10 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
operator_allowed = is_equality_cmp;
break;
+ case ZigTypeIdPointer:
+ operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len != PtrLenSingle);
+ break;
+
case ZigTypeIdUnreachable:
case ZigTypeIdArray:
case ZigTypeIdStruct:
@@ -12324,6 +12342,26 @@ static bool ok_float_op(IrBinOp op) {
zig_unreachable();
}
+static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
+ if (lhs_type->id != ZigTypeIdPointer)
+ return false;
+ switch (op) {
+ case IrBinOpAdd:
+ case IrBinOpSub:
+ break;
+ default:
+ return false;
+ }
+ switch (lhs_type->data.pointer.ptr_len) {
+ case PtrLenSingle:
+ return false;
+ case PtrLenUnknown:
+ case PtrLenC:
+ break;
+ }
+ return true;
+}
+
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
@@ -12336,9 +12374,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
IrBinOp op_id = instruction->op_id;
// look for pointer math
- if (op1->value.type->id == ZigTypeIdPointer && op1->value.type->data.pointer.ptr_len == PtrLenUnknown &&
- (op_id == IrBinOpAdd || op_id == IrBinOpSub))
- {
+ if (is_pointer_arithmetic_allowed(op1->value.type, op_id)) {
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize);
if (casted_op2 == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index b06a28d12d..63f04dae6c 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -1677,7 +1677,7 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im
return node;
}
case CK_NullToPointer:
- return trans_create_node(c, NodeTypeNullLiteral);
+ return trans_create_node_unsigned(c, 0);
case CK_Dependent:
emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent");
return nullptr;
@@ -2409,7 +2409,8 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *
case BuiltinType::Float16:
return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false));
case BuiltinType::NullPtr:
- return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
+ return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq,
+ trans_create_node_unsigned(c, 0));
case BuiltinType::Void:
case BuiltinType::Half:
@@ -2494,7 +2495,8 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *
break;
}
case Type::Pointer:
- return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral));
+ return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq,
+ trans_create_node_unsigned(c, 0));
case Type::Typedef:
{
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 79832bc316..63e4c314b1 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -56,3 +56,23 @@ test "implicit cast single item pointer to C pointer and back" {
z.* += 1;
expect(y == 12);
}
+
+test "C pointer comparison and arithmetic" {
+ var one: usize = 1;
+ var ptr1: [*c]u8 = 0;
+ var ptr2 = ptr1 + 10;
+ expect(ptr1 == 0);
+ expect(ptr1 >= 0);
+ expect(ptr1 <= 0);
+ expect(ptr1 < 1);
+ expect(ptr1 < one);
+ expect(1 > ptr1);
+ expect(one > ptr1);
+ expect(ptr1 < ptr2);
+ expect(ptr2 > ptr1);
+ expect(ptr2 >= 10);
+ expect(ptr2 == 10);
+ expect(ptr2 <= 10);
+ ptr2 -= 10;
+ expect(ptr1 == ptr2);
+}
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 746fa60b18..b87b962edc 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -610,11 +610,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub export fn and_or_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0;
- \\ if ((b != 0) and (c != null)) return 1;
- \\ if ((a != 0) and (c != null)) return 2;
+ \\ if ((b != 0) and (c != 0)) return 1;
+ \\ if ((a != 0) and (c != 0)) return 2;
\\ if ((a != 0) or (b != 0)) return 3;
- \\ if ((b != 0) or (c != null)) return 4;
- \\ if ((a != 0) or (c != null)) return 5;
+ \\ if ((b != 0) or (c != 0)) return 4;
+ \\ if ((a != 0) or (c != 0)) return 5;
\\ return 6;
\\}
);
@@ -778,7 +778,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
,
\\pub export fn foo() [*c]c_int {
- \\ return null;
+ \\ return 0;
\\}
);
@@ -1280,7 +1280,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return !(a == 0);
\\ return !(a != 0);
\\ return !(b != 0);
- \\ return !(c != null);
+ \\ return !(c != 0);
\\}
);
@@ -1337,7 +1337,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn if_none_bool(a: c_int, b: f32, c: [*c]c_void, d: enum_SomeEnum) c_int {
\\ if (a != 0) return 0;
\\ if (b != 0) return 1;
- \\ if (c != null) return 2;
+ \\ if (c != 0) return 2;
\\ if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3;
\\ return 4;
\\}
@@ -1354,7 +1354,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn while_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
- \\ while (c != null) return 2;
+ \\ while (c != 0) return 2;
\\ return 3;
\\}
);
@@ -1370,7 +1370,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn for_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
- \\ while (c != null) return 2;
+ \\ while (c != 0) return 2;
\\ return 3;
\\}
);
--
cgit v1.2.3
From 285e2f62ba0648d6d8e7ff64d1ee7d2900481e2f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 12 Feb 2019 00:51:06 -0500
Subject: disallow C pointers to non-C-ABI-compatible element types
See #1059
---
src/analyze.cpp | 2 +-
src/analyze.hpp | 2 +-
src/ir.cpp | 4 ++++
test/compile_errors.zig | 10 ++++++++++
4 files changed, 16 insertions(+), 2 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index af6200cc82..ab2afba561 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -1469,7 +1469,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) {
zig_unreachable();
}
-static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
+bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
switch (type_entry->id) {
case ZigTypeIdInvalid:
zig_unreachable();
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 1e4f2f2ce7..c8141b02ff 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -44,7 +44,7 @@ void find_libc_include_path(CodeGen *g);
void find_libc_lib_path(CodeGen *g);
bool type_has_bits(ZigType *type_entry);
-
+bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
diff --git a/src/ir.cpp b/src/ir.cpp
index 1f0edc910d..64e08ef7ea 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -21145,6 +21145,10 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct
} else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) {
ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
return ira->codegen->invalid_instruction;
+ } else if (instruction->ptr_len == PtrLenC && !type_allowed_in_extern(ira->codegen, child_type)) {
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
+ return ira->codegen->invalid_instruction;
}
uint32_t align_bytes;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index b47cdf2ed1..63850bb888 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,16 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "C pointer pointing to non C ABI compatible type",
+ \\const Foo = struct {};
+ \\export fn entry() [*c]Foo {
+ \\ return undefined;
+ \\}
+ ,
+ ".tmp_source.zig:2:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'",
+ );
+
cases.addTest(
"@truncate undefined value",
\\export fn entry() void {
--
cgit v1.2.3
From 5699ab5e77f8d13cac1e34775e6e51358119965c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 12 Feb 2019 18:20:00 -0500
Subject: C pointers: errors for nested pointer casting regarding null
See #1059
---
src/all_types.hpp | 12 +++--
src/analyze.cpp | 45 ++++++++++++++-----
src/ir.cpp | 99 +++++++++++++++++++++++++++++------------
test/compile_errors.zig | 114 +++++++++++++++++++++++++++++-------------------
4 files changed, 181 insertions(+), 89 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index fd66b77ad2..230dba9a42 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -691,15 +691,17 @@ struct AstNodePointerType {
AstNode *align_expr;
BigInt *bit_offset_start;
BigInt *host_int_bytes;
+ AstNode *op_expr;
+ Token *allow_zero_token;
bool is_const;
bool is_volatile;
- AstNode *op_expr;
};
struct AstNodeArrayType {
AstNode *size;
AstNode *child_type;
AstNode *align_expr;
+ Token *allow_zero_token;
bool is_const;
bool is_volatile;
};
@@ -1050,6 +1052,7 @@ struct ZigTypePointer {
uint32_t host_int_bytes; // size of host integer. 0 means no host integer; this field is aligned
bool is_const;
bool is_volatile;
+ bool allow_zero;
};
struct ZigTypeInt {
@@ -1499,11 +1502,12 @@ struct TypeId {
struct {
ZigType *child_type;
PtrLen ptr_len;
- bool is_const;
- bool is_volatile;
uint32_t alignment;
uint32_t bit_offset_in_host;
uint32_t host_int_bytes;
+ bool is_const;
+ bool is_volatile;
+ bool allow_zero;
} pointer;
struct {
ZigType *child_type;
@@ -2592,6 +2596,7 @@ struct IrInstructionPtrType {
PtrLen ptr_len;
bool is_const;
bool is_volatile;
+ bool allow_zero;
};
struct IrInstructionPromiseType {
@@ -2607,6 +2612,7 @@ struct IrInstructionSliceType {
IrInstruction *child_type;
bool is_const;
bool is_volatile;
+ bool allow_zero;
};
struct IrInstructionAsm {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index ab2afba561..900def52d4 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -433,6 +433,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes)
{
+ // TODO when implementing https://github.com/ziglang/zig/issues/1953
+ // move this to a parameter
+ bool allow_zero = (ptr_len == PtrLenC);
assert(!type_is_invalid(child_type));
assert(ptr_len != PtrLenUnknown || child_type->id != ZigTypeIdOpaque);
@@ -452,7 +455,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
TypeId type_id = {};
ZigType **parent_pointer = nullptr;
- if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle) {
+ if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || allow_zero) {
type_id.id = ZigTypeIdPointer;
type_id.data.pointer.child_type = child_type;
type_id.data.pointer.is_const = is_const;
@@ -461,6 +464,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
type_id.data.pointer.bit_offset_in_host = bit_offset_in_host;
type_id.data.pointer.host_int_bytes = host_int_bytes;
type_id.data.pointer.ptr_len = ptr_len;
+ type_id.data.pointer.allow_zero = allow_zero;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry)
@@ -481,18 +485,28 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
const char *star_str = ptr_len_to_star_str(ptr_len);
const char *const_str = is_const ? "const " : "";
const char *volatile_str = is_volatile ? "volatile " : "";
+ const char *allow_zero_str;
+ if (ptr_len == PtrLenC) {
+ assert(allow_zero);
+ allow_zero_str = "";
+ } else {
+ allow_zero_str = allow_zero ? "allowzero " : "";
+ }
buf_resize(&entry->name, 0);
if (host_int_bytes == 0 && byte_alignment == 0) {
- buf_appendf(&entry->name, "%s%s%s%s", star_str, const_str, volatile_str, buf_ptr(&child_type->name));
+ buf_appendf(&entry->name, "%s%s%s%s%s",
+ star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (host_int_bytes == 0) {
- buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s", star_str, byte_alignment,
- const_str, volatile_str, buf_ptr(&child_type->name));
+ buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
+ const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
} else if (byte_alignment == 0) {
- buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str,
- bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name));
+ buf_appendf(&entry->name, "%salign(:%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str,
+ bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
+ buf_ptr(&child_type->name));
} else {
- buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s", star_str, byte_alignment,
- bit_offset_in_host, host_int_bytes, const_str, volatile_str, buf_ptr(&child_type->name));
+ buf_appendf(&entry->name, "%salign(%" PRIu32 ":%" PRIu32 ":%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
+ bit_offset_in_host, host_int_bytes, const_str, volatile_str, allow_zero_str,
+ buf_ptr(&child_type->name));
}
assert(child_type->id != ZigTypeIdInvalid);
@@ -500,7 +514,9 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
entry->zero_bits = !type_has_bits(child_type);
if (!entry->zero_bits) {
- if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle || bit_offset_in_host != 0) {
+ if (is_const || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
+ bit_offset_in_host != 0 || allow_zero)
+ {
ZigType *peer_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenSingle, 0, 0, host_int_bytes);
entry->type_ref = peer_type->type_ref;
@@ -534,6 +550,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
entry->data.pointer.explicit_alignment = byte_alignment;
entry->data.pointer.bit_offset_in_host = bit_offset_in_host;
entry->data.pointer.host_int_bytes = host_int_bytes;
+ entry->data.pointer.allow_zero = allow_zero;
if (parent_pointer) {
*parent_pointer = entry;
@@ -850,7 +867,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
ZigType *child_type = ptr_type->data.pointer.child_type;
if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile ||
- ptr_type->data.pointer.explicit_alignment != 0)
+ ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero)
{
ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false,
PtrLenUnknown, 0, 0, 0);
@@ -873,7 +890,7 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) {
ZigType *child_ptr_type = child_type->data.structure.fields[slice_ptr_index].type_entry;
assert(child_ptr_type->id == ZigTypeIdPointer);
if (child_ptr_type->data.pointer.is_const || child_ptr_type->data.pointer.is_volatile ||
- child_ptr_type->data.pointer.explicit_alignment != 0)
+ child_ptr_type->data.pointer.explicit_alignment != 0 || child_ptr_type->data.pointer.allow_zero)
{
ZigType *grand_child_type = child_ptr_type->data.pointer.child_type;
ZigType *bland_child_ptr_type = get_pointer_to_type_extra(g, grand_child_type, false, false,
@@ -4053,7 +4070,9 @@ ZigType *get_src_ptr_type(ZigType *type) {
if (type->id == ZigTypeIdFn) return type;
if (type->id == ZigTypeIdPromise) return type;
if (type->id == ZigTypeIdOptional) {
- if (type->data.maybe.child_type->id == ZigTypeIdPointer) return type->data.maybe.child_type;
+ if (type->data.maybe.child_type->id == ZigTypeIdPointer) {
+ return type->data.maybe.child_type->data.pointer.allow_zero ? nullptr : type->data.maybe.child_type;
+ }
if (type->data.maybe.child_type->id == ZigTypeIdFn) return type->data.maybe.child_type;
if (type->data.maybe.child_type->id == ZigTypeIdPromise) return type->data.maybe.child_type;
}
@@ -6289,6 +6308,7 @@ uint32_t type_id_hash(TypeId x) {
((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +
(x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
(x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
+ (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
(((uint32_t)x.data.pointer.alignment) ^ (uint32_t)0x777fbe0e) +
(((uint32_t)x.data.pointer.bit_offset_in_host) ^ (uint32_t)2639019452) +
(((uint32_t)x.data.pointer.host_int_bytes) ^ (uint32_t)529908881);
@@ -6339,6 +6359,7 @@ bool type_id_eql(TypeId a, TypeId b) {
a.data.pointer.ptr_len == b.data.pointer.ptr_len &&
a.data.pointer.is_const == b.data.pointer.is_const &&
a.data.pointer.is_volatile == b.data.pointer.is_volatile &&
+ a.data.pointer.allow_zero == b.data.pointer.allow_zero &&
a.data.pointer.alignment == b.data.pointer.alignment &&
a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes;
diff --git a/src/ir.cpp b/src/ir.cpp
index 50b5661e12..19bec193d5 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -61,7 +61,7 @@ enum ConstCastResultId {
ConstCastResultIdType,
ConstCastResultIdUnresolvedInferredErrSet,
ConstCastResultIdAsyncAllocatorType,
- ConstCastResultIdNullWrapPtr
+ ConstCastResultIdBadAllowsZero,
};
struct ConstCastOnly;
@@ -83,6 +83,7 @@ struct ConstCastErrUnionErrSetMismatch;
struct ConstCastErrUnionPayloadMismatch;
struct ConstCastErrSetMismatch;
struct ConstCastTypeMismatch;
+struct ConstCastBadAllowsZero;
struct ConstCastOnly {
ConstCastResultId id;
@@ -99,6 +100,7 @@ struct ConstCastOnly {
ConstCastOnly *null_wrap_ptr_child;
ConstCastArg fn_arg;
ConstCastArgNoAlias arg_no_alias;
+ ConstCastBadAllowsZero *bad_allows_zero;
} data;
};
@@ -141,6 +143,12 @@ struct ConstCastErrSetMismatch {
ZigList missing_errors;
};
+struct ConstCastBadAllowsZero {
+ ZigType *wanted_type;
+ ZigType *actual_type;
+};
+
+
enum UndefAllowed {
UndefOk,
UndefBad,
@@ -8636,6 +8644,14 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
return err_set_type;
}
+static bool ptr_allows_addr_zero(ZigType *ptr_type) {
+ if (ptr_type->id == ZigTypeIdPointer) {
+ return ptr_type->data.pointer.allow_zero;
+ } else if (ptr_type->id == ZigTypeIdOptional) {
+ return true;
+ }
+ return false;
+}
static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
@@ -8649,34 +8665,35 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
if (wanted_type == actual_type)
return result;
- // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively
- // but not if we want a mutable pointer
- // and not if the actual pointer has zero bits
- if (!wanted_is_mutable && wanted_type->id == ZigTypeIdOptional &&
- wanted_type->data.maybe.child_type->id == ZigTypeIdPointer &&
- actual_type->id == ZigTypeIdPointer && type_has_bits(actual_type))
- {
- ConstCastOnly child = types_match_const_cast_only(ira,
- wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable);
- if (child.id == ConstCastResultIdInvalid)
- return child;
- if (child.id != ConstCastResultIdOk) {
- result.id = ConstCastResultIdNullWrapPtr;
- result.data.null_wrap_ptr_child = allocate_nonzero(1);
- *result.data.null_wrap_ptr_child = child;
- }
- return result;
- }
-
- // pointer const
+ // If pointers have the same representation in memory, they can be "const-casted".
+ // `const` attribute can be gained
+ // `volatile` attribute can be gained
+ // `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer)
+ // but only if !wanted_is_mutable
+ // alignment can be decreased
+ // bit offset attributes must match exactly
+ // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
+ bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
+ bool actual_allows_zero = ptr_allows_addr_zero(actual_type);
bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC;
bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC;
- if ((wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) ||
- (wanted_ptr_type != nullptr && actual_is_c_ptr) ||
- (actual_ptr_type != nullptr && wanted_is_c_ptr))
- {
+ bool wanted_opt_or_ptr = wanted_ptr_type != nullptr &&
+ (wanted_type->id == ZigTypeIdPointer || wanted_type->id == ZigTypeIdOptional);
+ bool actual_opt_or_ptr = actual_ptr_type != nullptr &&
+ (actual_type->id == ZigTypeIdPointer || actual_type->id == ZigTypeIdOptional);
+ if (wanted_opt_or_ptr && actual_opt_or_ptr) {
+ bool ok_allows_zero = (wanted_allows_zero &&
+ (actual_allows_zero || wanted_ptr_type->data.pointer.is_const)) ||
+ (!wanted_allows_zero && !actual_allows_zero);
+ if (!ok_allows_zero) {
+ result.id = ConstCastResultIdBadAllowsZero;
+ result.data.bad_allows_zero = allocate_nonzero(1);
+ result.data.bad_allows_zero->wanted_type = wanted_type;
+ result.data.bad_allows_zero->actual_type = actual_type;
+ return result;
+ }
ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
if (child.id == ConstCastResultIdInvalid)
@@ -8699,6 +8716,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
}
bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) &&
+ type_has_bits(wanted_type) == type_has_bits(actual_type) &&
(!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
@@ -9922,7 +9940,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
if (undef_allowed == UndefOk) {
return &value->value;
} else {
- ir_add_error(ira, value, buf_sprintf("use of undefined value"));
+ ir_add_error(ira, value, buf_sprintf("use of undefined value here causes undefined behavior"));
return nullptr;
}
}
@@ -10828,6 +10846,26 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg);
break;
}
+ case ConstCastResultIdBadAllowsZero: {
+ bool wanted_allows_zero = ptr_allows_addr_zero(cast_result->data.bad_allows_zero->wanted_type);
+ bool actual_allows_zero = ptr_allows_addr_zero(cast_result->data.bad_allows_zero->actual_type);
+ ZigType *wanted_ptr_type = get_src_ptr_type(cast_result->data.bad_allows_zero->wanted_type);
+ ZigType *actual_ptr_type = get_src_ptr_type(cast_result->data.bad_allows_zero->actual_type);
+ ZigType *wanted_elem_type = wanted_ptr_type->data.pointer.child_type;
+ ZigType *actual_elem_type = actual_ptr_type->data.pointer.child_type;
+ if (actual_allows_zero && !wanted_allows_zero) {
+ add_error_note(ira->codegen, parent_msg, source_node,
+ buf_sprintf("'%s' could have null values which are illegal in type '%s'",
+ buf_ptr(&actual_elem_type->name),
+ buf_ptr(&wanted_elem_type->name)));
+ } else {
+ add_error_note(ira->codegen, parent_msg, source_node,
+ buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'",
+ buf_ptr(&cast_result->data.bad_allows_zero->wanted_type->name),
+ buf_ptr(&cast_result->data.bad_allows_zero->actual_type->name)));
+ }
+ break;
+ }
case ConstCastResultIdFnAlign: // TODO
case ConstCastResultIdFnCC: // TODO
case ConstCastResultIdFnVarArgs: // TODO
@@ -10838,7 +10876,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
case ConstCastResultIdFnArgNoAlias: // TODO
case ConstCastResultIdUnresolvedInferredErrSet: // TODO
case ConstCastResultIdAsyncAllocatorType: // TODO
- case ConstCastResultIdNullWrapPtr: // TODO
break;
}
}
@@ -20589,12 +20626,14 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
// We have a check for zero bits later so we use get_src_ptr_type to
// validate src_type and dest_type.
- if (get_src_ptr_type(src_type) == nullptr) {
+ ZigType *src_ptr_type = get_src_ptr_type(src_type);
+ if (src_ptr_type == nullptr) {
ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
return ira->codegen->invalid_instruction;
}
- if (get_src_ptr_type(dest_type) == nullptr) {
+ ZigType *dest_ptr_type = get_src_ptr_type(dest_type);
+ if (dest_ptr_type == nullptr) {
ir_add_error(ira, dest_type_src,
buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
@@ -20606,6 +20645,8 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
}
if (instr_is_comptime(ptr)) {
+ // Undefined is OK here; @ptrCast is defined to reinterpret the bit pattern
+ // of the pointer as the new pointer type.
ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk);
if (!val)
return ira->codegen->invalid_instruction;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 8f8e2a0bdf..c51a65cadf 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,30 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "implicit casting C pointers which would mess up null semantics",
+ \\export fn entry() void {
+ \\ var slice: []const u8 = "aoeu";
+ \\ const opt_many_ptr: [*]const u8 = slice.ptr;
+ \\ var ptr_opt_many_ptr = &opt_many_ptr;
+ \\ var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr;
+ \\ ptr_opt_many_ptr = c_ptr;
+ \\}
+ \\export fn entry2() void {
+ \\ var buf: [4]u8 = "aoeu";
+ \\ var slice: []u8 = &buf;
+ \\ var opt_many_ptr: [*]u8 = slice.ptr;
+ \\ var ptr_opt_many_ptr = &opt_many_ptr;
+ \\ var c_ptr: [*c]const [*c]u8 = ptr_opt_many_ptr;
+ \\}
+ ,
+ ".tmp_source.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'",
+ ".tmp_source.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'",
+ ".tmp_source.zig:13:35: error: expected type '[*c]const [*c]u8', found '*[*]u8'",
+ ".tmp_source.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]u8'",
+ ".tmp_source.zig:13:35: note: mutable '[*c]u8' allows illegal null values stored to type '[*]u8'",
+ );
+
cases.addTest(
"implicit casting too big integers to C pointers",
\\export fn a() void {
@@ -31,7 +55,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ var z = @truncate(u8, u16(undefined));
\\}
,
- ".tmp_source.zig:2:30: error: use of undefined value",
+ ".tmp_source.zig:2:30: error: use of undefined value here causes undefined behavior",
);
cases.addTest(
@@ -392,7 +416,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ f(i32);
\\}
,
- ".tmp_source.zig:4:5: error: use of undefined value",
+ ".tmp_source.zig:4:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -792,7 +816,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ command.exec();
\\}
,
- ".tmp_source.zig:6:12: error: use of undefined value",
+ ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -805,7 +829,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ command.exec();
\\}
,
- ".tmp_source.zig:6:12: error: use of undefined value",
+ ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2776,7 +2800,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\
\\export fn entry() usize { return @sizeOf(@typeOf(x)); }
,
- ".tmp_source.zig:1:15: error: use of undefined value",
+ ".tmp_source.zig:1:15: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2786,7 +2810,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a / a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2796,7 +2820,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a /= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2806,7 +2830,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a % a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2816,7 +2840,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a %= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2826,7 +2850,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a + a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2836,7 +2860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a += a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2846,7 +2870,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a +% a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2856,7 +2880,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a +%= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2866,7 +2890,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a - a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2876,7 +2900,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a -= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2886,7 +2910,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a -% a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2896,7 +2920,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a -%= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2906,7 +2930,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a * a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2916,7 +2940,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a *= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2926,7 +2950,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a *% a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2936,7 +2960,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a *%= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2946,7 +2970,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a << 2;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2956,7 +2980,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a <<= 2;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2966,7 +2990,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a >> 2;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2976,7 +3000,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a >>= 2;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2986,7 +3010,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a & a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -2996,7 +3020,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a &= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3006,7 +3030,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a | a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3016,7 +3040,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a |= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3026,7 +3050,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a ^ a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3036,7 +3060,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ a ^= a;
\\}
,
- ".tmp_source.zig:3:5: error: use of undefined value",
+ ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3046,7 +3070,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a == a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3056,7 +3080,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a != a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3066,7 +3090,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a > a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3076,7 +3100,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a >= a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3086,7 +3110,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a < a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3096,7 +3120,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a <= a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3106,7 +3130,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a and a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3116,7 +3140,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a or a;
\\}
,
- ".tmp_source.zig:3:9: error: use of undefined value",
+ ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3126,7 +3150,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = -a;
\\}
,
- ".tmp_source.zig:3:10: error: use of undefined value",
+ ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3136,7 +3160,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = -%a;
\\}
,
- ".tmp_source.zig:3:11: error: use of undefined value",
+ ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3146,7 +3170,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = ~a;
\\}
,
- ".tmp_source.zig:3:10: error: use of undefined value",
+ ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3156,7 +3180,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = !a;
\\}
,
- ".tmp_source.zig:3:10: error: use of undefined value",
+ ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3166,7 +3190,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a orelse false;
\\}
,
- ".tmp_source.zig:3:11: error: use of undefined value",
+ ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
);
cases.add(
@@ -3176,7 +3200,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ _ = a catch |err| false;
\\}
,
- ".tmp_source.zig:3:11: error: use of undefined value",
+ ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior",
);
cases.add(
--
cgit v1.2.3
From 0b3db784f1cf21dd35fc14dfee29ab6fd867c0ed Mon Sep 17 00:00:00 2001
From: Matthew McAllister
Date: Sun, 3 Feb 2019 01:15:51 -0800
Subject: Enable compileLog to display slices
---
src/analyze.cpp | 53 ++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 38 insertions(+), 15 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 970d1cc382..b3b4c36cf1 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6010,16 +6010,20 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const
}
}
-static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) {
- switch (const_val->data.x_array.special) {
+static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstExprValue *const_val, uint64_t start, uint64_t len) {
+ ConstArrayValue *array = &const_val->data.x_array;
+ switch (array->special) {
case ConstArraySpecialUndef:
buf_append_str(buf, "undefined");
return;
case ConstArraySpecialBuf: {
- Buf *array_buf = const_val->data.x_array.data.s_buf;
+ Buf *array_buf = array->data.s_buf;
+ const char *base = &buf_ptr(array_buf)[start];
+ assert(start + len <= buf_len(array_buf));
+
buf_append_char(buf, '"');
- for (size_t i = 0; i < buf_len(array_buf); i += 1) {
- uint8_t c = buf_ptr(array_buf)[i];
+ for (size_t i = 0; i < len; i += 1) {
+ uint8_t c = base[i];
if (c == '"') {
buf_append_str(buf, "\\\"");
} else {
@@ -6030,12 +6034,13 @@ static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_v
return;
}
case ConstArraySpecialNone: {
- buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name));
+ ConstExprValue *base = &array->data.s_none.elements[start];
+ assert(start + len <= const_val->type->data.array.len);
+
+ buf_appendf(buf, "%s{", buf_ptr(type_name));
for (uint64_t i = 0; i < len; i += 1) {
- if (i != 0)
- buf_appendf(buf, ",");
- ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i];
- render_const_value(g, buf, child_value);
+ if (i != 0) buf_appendf(buf, ",");
+ render_const_value(g, buf, &base[i]);
}
buf_appendf(buf, "}");
return;
@@ -6124,10 +6129,16 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
case ZigTypeIdPointer:
return render_const_val_ptr(g, buf, const_val, type_entry);
- case ZigTypeIdVector:
- return render_const_val_array(g, buf, const_val, type_entry->data.vector.len);
- case ZigTypeIdArray:
- return render_const_val_array(g, buf, const_val, type_entry->data.array.len);
+ case ZigTypeIdArray: {
+ uint64_t len = type_entry->data.array.len;
+ render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
+ return;
+ }
+ case ZigTypeIdVector: {
+ uint32_t len = type_entry->data.vector.len;
+ render_const_val_array(g, buf, &type_entry->name, const_val, 0, len);
+ return;
+ }
case ZigTypeIdNull:
{
buf_appendf(buf, "null");
@@ -6169,7 +6180,19 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
}
case ZigTypeIdStruct:
{
- buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
+ if (is_slice(type_entry)) {
+ ConstPtrValue *ptr = &const_val->data.x_struct.fields[slice_ptr_index].data.x_ptr;
+ assert(ptr->special == ConstPtrSpecialBaseArray);
+ ConstExprValue *array = ptr->data.base_array.array_val;
+ size_t start = ptr->data.base_array.elem_index;
+
+ ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index];
+ size_t len = bigint_as_unsigned(&len_val->data.x_bigint);
+
+ render_const_val_array(g, buf, &type_entry->name, array, start, len);
+ } else {
+ buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
+ }
return;
}
case ZigTypeIdEnum:
--
cgit v1.2.3
From 59de24817e8538434f35a20a401f40c2f0231a9a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 01:09:33 -0500
Subject: runtime safety check for casting null to pointer
see #1059
---
src/all_types.hpp | 3 +++
src/analyze.cpp | 9 +++++++++
src/analyze.hpp | 1 +
src/codegen.cpp | 19 +++++++++++++++++-
src/ir.cpp | 52 ++++++++++++++++++++++++-------------------------
test/runtime_safety.zig | 10 ++++++++++
6 files changed, 67 insertions(+), 27 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 230dba9a42..bafe316c3d 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1488,6 +1488,7 @@ enum PanicMsgId {
PanicMsgIdBadUnionField,
PanicMsgIdBadEnumValue,
PanicMsgIdFloatToInt,
+ PanicMsgIdPtrCastNull,
PanicMsgIdCount,
};
@@ -3001,12 +3002,14 @@ struct IrInstructionPtrCastSrc {
IrInstruction *dest_type;
IrInstruction *ptr;
+ bool safety_check_on;
};
struct IrInstructionPtrCastGen {
IrInstruction base;
IrInstruction *ptr;
+ bool safety_check_on;
};
struct IrInstructionBitCast {
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 900def52d4..6a8090a843 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6902,3 +6902,12 @@ const char *container_string(ContainerKind kind) {
}
zig_unreachable();
}
+
+bool ptr_allows_addr_zero(ZigType *ptr_type) {
+ if (ptr_type->id == ZigTypeIdPointer) {
+ return ptr_type->data.pointer.allow_zero;
+ } else if (ptr_type->id == ZigTypeIdOptional) {
+ return true;
+ }
+ return false;
+}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index c8141b02ff..50e841baa1 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -45,6 +45,7 @@ void find_libc_lib_path(CodeGen *g);
bool type_has_bits(ZigType *type_entry);
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
+bool ptr_allows_addr_zero(ZigType *ptr_type);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 142e8174f5..dbb13ca885 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -950,6 +950,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("invalid enum value");
case PanicMsgIdFloatToInt:
return buf_create_from_str("integer part of floating point value out of bounds");
+ case PanicMsgIdPtrCastNull:
+ return buf_create_from_str("cast causes pointer to be null");
}
zig_unreachable();
}
@@ -3028,7 +3030,22 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable,
return nullptr;
}
LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
- return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
+ LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
+ bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
+ if (!want_safety_check || ptr_allows_addr_zero(wanted_type))
+ return result_ptr;
+
+ LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr));
+ LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, "");
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail");
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk");
+ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_safety_crash(g, PanicMsgIdPtrCastNull);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ return result_ptr;
}
static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable,
diff --git a/src/ir.cpp b/src/ir.cpp
index f064adb128..dfbc36e02c 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -172,7 +172,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *out_val, ConstExprValue *ptr_val);
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
- ZigType *dest_type, IrInstruction *dest_type_src);
+ ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on);
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
@@ -2202,12 +2202,13 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo
}
static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction *dest_type, IrInstruction *ptr)
+ IrInstruction *dest_type, IrInstruction *ptr, bool safety_check_on)
{
IrInstructionPtrCastSrc *instruction = ir_build_instruction(
irb, scope, source_node);
instruction->dest_type = dest_type;
instruction->ptr = ptr;
+ instruction->safety_check_on = safety_check_on;
ir_ref_instruction(dest_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
@@ -2216,12 +2217,13 @@ static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNod
}
static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
- ZigType *ptr_type, IrInstruction *ptr)
+ ZigType *ptr_type, IrInstruction *ptr, bool safety_check_on)
{
IrInstructionPtrCastGen *instruction = ir_build_instruction(
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
instruction->base.value.type = ptr_type;
instruction->ptr = ptr;
+ instruction->safety_check_on = safety_check_on;
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
@@ -4505,7 +4507,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
- IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value);
+ IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, ptr_cast, lval);
}
case BuiltinFnIdBitCast:
@@ -6740,7 +6742,8 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
// TODO relies on Zig not re-ordering fields
- IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst);
+ IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
+ false);
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
@@ -6818,7 +6821,8 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
// TODO relies on Zig not re-ordering fields
- IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst);
+ IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
+ false);
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
@@ -7363,7 +7367,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
- IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr);
+ IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type,
+ coro_promise_ptr, false);
coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
@@ -7387,7 +7392,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_build_return(irb, coro_scope, node, undef);
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
- IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
+ IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr,
+ false);
irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
@@ -7465,9 +7471,10 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0));
IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
- IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr);
- IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
- irb->exec->coro_result_field_ptr);
+ IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
+ result_ptr, false);
+ IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node,
+ u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr, false);
IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node,
fn_entry->type_entry->data.fn.fn_type_id.return_type);
IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
@@ -7517,7 +7524,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
false, false, PtrLenUnknown, 0, 0, 0));
- IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe);
+ IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
+ coro_mem_ptr_maybe, false);
IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false);
IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr);
@@ -8644,15 +8652,6 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
return err_set_type;
}
-static bool ptr_allows_addr_zero(ZigType *ptr_type) {
- if (ptr_type->id == ZigTypeIdPointer) {
- return ptr_type->data.pointer.allow_zero;
- } else if (ptr_type->id == ZigTypeIdOptional) {
- return true;
- }
- return false;
-}
-
static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
{
@@ -11310,7 +11309,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
actual_type->data.pointer.host_int_bytes == dest_ptr_type->data.pointer.host_int_bytes &&
get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type))
{
- return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
+ return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
}
}
@@ -11352,7 +11351,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
actual_type->data.pointer.child_type, source_node,
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
{
- return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
+ return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
}
// cast from integer to C pointer
@@ -20616,7 +20615,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
}
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
- ZigType *dest_type, IrInstruction *dest_type_src)
+ ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on)
{
Error err;
@@ -20685,7 +20684,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
return ira->codegen->invalid_instruction;
}
- IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr);
+ IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
if (type_has_bits(dest_type) && !type_has_bits(src_type)) {
ErrorMsg *msg = ir_add_error(ira, source_instr,
@@ -20722,7 +20721,8 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
if (type_is_invalid(src_type))
return ira->codegen->invalid_instruction;
- return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value);
+ return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value,
+ instruction->safety_check_on);
}
static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index 821328b7a6..12cac64b3a 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -1,6 +1,16 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
+ cases.addRuntimeSafety("pointer casting null to non-optional pointer",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\pub fn main() void {
+ \\ var c_ptr: [*c]u8 = 0;
+ \\ var zig_ptr: *u8 = c_ptr;
+ \\}
+ );
+
cases.addRuntimeSafety("@intToEnum - no matching tag value",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
--
cgit v1.2.3
From 52c03de5c2495b369ae730ff203e5342e4f33a36 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 13:07:51 -0500
Subject: add missing compile error for OpaqueType inside structs/unions
closes #1862
---
src/analyze.cpp | 14 ++++++++++++++
test/compile_errors.zig | 21 +++++++++++++++++++++
2 files changed, 35 insertions(+)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 6a8090a843..90ce3d3371 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2679,6 +2679,13 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
buf_sprintf("enums, not structs, support field assignment"));
}
+ if (field_type->id == ZigTypeIdOpaque) {
+ add_node_error(g, field_node->data.struct_field.type,
+ buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs"));
+ struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+ continue;
+ }
+
switch (type_requires_comptime(g, field_type)) {
case ReqCompTimeYes:
struct_type->data.structure.requires_comptime = true;
@@ -2963,6 +2970,13 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
}
union_field->type_entry = field_type;
+ if (field_type->id == ZigTypeIdOpaque) {
+ add_node_error(g, field_node->data.struct_field.type,
+ buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in unions"));
+ union_type->data.unionation.is_invalid = true;
+ continue;
+ }
+
switch (type_requires_comptime(g, field_type)) {
case ReqCompTimeInvalid:
union_type->data.unionation.is_invalid = true;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 630386aa4f..ac8d413d2c 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,27 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "directly embedding opaque type in struct and union",
+ \\const O = @OpaqueType();
+ \\const Foo = struct {
+ \\ o: O,
+ \\};
+ \\const Bar = union {
+ \\ One: i32,
+ \\ Two: O,
+ \\};
+ \\export fn a() void {
+ \\ var foo: Foo = undefined;
+ \\}
+ \\export fn b() void {
+ \\ var bar: Bar = undefined;
+ \\}
+ ,
+ ".tmp_source.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs",
+ ".tmp_source.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions",
+ );
+
cases.addTest(
"implicit cast between C pointer and Zig pointer - bad const/align/child",
\\export fn a() void {
--
cgit v1.2.3
From df87044fd6588452755014d5909e0db1b776deb2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 16:10:12 -0500
Subject: omit nonnull attribute for C pointers
See #1059
---
src/analyze.cpp | 4 ++++
src/analyze.hpp | 1 +
src/codegen.cpp | 19 ++++++++++++++++---
src/target.cpp | 4 ++++
src/target.hpp | 1 +
5 files changed, 26 insertions(+), 3 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 90ce3d3371..55deafb3a8 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -4100,6 +4100,10 @@ ZigType *get_codegen_ptr_type(ZigType *type) {
return ty;
}
+bool type_is_nonnull_ptr(ZigType *type) {
+ return type_is_codegen_pointer(type) && !ptr_allows_addr_zero(type);
+}
+
bool type_is_codegen_pointer(ZigType *type) {
return get_codegen_ptr_type(type) == type;
}
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 50e841baa1..845fb62534 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -46,6 +46,7 @@ void find_libc_lib_path(CodeGen *g);
bool type_has_bits(ZigType *type_entry);
bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry);
bool ptr_allows_addr_zero(ZigType *ptr_type);
+bool type_is_nonnull_ptr(ZigType *type);
ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index dbb13ca885..bae9abe06d 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -617,9 +617,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
unsigned init_gen_i = 0;
if (!type_has_bits(return_type)) {
// nothing to do
- } else if (type_is_codegen_pointer(return_type)) {
+ } else if (type_is_nonnull_ptr(return_type)) {
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
} else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
+ // Sret pointers must not be address 0
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
if (cc_want_sret_attr(cc)) {
@@ -637,6 +638,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
if (err_ret_trace_arg_index != UINT32_MAX) {
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
}
@@ -1246,6 +1249,8 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@@ -1320,9 +1325,13 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)0, "noalias");
addLLVMArgAttr(fn_val, (unsigned)0, "writeonly");
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
addLLVMArgAttr(fn_val, (unsigned)1, "noalias");
addLLVMArgAttr(fn_val, (unsigned)1, "readonly");
@@ -1450,6 +1459,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
+ // Error return trace memory is in the stack, which is impossible to be at address 0
+ // on any architecture.
addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@@ -2051,7 +2062,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
case FnWalkIdAttrs: {
ZigType *ptr_type = get_codegen_ptr_type(ty);
if (ptr_type != nullptr) {
- if (ty->id != ZigTypeIdOptional) {
+ if (type_is_nonnull_ptr(ty)) {
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
}
if (ptr_type->data.pointer.is_const) {
@@ -2095,6 +2106,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
assert(handle_is_ptr(ty));
switch (fn_walk->id) {
case FnWalkIdAttrs:
+ // arrays passed to C ABI functions may not be at address 0
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
fn_walk->data.attrs.gen_i += 1;
@@ -2134,6 +2146,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_
case FnWalkIdAttrs:
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval");
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
+ // Byvalue parameters must not have address 0
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
fn_walk->data.attrs.gen_i += 1;
break;
@@ -2266,7 +2279,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) {
if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) {
addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly");
}
- if (param_type->id == ZigTypeIdPointer) {
+ if (type_is_nonnull_ptr(param_type)) {
addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull");
}
break;
diff --git a/src/target.cpp b/src/target.cpp
index 6fea79518c..b1434c6871 100644
--- a/src/target.cpp
+++ b/src/target.cpp
@@ -807,6 +807,10 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
zig_unreachable();
}
+bool target_allows_addr_zero(const ZigTarget *target) {
+ return target->os == OsFreestanding;
+}
+
const char *target_o_file_ext(ZigTarget *target) {
if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) {
return ".obj";
diff --git a/src/target.hpp b/src/target.hpp
index a87b12351a..99d1cadf56 100644
--- a/src/target.hpp
+++ b/src/target.hpp
@@ -135,5 +135,6 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target
ZigLLVM_OSType get_llvm_os_type(Os os_type);
bool target_is_arm(const ZigTarget *target);
+bool target_allows_addr_zero(const ZigTarget *target);
#endif
--
cgit v1.2.3
From d6e0d82c328b4f9d733364382cce0941a601e91a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 23:09:12 -0500
Subject: translate-c: back to *c_void for opaque types
See #1059
---
src/analyze.cpp | 2 +-
src/ir.cpp | 13 +++++++++----
src/translate_c.cpp | 34 ++++++++++++++++++++++++++++++++--
test/compile_errors.zig | 10 ++++++++++
test/translate_c.zig | 20 ++++++++++----------
5 files changed, 62 insertions(+), 17 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 55deafb3a8..1917784511 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -437,7 +437,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
// move this to a parameter
bool allow_zero = (ptr_len == PtrLenC);
assert(!type_is_invalid(child_type));
- assert(ptr_len != PtrLenUnknown || child_type->id != ZigTypeIdOpaque);
+ assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
if (byte_alignment != 0) {
uint32_t abi_alignment = get_abi_alignment(g, child_type);
diff --git a/src/ir.cpp b/src/ir.cpp
index 7c46b21717..707eac0181 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -21205,10 +21205,15 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct
} else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) {
ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
return ira->codegen->invalid_instruction;
- } else if (instruction->ptr_len == PtrLenC && !type_allowed_in_extern(ira->codegen, child_type)) {
- ir_add_error(ira, &instruction->base,
- buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
- return ira->codegen->invalid_instruction;
+ } else if (instruction->ptr_len == PtrLenC) {
+ if (!type_allowed_in_extern(ira->codegen, child_type)) {
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
+ return ira->codegen->invalid_instruction;
+ } else if (child_type->id == ZigTypeIdOpaque) {
+ ir_add_error(ira, &instruction->base, buf_sprintf("C pointers cannot point opaque types"));
+ return ira->codegen->invalid_instruction;
+ }
}
uint32_t align_bytes;
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index 63f04dae6c..42a7ab436d 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -763,6 +763,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
}
}
+static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) {
+ switch (ty->getTypeClass()) {
+ case Type::Builtin: {
+ const BuiltinType *builtin_ty = static_cast(ty);
+ return builtin_ty->getKind() == BuiltinType::Void;
+ }
+ case Type::Record: {
+ const RecordType *record_ty = static_cast(ty);
+ return record_ty->getDecl()->getDefinition() == nullptr;
+ }
+ case Type::Elaborated: {
+ const ElaboratedType *elaborated_ty = static_cast(ty);
+ return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc);
+ }
+ case Type::Typedef: {
+ const TypedefType *typedef_ty = static_cast(ty);
+ const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
+ return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc);
+ }
+ default:
+ return false;
+ }
+}
+
static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
switch (ty->getTypeClass()) {
case Type::Builtin:
@@ -912,8 +936,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
}
- return trans_create_node_ptr_type(c, child_qt.isConstQualified(),
- child_qt.isVolatileQualified(), child_node, PtrLenC);
+ if (type_is_opaque(c, child_qt.getTypePtr(), source_loc)) {
+ AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
+ child_qt.isVolatileQualified(), child_node, PtrLenSingle);
+ return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
+ } else {
+ return trans_create_node_ptr_type(c, child_qt.isConstQualified(),
+ child_qt.isVolatileQualified(), child_node, PtrLenC);
+ }
}
case Type::Typedef:
{
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index a2fd901197..1f641a9052 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,16 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "C pointer to c_void",
+ \\export fn a() void {
+ \\ var x: *c_void = undefined;
+ \\ var y: [*c]c_void = x;
+ \\}
+ ,
+ ".tmp_source.zig:3:12: error: C pointers cannot point opaque types",
+ );
+
cases.addTest(
"directly embedding opaque type in struct and union",
\\const O = @OpaqueType();
diff --git a/test/translate_c.zig b/test/translate_c.zig
index b87b962edc..7385427dbe 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -202,7 +202,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("restrict -> noalias",
\\void foo(void *restrict bar, void *restrict);
,
- \\pub extern fn foo(noalias bar: [*c]c_void, noalias arg1: [*c]c_void) void;
+ \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
);
cases.add("simple struct",
@@ -275,7 +275,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const struct_Foo = @OpaqueType();
,
- \\pub extern fn some_func(foo: [*c]struct_Foo, x: c_int) [*c]struct_Foo;
+ \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
,
\\pub const Foo = struct_Foo;
);
@@ -336,7 +336,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
,
\\pub const Foo = c_void;
,
- \\pub extern fn fun(a: [*c]Foo) Foo;
+ \\pub extern fn fun(a: ?*Foo) Foo;
);
cases.add("generate inline func for #define global extern fn",
@@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 6;
\\}
,
- \\pub export fn and_or_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
+ \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0;
\\ if ((b != 0) and (c != 0)) return 1;
\\ if ((a != 0) and (c != 0)) return 2;
@@ -756,8 +756,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return x;
\\}
,
- \\pub export fn foo(x: [*c]c_ushort) [*c]c_void {
- \\ return @ptrCast([*c]c_void, x);
+ \\pub export fn foo(x: [*c]c_ushort) ?*c_void {
+ \\ return @ptrCast(?*c_void, x);
\\}
);
@@ -1276,7 +1276,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return !c;
\\}
,
- \\pub fn foo(a: c_int, b: f32, c: [*c]c_void) c_int {
+ \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
\\ return !(a == 0);
\\ return !(a != 0);
\\ return !(b != 0);
@@ -1334,7 +1334,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ B,
\\ C,
\\};
- \\pub fn if_none_bool(a: c_int, b: f32, c: [*c]c_void, d: enum_SomeEnum) c_int {
+ \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
\\ if (a != 0) return 0;
\\ if (b != 0) return 1;
\\ if (c != 0) return 2;
@@ -1351,7 +1351,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 3;
\\}
,
- \\pub fn while_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
+ \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != 0) return 2;
@@ -1367,7 +1367,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 3;
\\}
,
- \\pub fn for_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
+ \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != 0) return 2;
--
cgit v1.2.3
From 7293e012d7956b892380517e914108ffadc6941b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 15 Feb 2019 18:05:50 -0500
Subject: breaking: fix @sizeOf to be alloc size rather than store size
* Fixes breaches of the guarantee that `@sizeOf(T) >= @alignOf(T)`
* Fixes std.mem.secureZero for integers where this guarantee previously
was breached
* Fixes std.mem.Allocator for integers where this guarantee previously
was breached
Closes #1851
Closes #1864
---
doc/langref.html.in | 7 +++++-
src/analyze.cpp | 37 ++++++++++++++++++++++++----
src/analyze.hpp | 1 +
src/ir.cpp | 28 +++++++++++++++-------
std/io.zig | 13 ++++------
std/mem.zig | 49 ++++++++++++++++----------------------
test/stage1/behavior.zig | 1 +
test/stage1/behavior/bugs/1851.zig | 27 +++++++++++++++++++++
8 files changed, 112 insertions(+), 51 deletions(-)
create mode 100644 test/stage1/behavior/bugs/1851.zig
(limited to 'src/analyze.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e5a60b0bc1..1341bf1be5 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6299,10 +6299,15 @@ pub const FloatMode = enum {
{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}
This function returns the number of bytes it takes to store {#syntax#}T{#endsyntax#} in memory.
+ The result is a target-specific compile time constant.
- The result is a target-specific compile time constant.
+ This size may contain padding bytes. If there were two consecutive T in memory, this would be the offset
+ in bytes between element at index 0 and the element at index 1. For {#link|integer|Integers#},
+ consider whether you want to use {#syntax#}@sizeOf(T){#endsyntax#} or
+ {#syntax#}@typeInfo(T).Int.bits{#endsyntax#}.
+ {#see_also|@typeInfo#}
{#header_close#}
{#header_open|@sliceToBytes#}
diff --git a/src/analyze.cpp b/src/analyze.cpp
index e2a96da7c3..7949493586 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -356,6 +356,28 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) {
}
}
+ return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref);
+}
+
+uint64_t type_size_store(CodeGen *g, ZigType *type_entry) {
+ assert(type_is_complete(type_entry));
+
+ if (!type_has_bits(type_entry))
+ return 0;
+
+ if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) {
+ uint64_t size_in_bits = type_size_bits(g, type_entry);
+ return (size_in_bits + 7) / 8;
+ } else if (type_entry->id == ZigTypeIdArray) {
+ ZigType *child_type = type_entry->data.array.child_type;
+ if (child_type->id == ZigTypeIdStruct &&
+ child_type->data.structure.layout == ContainerLayoutPacked)
+ {
+ uint64_t size_in_bits = type_size_bits(g, type_entry);
+ return (size_in_bits + 7) / 8;
+ }
+ }
+
return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
}
@@ -6230,14 +6252,19 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
case ZigTypeIdStruct:
{
if (is_slice(type_entry)) {
- ConstPtrValue *ptr = &const_val->data.x_struct.fields[slice_ptr_index].data.x_ptr;
- assert(ptr->special == ConstPtrSpecialBaseArray);
- ConstExprValue *array = ptr->data.base_array.array_val;
- size_t start = ptr->data.base_array.elem_index;
-
ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index];
size_t len = bigint_as_unsigned(&len_val->data.x_bigint);
+ ConstExprValue *ptr_val = &const_val->data.x_struct.fields[slice_ptr_index];
+ if (ptr_val->special == ConstValSpecialUndef) {
+ assert(len == 0);
+ buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name));
+ return;
+ }
+ assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
+ ConstExprValue *array = ptr_val->data.x_ptr.data.base_array.array_val;
+ size_t start = ptr_val->data.x_ptr.data.base_array.elem_index;
+
render_const_val_array(g, buf, &type_entry->name, array, start, len);
} else {
buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 845fb62534..7ded651e95 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -19,6 +19,7 @@ ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
uint64_t type_size(CodeGen *g, ZigType *type_entry);
+uint64_t type_size_store(CodeGen *g, ZigType *type_entry);
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
diff --git a/src/ir.cpp b/src/ir.cpp
index c9262038e0..92cdd8c891 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -14331,15 +14331,15 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown)))
return ErrorSemanticAnalyzeFail;
- size_t src_size = type_size(codegen, pointee->type);
- size_t dst_size = type_size(codegen, out_val->type);
-
- if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
- copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
- return ErrorNone;
- }
+ // We don't need to read the padding bytes, so we look at type_size_store bytes
+ size_t src_size = type_size_store(codegen, pointee->type);
+ size_t dst_size = type_size_store(codegen, out_val->type);
if (dst_size <= src_size) {
+ if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
+ copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
+ return ErrorNone;
+ }
Buf buf = BUF_INIT;
buf_resize(&buf, src_size);
buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee);
@@ -15798,6 +15798,8 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio
static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
IrInstructionToPtrType *to_ptr_type_instruction)
{
+ Error err;
+
IrInstruction *value = to_ptr_type_instruction->value->child;
ZigType *type_entry = value->value.type;
if (type_is_invalid(type_entry))
@@ -15813,7 +15815,17 @@ static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
ptr_type = get_pointer_to_type(ira->codegen,
type_entry->data.pointer.child_type->data.array.child_type, type_entry->data.pointer.is_const);
} else if (is_slice(type_entry)) {
- ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle);
+ ZigType *slice_ptr_type = type_entry->data.structure.fields[0].type_entry;
+ ptr_type = adjust_ptr_len(ira->codegen, slice_ptr_type, PtrLenSingle);
+ // If the pointer is over-aligned, we may have to reduce it based on the alignment of the element type.
+ if (slice_ptr_type->data.pointer.explicit_alignment != 0) {
+ ZigType *elem_type = slice_ptr_type->data.pointer.child_type;
+ if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown)))
+ return ira->codegen->invalid_instruction;
+ uint32_t elem_align = get_abi_alignment(ira->codegen, elem_type);
+ uint32_t reduced_align = min(elem_align, slice_ptr_type->data.pointer.explicit_alignment);
+ ptr_type = adjust_ptr_align(ira->codegen, ptr_type, reduced_align);
+ }
} else if (type_entry->id == ZigTypeIdArgTuple) {
ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad);
if (!arg_tuple_val)
diff --git a/std/io.zig b/std/io.zig
index d7e8507f9b..6c70834b52 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -935,8 +935,6 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
};
}
-
-
pub const BufferedAtomicFile = struct {
atomic_file: os.AtomicFile,
file_stream: os.File.OutStream,
@@ -978,7 +976,6 @@ pub const BufferedAtomicFile = struct {
}
};
-
pub fn readLine(buf: *std.Buffer) ![]u8 {
var stdin = try getStdIn();
var stdin_stream = stdin.inStream();
@@ -1073,13 +1070,13 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
else => in_stream,
} };
}
-
+
pub fn alignToByte(self: *Self) void {
- if(!is_packed) return;
+ if (!is_packed) return;
self.in_stream.alignToByte();
}
- //@BUG: inferred error issue. See: #1386
+ //@BUG: inferred error issue. See: #1386
fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
@@ -1088,7 +1085,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
const U = @IntType(false, t_bit_count);
const Log2U = math.Log2Int(U);
- const int_size = @sizeOf(U);
+ const int_size = (U.bit_count + 7) / 8;
if (is_packed) {
const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
@@ -1301,7 +1298,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
const U = @IntType(false, t_bit_count);
const Log2U = math.Log2Int(U);
- const int_size = @sizeOf(U);
+ const int_size = (U.bit_count + 7) / 8;
const u_value = @bitCast(U, value);
diff --git a/std/mem.zig b/std/mem.zig
index 1c7523bf13..39b9701754 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -423,8 +423,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.
/// This function cannot fail and cannot cause undefined behavior.
/// Assumes the endianness of memory is native. This means the function can
/// simply pointer cast memory.
-pub fn readIntNative(comptime T: type, bytes: *const [@sizeOf(T)]u8) T {
- comptime assert(T.bit_count % 8 == 0);
+pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T {
return @ptrCast(*align(1) const T, bytes).*;
}
@@ -432,7 +431,7 @@ pub fn readIntNative(comptime T: type, bytes: *const [@sizeOf(T)]u8) T {
/// The bit count of T must be evenly divisible by 8.
/// This function cannot fail and cannot cause undefined behavior.
/// Assumes the endianness of memory is foreign, so it must byte-swap.
-pub fn readIntForeign(comptime T: type, bytes: *const [@sizeOf(T)]u8) T {
+pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T {
return @bswap(T, readIntNative(T, bytes));
}
@@ -446,22 +445,20 @@ pub const readIntBig = switch (builtin.endian) {
builtin.Endian.Big => readIntNative,
};
-/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0
+/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
/// and ignores extra bytes.
-/// Note that @sizeOf(u24) is 3.
/// The bit count of T must be evenly divisible by 8.
/// Assumes the endianness of memory is native. This means the function can
/// simply pointer cast memory.
pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T {
- assert(@sizeOf(u24) == 3);
- assert(bytes.len >= @sizeOf(T));
+ const n = @divExact(T.bit_count, 8);
+ assert(bytes.len >= n);
// TODO https://github.com/ziglang/zig/issues/863
- return readIntNative(T, @ptrCast(*const [@sizeOf(T)]u8, bytes.ptr));
+ return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr));
}
-/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0
+/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
/// and ignores extra bytes.
-/// Note that @sizeOf(u24) is 3.
/// The bit count of T must be evenly divisible by 8.
/// Assumes the endianness of memory is foreign, so it must byte-swap.
pub fn readIntSliceForeign(comptime T: type, bytes: []const u8) T {
@@ -481,7 +478,7 @@ pub const readIntSliceBig = switch (builtin.endian) {
/// Reads an integer from memory with bit count specified by T.
/// The bit count of T must be evenly divisible by 8.
/// This function cannot fail and cannot cause undefined behavior.
-pub fn readInt(comptime T: type, bytes: *const [@sizeOf(T)]u8, endian: builtin.Endian) T {
+pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, endian: builtin.Endian) T {
if (endian == builtin.endian) {
return readIntNative(T, bytes);
} else {
@@ -489,15 +486,14 @@ pub fn readInt(comptime T: type, bytes: *const [@sizeOf(T)]u8, endian: builtin.E
}
}
-/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0
+/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
/// and ignores extra bytes.
-/// Note that @sizeOf(u24) is 3.
/// The bit count of T must be evenly divisible by 8.
pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T {
- assert(@sizeOf(u24) == 3);
- assert(bytes.len >= @sizeOf(T));
+ const n = @divExact(T.bit_count, 8);
+ assert(bytes.len >= n);
// TODO https://github.com/ziglang/zig/issues/863
- return readInt(T, @ptrCast(*const [@sizeOf(T)]u8, bytes.ptr), endian);
+ return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian);
}
test "comptime read/write int" {
@@ -540,7 +536,7 @@ test "readIntBig and readIntLittle" {
/// accepts any integer bit width.
/// This function stores in native endian, which means it is implemented as a simple
/// memory store.
-pub fn writeIntNative(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void {
+pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value: T) void {
@ptrCast(*align(1) T, buf).* = value;
}
@@ -548,7 +544,7 @@ pub fn writeIntNative(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void {
/// This function always succeeds, has defined behavior for all inputs, but
/// the integer bit width must be divisible by 8.
/// This function stores in foreign endian, which means it does a @bswap first.
-pub fn writeIntForeign(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void {
+pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, value: T) void {
writeIntNative(T, buf, @bswap(T, value));
}
@@ -565,8 +561,7 @@ pub const writeIntBig = switch (builtin.endian) {
/// Writes an integer to memory, storing it in twos-complement.
/// This function always succeeds, has defined behavior for all inputs, but
/// the integer bit width must be divisible by 8.
-pub fn writeInt(comptime T: type, buffer: *[@sizeOf(T)]u8, value: T, endian: builtin.Endian) void {
- comptime assert(T.bit_count % 8 == 0);
+pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: T, endian: builtin.Endian) void {
if (endian == builtin.endian) {
return writeIntNative(T, buffer, value);
} else {
@@ -575,15 +570,13 @@ pub fn writeInt(comptime T: type, buffer: *[@sizeOf(T)]u8, value: T, endian: bui
}
/// Writes a twos-complement little-endian integer to memory.
-/// Asserts that buf.len >= @sizeOf(T). Note that @sizeOf(u24) is 3.
+/// Asserts that buf.len >= T.bit_count / 8.
/// The bit count of T must be divisible by 8.
/// Any extra bytes in buffer after writing the integer are set to zero. To
/// avoid the branch to check for extra buffer bytes, use writeIntLittle
/// instead.
pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
- comptime assert(@sizeOf(u24) == 3);
- comptime assert(T.bit_count % 8 == 0);
- assert(buffer.len >= @sizeOf(T));
+ assert(buffer.len >= @divExact(T.bit_count, 8));
// TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough
const uint = @IntType(false, T.bit_count);
@@ -595,14 +588,12 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
}
/// Writes a twos-complement big-endian integer to memory.
-/// Asserts that buffer.len >= @sizeOf(T). Note that @sizeOf(u24) is 3.
+/// Asserts that buffer.len >= T.bit_count / 8.
/// The bit count of T must be divisible by 8.
/// Any extra bytes in buffer before writing the integer are set to zero. To
/// avoid the branch to check for extra buffer bytes, use writeIntBig instead.
pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void {
- comptime assert(@sizeOf(u24) == 3);
- comptime assert(T.bit_count % 8 == 0);
- assert(buffer.len >= @sizeOf(T));
+ assert(buffer.len >= @divExact(T.bit_count, 8));
// TODO I want to call writeIntBig here but comptime eval facilities aren't good enough
const uint = @IntType(false, T.bit_count);
@@ -626,7 +617,7 @@ pub const writeIntSliceForeign = switch (builtin.endian) {
};
/// Writes a twos-complement integer to memory, with the specified endianness.
-/// Asserts that buf.len >= @sizeOf(T). Note that @sizeOf(u24) is 3.
+/// Asserts that buf.len >= T.bit_count / 8.
/// The bit count of T must be evenly divisible by 8.
/// Any extra bytes in buffer not part of the integer are set to zero, with
/// respect to endianness. To avoid the branch to check for extra buffer bytes,
diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig
index 1fa00b34fd..df311637fa 100644
--- a/test/stage1/behavior.zig
+++ b/test/stage1/behavior.zig
@@ -17,6 +17,7 @@ comptime {
_ = @import("behavior/bugs/1421.zig");
_ = @import("behavior/bugs/1442.zig");
_ = @import("behavior/bugs/1486.zig");
+ _ = @import("behavior/bugs/1851.zig");
_ = @import("behavior/bugs/394.zig");
_ = @import("behavior/bugs/655.zig");
_ = @import("behavior/bugs/656.zig");
diff --git a/test/stage1/behavior/bugs/1851.zig b/test/stage1/behavior/bugs/1851.zig
new file mode 100644
index 0000000000..ff9ab419f8
--- /dev/null
+++ b/test/stage1/behavior/bugs/1851.zig
@@ -0,0 +1,27 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "allocation and looping over 3-byte integer" {
+ expect(@sizeOf(u24) == 4);
+ expect(@sizeOf([1]u24) == 4);
+ expect(@alignOf(u24) == 4);
+ expect(@alignOf([1]u24) == 4);
+ var buffer: [100]u8 = undefined;
+ const a = &std.heap.FixedBufferAllocator.init(&buffer).allocator;
+
+ var x = a.alloc(u24, 2) catch unreachable;
+ expect(x.len == 2);
+ x[0] = 0xFFFFFF;
+ x[1] = 0xFFFFFF;
+
+ const bytes = @sliceToBytes(x);
+ expect(@typeOf(bytes) == []align(4) u8);
+ expect(bytes.len == 8);
+
+ for (bytes) |*b| {
+ b.* = 0x00;
+ }
+
+ expect(x[0] == 0x00);
+ expect(x[1] == 0x00);
+}
--
cgit v1.2.3
From a05e224150a5a4bcad5ab1b399b43db8a0e28104 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 15 Feb 2019 19:19:28 -0500
Subject: typecheck the panic function
this adds the prototype of panic to @import("builtin")
and then uses it to do an implicit cast of the panic
function to this prototype, rather than redoing all the
implicit cast logic.
closes #1894
closes #1895
---
src/all_types.hpp | 1 +
src/analyze.cpp | 61 ++++++++++++++++++-------------------------------
src/analyze.hpp | 1 +
src/codegen.cpp | 4 ++++
src/ir.cpp | 15 +++++++-----
src/ir.hpp | 2 +-
test/compile_errors.zig | 14 ++++++++++--
7 files changed, 50 insertions(+), 48 deletions(-)
(limited to 'src/analyze.cpp')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index bafe316c3d..6fbd987b9e 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1758,6 +1758,7 @@ struct CodeGen {
ZigFn *cur_fn;
ZigFn *main_fn;
ZigFn *panic_fn;
+ TldFn *panic_tld_fn;
AstNode *root_export_decl;
CacheHash cache_hash;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 7949493586..12e245bd72 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -1351,7 +1351,7 @@ static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *no
size_t backward_branch_count = 0;
return ir_eval_const_value(g, scope, node, type_entry,
&backward_branch_count, default_backward_branch_quota,
- nullptr, nullptr, node, type_name, nullptr);
+ nullptr, nullptr, node, type_name, nullptr, nullptr);
}
ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
@@ -3247,36 +3247,19 @@ static bool scope_is_root_decls(Scope *scope) {
zig_unreachable();
}
-static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, ZigType *fn_type) {
- add_node_error(g, proto_node,
- buf_sprintf("expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found '%s'",
- buf_ptr(&fn_type->name)));
-}
-
-static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) {
- AstNode *proto_node = panic_fn->proto_node;
- assert(proto_node->type == NodeTypeFnProto);
- ZigType *fn_type = panic_fn->type_entry;
- FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- if (fn_type_id->param_count != 2) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
- ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
- PtrLenUnknown, 0, 0, 0);
- ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr);
- if (fn_type_id->param_info[0].type != const_u8_slice) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
+void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) {
+ ConstExprValue *panic_fn_type_val = get_builtin_value(g, "PanicFn");
+ assert(panic_fn_type_val != nullptr);
+ assert(panic_fn_type_val->type->id == ZigTypeIdMetaType);
+ ZigType *panic_fn_type = panic_fn_type_val->data.x_type;
- ZigType *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g));
- if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
+ AstNode *fake_decl = allocate(1);
+ *fake_decl = *panic_fn->proto_node;
+ fake_decl->type = NodeTypeSymbol;
+ fake_decl->data.symbol_expr.symbol = &panic_fn->symbol_name;
- ZigType *actual_return_type = fn_type_id->return_type;
- if (actual_return_type != g->builtin_types.entry_unreachable) {
- return wrong_panic_prototype(g, proto_node, fn_type);
- }
+ // call this for the side effects of casting to panic_fn_type
+ analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr);
}
ZigType *get_test_fn_type(CodeGen *g) {
@@ -3371,18 +3354,18 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
if (!fn_table_entry->type_entry->data.fn.is_generic) {
if (fn_def_node)
g->fn_defs.append(fn_table_entry);
+ }
- if (scope_is_root_decls(tld_fn->base.parent_scope) &&
- (import == g->root_import || import->package == g->panic_package))
+ if (scope_is_root_decls(tld_fn->base.parent_scope) &&
+ (import == g->root_import || import->package == g->panic_package))
+ {
+ if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
+ g->main_fn = fn_table_entry;
+ } else if ((import->package == g->panic_package || g->have_pub_panic) &&
+ buf_eql_str(&fn_table_entry->symbol_name, "panic"))
{
- if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
- g->main_fn = fn_table_entry;
- } else if ((import->package == g->panic_package || g->have_pub_panic) &&
- buf_eql_str(&fn_table_entry->symbol_name, "panic"))
- {
- g->panic_fn = fn_table_entry;
- typecheck_panic_fn(g, fn_table_entry);
- }
+ g->panic_fn = fn_table_entry;
+ g->panic_tld_fn = tld_fn;
}
}
} else if (source_node->type == NodeTypeTestDecl) {
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 7ded651e95..956ef47309 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -239,4 +239,5 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry);
Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
ConstExprValue *const_val, ZigType *wanted_type);
+void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn);
#endif
diff --git a/src/codegen.cpp b/src/codegen.cpp
index d2662b10d2..d2b2836b0c 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -7144,6 +7144,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" instruction_addresses: []usize,\n"
"};\n\n");
+ buf_append_str(contents, "pub const PanicFn = fn([]const u8, ?*StackTrace) noreturn;\n\n");
+
const char *cur_os = nullptr;
{
buf_appendf(contents, "pub const Os = enum {\n");
@@ -7913,6 +7915,8 @@ static void gen_root_source(CodeGen *g) {
}
}
+ typecheck_panic_fn(g, g->panic_tld_fn, g->panic_fn);
+
report_errors_and_maybe_exit(g);
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 92cdd8c891..0fcbb60fe8 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9949,7 +9949,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
- IrExecutable *parent_exec)
+ IrExecutable *parent_exec, AstNode *expected_type_source_node)
{
if (expected_type != nullptr && type_is_invalid(expected_type))
return &codegen->invalid_instruction->value;
@@ -9985,7 +9985,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod
analyzed_executable->backward_branch_count = backward_branch_count;
analyzed_executable->backward_branch_quota = backward_branch_quota;
analyzed_executable->begin_scope = scope;
- ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node);
+ ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node);
if (type_is_invalid(result_type))
return &codegen->invalid_instruction->value;
@@ -10863,10 +10863,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
}
break;
}
+ case ConstCastResultIdFnIsGeneric:
+ add_error_note(ira->codegen, parent_msg, source_node,
+ buf_sprintf("only one of the functions is generic"));
+ break;
case ConstCastResultIdFnAlign: // TODO
case ConstCastResultIdFnCC: // TODO
case ConstCastResultIdFnVarArgs: // TODO
- case ConstCastResultIdFnIsGeneric: // TODO
case ConstCastResultIdFnReturnType: // TODO
case ConstCastResultIdFnArgCount: // TODO
case ConstCastResultIdFnGenericArgCount: // TODO
@@ -13856,7 +13859,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
AstNode *body_node = fn_entry->body_node;
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
- nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
+ nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node);
if (inferred_err_set_type != nullptr) {
inferred_err_set_type->data.error_set.infer_fn = nullptr;
@@ -14052,7 +14055,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call
ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope,
fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen),
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
- nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec);
+ nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr);
IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb,
impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
const_instruction->base.value = *align_result;
@@ -18464,7 +18467,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct
ZigType *void_type = ira->codegen->builtin_types.entry_void;
ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type,
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
- &cimport_scope->buf, block_node, nullptr, nullptr);
+ &cimport_scope->buf, block_node, nullptr, nullptr, nullptr);
if (type_is_invalid(cimport_result->type))
return ira->codegen->invalid_instruction;
diff --git a/src/ir.hpp b/src/ir.hpp
index 0a7c614812..0b85ad2c55 100644
--- a/src/ir.hpp
+++ b/src/ir.hpp
@@ -16,7 +16,7 @@ bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry);
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota,
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
- IrExecutable *parent_exec);
+ IrExecutable *parent_exec, AstNode *expected_type_source_node);
ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
ZigType *expected_type, AstNode *expected_type_source_node);
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index e7108cb36a..9ef4af4162 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -346,13 +346,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
);
cases.add(
- "Panic declared with wrong type signature in tests",
+ "wrong panic signature, runtime function",
\\test "" {}
\\
\\pub fn panic() void {}
\\
,
- ".tmp_source.zig:3:5: error: expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'",
+ ".tmp_source.zig:3:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn() void'",
+ );
+
+ cases.add(
+ "wrong panic signature, generic function",
+ \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
+ \\ while (true) {}
+ \\}
+ ,
+ ".tmp_source.zig:1:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn([]const u8,var)var'",
+ ".tmp_source.zig:1:5: note: only one of the functions is generic",
);
cases.add(
--
cgit v1.2.3
From 5736a9c6a9b357ab346dd8fcbe64f5d729d6d244 Mon Sep 17 00:00:00 2001
From: emekoi
Date: Tue, 12 Feb 2019 10:21:45 -0600
Subject: removed hidden union tag in release modes
---
src/analyze.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src/analyze.cpp')
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 12e245bd72..9941104bc4 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -2886,7 +2886,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum ||
enum_type_node != nullptr;
bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
- bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr);
+ bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
ZigType *tag_type;
bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety);
bool *covered_enum_fields;
--
cgit v1.2.3