Date: Sat, 9 Feb 2019 21:10:59 -0500
Subject: `@truncate`: comptime 0 when target type is 0 bits
also if the dest type is a comptime_int, then treat it
as an implicit cast.
also compile error for attempting to truncate undefined
closes #1568
---
doc/langref.html.in | 11 +++++++----
src/ir.cpp | 24 ++++++++++++++++--------
test/compile_errors.zig | 11 ++++++++++-
test/stage1/behavior/truncate.zig | 23 +++++++++++++++++++++++
4 files changed, 56 insertions(+), 13 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index dfea8e1e04..779eb6a31b 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6381,14 +6381,14 @@ fn List(comptime T: type) type {
{#header_close#}
{#header_open|@truncate#}
- {#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}
+ {#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}
This function truncates bits from an integer type, resulting in a smaller
integer type.
- The following produces a crash in debug mode and undefined behavior in
- release mode:
+ The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in
+ {#link|ReleaseFast#} mode:
{#syntax#}const a: u16 = 0xabcd;
const b: u8 = u8(a);{#endsyntax#}
@@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a);
This function always truncates the significant bits of the integer, regardless
of endianness on the target platform.
-
+
+ If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
+ then this is semantically equivalent to an {#link|implicit cast|Implicit Casts#}.
+
{#header_close#}
{#header_open|@typeId#}
diff --git a/src/ir.cpp b/src/ir.cpp
index d87486bbdd..5d4013b4b9 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
}
- if (src_type->data.integral.bit_count == 0) {
+ if (dest_type->id == ZigTypeIdComptimeInt) {
+ return ir_implicit_cast(ira, target, dest_type);
+ }
+
+ if (instr_is_comptime(target)) {
+ ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
+ if (val == nullptr)
+ return ira->codegen->invalid_instruction;
+
+ IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
+ bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint,
+ dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
+ return result;
+ }
+
+ if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) {
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
@@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
}
- if (target->value.special == ConstValSpecialStatic) {
- IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
- bigint_truncate(&result->value.data.x_bigint, &target->value.data.x_bigint,
- dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
- return result;
- }
-
IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, dest_type_value, target);
new_instruction->value.type = dest_type;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index de01a5ac45..b47cdf2ed1 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "@truncate undefined value",
+ \\export fn entry() void {
+ \\ var z = @truncate(u8, u16(undefined));
+ \\}
+ ,
+ ".tmp_source.zig:2:30: error: use of undefined value",
+ );
+
cases.addTest(
"return invalid type from test",
\\test "example" { return 1; }
@@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"truncate sign mismatch",
\\fn f() i8 {
- \\ const x: u32 = 10;
+ \\ var x: u32 = 10;
\\ return @truncate(i8, x);
\\}
\\
diff --git a/test/stage1/behavior/truncate.zig b/test/stage1/behavior/truncate.zig
index c195b64cbf..568346369f 100644
--- a/test/stage1/behavior/truncate.zig
+++ b/test/stage1/behavior/truncate.zig
@@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" {
const y = @truncate(u8, x);
comptime expect(y == 0);
}
+
+test "truncate.u0.literal" {
+ var z = @truncate(u0, 0);
+ expect(z == 0);
+}
+
+test "truncate.u0.const" {
+ const c0: usize = 0;
+ var z = @truncate(u0, c0);
+ expect(z == 0);
+}
+
+test "truncate.u0.var" {
+ var d: u8 = 2;
+ var z = @truncate(u0, d);
+ expect(z == 0);
+}
+
+test "truncate sign mismatch but comptime known so it works anyway" {
+ const x: u32 = 10;
+ var result = @truncate(i8, x);
+ expect(result == 10);
+}
--
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/ir.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 73e8e46257ac8f34e941a357a860d19e0d38dbac Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 10 Feb 2019 01:11:30 -0500
Subject: casting between C pointers and normal pointers
See #1059
---
src/ir.cpp | 41 ++++++++++++++++++++++++---------------
test/stage1/behavior/pointers.zig | 8 ++++++++
2 files changed, 33 insertions(+), 16 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 76277f541b..30350d75de 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -2462,7 +2462,7 @@ static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode *
ir_ref_instruction(type_value, irb->current_basic_block);
- return &instruction->base;
+ return &instruction->base;
}
static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node,
@@ -6739,7 +6739,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode
atomic_state_field_name);
// set the is_canceled bit
- IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
+ IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
@@ -6817,7 +6817,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode
atomic_state_field_name);
// clear the is_suspended bit
- IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
+ IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr,
AtomicRmwOp_and, AtomicOrderSeqCst);
@@ -6933,7 +6933,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n
IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
- IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
+ IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
@@ -6976,7 +6976,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n
ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);
- IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
+ IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false);
@@ -7008,7 +7008,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false);
- IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
+ IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr,
AtomicRmwOp_or, AtomicOrderSeqCst);
IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false);
@@ -11279,12 +11279,21 @@ 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);
- }
+ // casting between C pointers and normal pointers
+ if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer &&
+ (wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) &&
+ types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
+ actual_type->data.pointer.child_type, source_node,
+ !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
+ {
+ return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr);
+ }
+
+ // cast from integer to C pointer
+ if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
+ (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
@@ -16436,7 +16445,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node);
if (pointee_val == nullptr)
return ira->codegen->invalid_instruction;
-
+
if (pointee_val->special == ConstValSpecialRuntime)
pointee_val = nullptr;
}
@@ -17186,7 +17195,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
static TypeStructField *validate_byte_offset(IrAnalyze *ira,
IrInstruction *type_value,
IrInstruction *field_name_value,
- size_t *byte_offset)
+ size_t *byte_offset)
{
ZigType *container_type = ir_resolve_type(ira, type_value);
if (type_is_invalid(container_type))
@@ -17360,7 +17369,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
// Loop through the definitions and generate info.
decl_it = decls_scope->decl_table.entry_iterator();
- curr_entry = nullptr;
+ curr_entry = nullptr;
int definition_index = 0;
while ((curr_entry = decl_it.next()) != nullptr) {
// Skip comptime blocks and test functions.
@@ -20312,7 +20321,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
} else {
seenFalse += 1;
}
-
+
if ((seenTrue > 1) || (seenFalse > 1)) {
ir_add_error(ira, value, buf_sprintf("duplicate switch value"));
return ira->codegen->invalid_instruction;
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 6969e151df..79832bc316 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -48,3 +48,11 @@ test "assigning integer to C pointer" {
var ptr: [*c]u8 = 0;
var ptr2: [*c]u8 = x;
}
+
+test "implicit cast single item pointer to C pointer and back" {
+ var y: u8 = 11;
+ var x: [*c]u8 = &y;
+ var z: *u8 = x;
+ z.* += 1;
+ expect(y == 12);
+}
--
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/ir.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 90b8cd4a45bcb2ca131b6ed6466f799aaa162d13 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 16:07:40 -0500
Subject: add C pointer type to @typeInfo
See #1059
---
src-self-hosted/type.zig | 2 ++
src/codegen.cpp | 1 +
src/ir.cpp | 14 +++++++++++++-
std/fmt/index.zig | 3 +++
std/meta/index.zig | 9 ++++++---
std/testing.zig | 3 +--
test/stage1/behavior/type_info.zig | 15 +++++++++++++++
7 files changed, 41 insertions(+), 6 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
index 8a05594b30..790b51b7be 100644
--- a/src-self-hosted/type.zig
+++ b/src-self-hosted/type.zig
@@ -794,6 +794,7 @@ pub const Type = struct {
Size.One => "*",
Size.Many => "[*]",
Size.Slice => "[]",
+ Size.C => "[*c]",
};
const mut_str = switch (self.key.mut) {
Mut.Const => "const ",
@@ -1088,6 +1089,7 @@ fn hashAny(x: var, comptime seed: u64) u32 {
builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
+ builtin.TypeInfo.Pointer.Size.C => unreachable,
}
},
builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 605ea59b06..142e8174f5 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -7309,6 +7309,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
" One,\n"
" Many,\n"
" Slice,\n"
+ " C,\n"
" };\n"
" };\n"
"\n"
diff --git a/src/ir.cpp b/src/ir.cpp
index bc37ac9b54..36ea50ed52 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -17584,6 +17584,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco
return ErrorNone;
}
+static uint32_t ptr_len_to_size_enum_index(PtrLen ptr_len) {
+ switch (ptr_len) {
+ case PtrLenSingle:
+ return 0;
+ case PtrLenUnknown:
+ return 1;
+ case PtrLenC:
+ return 3;
+ }
+ zig_unreachable();
+}
+
static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {
Error err;
ZigType *attrs_type;
@@ -17593,7 +17605,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
size_enum_index = 2;
} else if (ptr_type_entry->id == ZigTypeIdPointer) {
attrs_type = ptr_type_entry;
- size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1;
+ size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len);
} else {
zig_unreachable();
}
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index 05b028112f..b09fe21032 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -236,6 +236,9 @@ pub fn formatType(
const casted_value = ([]const u8)(value);
return output(context, casted_value);
},
+ builtin.TypeInfo.Pointer.Size.C => {
+ return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
+ },
},
builtin.TypeId.Array => |info| {
if (info.child == u8) {
diff --git a/std/meta/index.zig b/std/meta/index.zig
index 3f8ea762a6..652e2d39ec 100644
--- a/std/meta/index.zig
+++ b/std/meta/index.zig
@@ -463,13 +463,16 @@ pub fn eql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Pointer => {
const info = @typeInfo(T).Pointer;
switch (info.size) {
- builtin.TypeInfo.Pointer.Size.One, builtin.TypeInfo.Pointer.Size.Many => return a == b,
+ builtin.TypeInfo.Pointer.Size.One,
+ builtin.TypeInfo.Pointer.Size.Many,
+ builtin.TypeInfo.Pointer.Size.C,
+ => return a == b,
builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len,
}
},
builtin.TypeId.Optional => {
- if(a == null and b == null) return true;
- if(a == null or b == null) return false;
+ if (a == null and b == null) return true;
+ if (a == null or b == null) return false;
return eql(a.?, b.?);
},
else => return a == b,
diff --git a/std/testing.zig b/std/testing.zig
index ade6e8b0dd..f3ce69659d 100644
--- a/std/testing.zig
+++ b/std/testing.zig
@@ -69,7 +69,7 @@ pub fn expectEqual(expected: var, actual: var) void {
}
},
- builtin.TypeInfo.Pointer.Size.Slice => {
+ builtin.TypeInfo.Pointer.Size.Slice => {
if (actual.ptr != expected.ptr) {
std.debug.panic("expected slice ptr {}, found {}", expected.ptr, actual.ptr);
}
@@ -122,7 +122,6 @@ pub fn expectEqual(expected: var, actual: var) void {
}
}
},
-
}
}
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index ce0ad795b4..dc185cc960 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -61,6 +61,21 @@ fn testUnknownLenPtr() void {
expect(u32_ptr_info.Pointer.child == f64);
}
+test "type info: C pointer type info" {
+ testCPtr();
+ comptime testCPtr();
+}
+
+fn testCPtr() void {
+ const ptr_info = @typeInfo([*c]align(4) const i8);
+ expect(TypeId(ptr_info) == TypeId.Pointer);
+ expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C);
+ expect(ptr_info.Pointer.is_const);
+ expect(!ptr_info.Pointer.is_volatile);
+ expect(ptr_info.Pointer.alignment == 4);
+ expect(ptr_info.Pointer.child == i8);
+}
+
test "type info: slice type info" {
testSlice();
comptime testSlice();
--
cgit v1.2.3
From 57a7ab0d330416f15c0288004b67101c1c3e9629 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 19:12:01 -0500
Subject: comptime support for pointer arithmetic with hard coded addresses
---
src/ir.cpp | 109 ++++++++++++++++++++++++++++++--------
test/stage1/behavior/pointers.zig | 40 ++++++++------
2 files changed, 109 insertions(+), 40 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 36ea50ed52..5ec397b0b2 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -11682,28 +11682,34 @@ static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp
}
static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
- if (op_id == IrBinOpCmpEq) {
- return cmp == CmpEQ;
- } else if (op_id == IrBinOpCmpNotEq) {
- return cmp != CmpEQ;
- } else if (op_id == IrBinOpCmpLessThan) {
- return cmp == CmpLT;
- } else if (op_id == IrBinOpCmpGreaterThan) {
- return cmp == CmpGT;
- } else if (op_id == IrBinOpCmpLessOrEq) {
- return cmp != CmpGT;
- } else if (op_id == IrBinOpCmpGreaterOrEq) {
- return cmp != CmpLT;
- } else {
- zig_unreachable();
+ switch (op_id) {
+ case IrBinOpCmpEq:
+ return cmp == CmpEQ;
+ case IrBinOpCmpNotEq:
+ return cmp != CmpEQ;
+ case IrBinOpCmpLessThan:
+ return cmp == CmpLT;
+ case IrBinOpCmpGreaterThan:
+ return cmp == CmpGT;
+ case IrBinOpCmpLessOrEq:
+ return cmp != CmpGT;
+ case IrBinOpCmpGreaterOrEq:
+ return cmp != CmpLT;
+ default:
+ zig_unreachable();
}
}
static bool optional_value_is_null(ConstExprValue *val) {
assert(val->special == ConstValSpecialStatic);
if (get_codegen_ptr_type(val->type) != nullptr) {
- return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
- val->data.x_ptr.data.hard_coded_addr.addr == 0;
+ if (val->data.x_ptr.special == ConstPtrSpecialNull) {
+ return true;
+ } else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
+ return val->data.x_ptr.data.hard_coded_addr.addr == 0;
+ } else {
+ return false;
+ }
} else if (is_opt_err_set(val->type)) {
return val->data.x_err_set == nullptr;
} else {
@@ -11879,7 +11885,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
break;
case ZigTypeIdPointer:
- operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len != PtrLenSingle);
+ operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len == PtrLenC);
break;
case ZigTypeIdUnreachable:
@@ -11929,15 +11935,38 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
if (op2_val == nullptr)
return ira->codegen->invalid_instruction;
- bool answer;
if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) {
Cmp cmp_result = float_cmp(op1_val, op2_val);
- answer = resolve_cmp_op_id(op_id, cmp_result);
+ bool answer = resolve_cmp_op_id(op_id, cmp_result);
+ return ir_const_bool(ira, &bin_op_instruction->base, answer);
} else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) {
Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
- answer = resolve_cmp_op_id(op_id, cmp_result);
+ bool answer = resolve_cmp_op_id(op_id, cmp_result);
+ return ir_const_bool(ira, &bin_op_instruction->base, answer);
+ } else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) {
+ if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
+ op1_val->data.x_ptr.special == ConstPtrSpecialNull) &&
+ (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
+ op2_val->data.x_ptr.special == ConstPtrSpecialNull))
+ {
+ uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ?
+ 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr;
+ uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ?
+ 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr;
+ Cmp cmp_result;
+ if (op1_addr > op2_addr) {
+ cmp_result = CmpGT;
+ } else if (op1_addr < op2_addr) {
+ cmp_result = CmpLT;
+ } else {
+ cmp_result = CmpEQ;
+ }
+ bool answer = resolve_cmp_op_id(op_id, cmp_result);
+ return ir_const_bool(ira, &bin_op_instruction->base, answer);
+ }
} else {
bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val);
+ bool answer;
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
@@ -11945,9 +11974,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
} else {
zig_unreachable();
}
+ return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
-
- return ir_const_bool(ira, &bin_op_instruction->base, answer);
}
// some comparisons with unsigned numbers can be evaluated
@@ -12363,6 +12391,8 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
}
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
+ Error err;
+
IrInstruction *op1 = instruction->op1->child;
if (type_is_invalid(op1->value.type))
return ira->codegen->invalid_instruction;
@@ -12376,9 +12406,42 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
// look for pointer math
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)
+ if (type_is_invalid(casted_op2->value.type))
return ira->codegen->invalid_instruction;
+ if (op1->value.special == ConstValSpecialUndef || casted_op2->value.special == ConstValSpecialUndef) {
+ IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
+ result->value.special = ConstValSpecialUndef;
+ return result;
+ }
+ if (casted_op2->value.special == ConstValSpecialStatic && op1->value.special == ConstValSpecialStatic &&
+ (op1->value.data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
+ op1->value.data.x_ptr.special == ConstPtrSpecialNull))
+ {
+ uint64_t start_addr = (op1->value.data.x_ptr.special == ConstPtrSpecialNull) ?
+ 0 : op1->value.data.x_ptr.data.hard_coded_addr.addr;
+ uint64_t elem_offset;
+ if (!ir_resolve_usize(ira, casted_op2, &elem_offset))
+ return ira->codegen->invalid_instruction;
+ ZigType *elem_type = op1->value.type->data.pointer.child_type;
+ if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
+ return ira->codegen->invalid_instruction;
+ uint64_t byte_offset = type_size(ira->codegen, elem_type) * elem_offset;
+ uint64_t new_addr;
+ if (op_id == IrBinOpAdd) {
+ new_addr = start_addr + byte_offset;
+ } else if (op_id == IrBinOpSub) {
+ new_addr = start_addr - byte_offset;
+ } else {
+ zig_unreachable();
+ }
+ IrInstruction *result = ir_const(ira, &instruction->base, op1->value.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 = new_addr;
+ return result;
+ }
+
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, op_id, op1, casted_op2, true);
result->value.type = op1->value.type;
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 63e4c314b1..4375a6971f 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -58,21 +58,27 @@ test "implicit cast single item pointer to C pointer and back" {
}
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);
+ const S = struct {
+ fn doTheTest() void {
+ var one: usize = 1;
+ var ptr1: [*c]u32 = 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 >= 40);
+ expect(ptr2 == 40);
+ expect(ptr2 <= 40);
+ ptr2 -= 10;
+ expect(ptr1 == ptr2);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
}
--
cgit v1.2.3
From 069fc1a26990b3946cf788b4ebe5edefca5a3bfd Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 11 Feb 2019 19:21:59 -0500
Subject: peer type resolution with C pointers
See #1059
---
src/ir.cpp | 18 ++++++++++++++++++
test/stage1/behavior/pointers.zig | 15 +++++++++++++++
2 files changed, 33 insertions(+)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 5ec397b0b2..91c8503234 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -9275,6 +9275,24 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
continue;
}
+ if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) {
+ if (prev_type->data.pointer.ptr_len == PtrLenC &&
+ types_match_const_cast_only(ira, prev_type->data.pointer.child_type,
+ cur_type->data.pointer.child_type, source_node,
+ !prev_type->data.pointer.is_const).id == ConstCastResultIdOk)
+ {
+ continue;
+ }
+ if (cur_type->data.pointer.ptr_len == PtrLenC &&
+ types_match_const_cast_only(ira, cur_type->data.pointer.child_type,
+ prev_type->data.pointer.child_type, source_node,
+ !cur_type->data.pointer.is_const).id == ConstCastResultIdOk)
+ {
+ prev_inst = cur_inst;
+ continue;
+ }
+ }
+
if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) {
continue;
}
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 4375a6971f..3f62bd1cec 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -82,3 +82,18 @@ test "C pointer comparison and arithmetic" {
S.doTheTest();
comptime S.doTheTest();
}
+
+test "peer type resolution with C pointers" {
+ var ptr_one: *u8 = undefined;
+ var ptr_many: [*]u8 = undefined;
+ var ptr_c: [*c]u8 = undefined;
+ var t = true;
+ var x1 = if (t) ptr_one else ptr_c;
+ var x2 = if (t) ptr_many else ptr_c;
+ var x3 = if (t) ptr_c else ptr_one;
+ var x4 = if (t) ptr_c else ptr_many;
+ expect(@typeOf(x1) == [*c]u8);
+ expect(@typeOf(x2) == [*c]u8);
+ expect(@typeOf(x3) == [*c]u8);
+ expect(@typeOf(x4) == [*c]u8);
+}
--
cgit v1.2.3
From 0abe6d668eb52aefa59c75a8d8f782f2372f8600 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 12 Feb 2019 00:39:08 -0500
Subject: C pointers: delete dead code in ir_num_lit_fits_in_other_type
---
src/ir.cpp | 14 --------------
1 file changed, 14 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 91c8503234..1f0edc910d 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8555,20 +8555,6 @@ 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();
--
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/ir.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 6f05e8d1be083a429673e717e8b3c736c7ddb8d2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 12 Feb 2019 01:38:11 -0500
Subject: implicit casting between C pointer and optional non-C pointer
See #1059
---
src/ir.cpp | 34 +++++++++++++++++++++-------------
test/stage1/behavior/pointers.zig | 10 ++++++++++
2 files changed, 31 insertions(+), 13 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 64e08ef7ea..00d82c9224 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8669,33 +8669,41 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
}
// pointer const
- if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) {
- ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
- actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const);
+ ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
+ ZigType *actual_ptr_type = get_src_ptr_type(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))
+ {
+ 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)
return child;
if (child.id != ConstCastResultIdOk) {
result.id = ConstCastResultIdPointerChild;
result.data.pointer_mismatch = allocate_nonzero(1);
result.data.pointer_mismatch->child = child;
- result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type;
- result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type;
+ result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
+ result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
return result;
}
- if ((err = type_resolve(g, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
+ if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
- if ((err = type_resolve(g, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
+ if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
}
- if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) &&
- (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
- (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) &&
- actual_type->data.pointer.bit_offset_in_host == wanted_type->data.pointer.bit_offset_in_host &&
- actual_type->data.pointer.host_int_bytes == wanted_type->data.pointer.host_int_bytes &&
- get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type))
+ 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) &&
+ (!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 &&
+ actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes &&
+ get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type))
{
return result;
}
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 3f62bd1cec..8d87fe2a20 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -97,3 +97,13 @@ test "peer type resolution with C pointers" {
expect(@typeOf(x3) == [*c]u8);
expect(@typeOf(x4) == [*c]u8);
}
+
+test "implicit casting between C pointer and optional non-C pointer" {
+ 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;
+ expect(c_ptr.*.* == 'a');
+ ptr_opt_many_ptr = c_ptr;
+ expect(ptr_opt_many_ptr.*.?[1] == 'o');
+}
--
cgit v1.2.3
From 270933b1e997c91a9c2d28b6896d625c0ae1b163 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 12 Feb 2019 10:25:21 -0500
Subject: compile error test for casting integer to c pointer
when the int has more bits than pointers
See #1059
---
src/ir.cpp | 4 +++-
test/compile_errors.zig | 14 ++++++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 00d82c9224..50b5661e12 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -10882,7 +10882,9 @@ static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *sou
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)));
+ buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'",
+ buf_ptr(&integer->value.type->name),
+ buf_ptr(&dest_type->name)));
return ira->codegen->invalid_instruction;
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 63850bb888..8f8e2a0bdf 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,20 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "implicit casting too big integers to C pointers",
+ \\export fn a() void {
+ \\ var ptr: [*c]u8 = (1 << 64) + 1;
+ \\}
+ \\export fn b() void {
+ \\ var x: @IntType(false, 65) = 0x1234;
+ \\ var ptr: [*c]u8 = x;
+ \\}
+ ,
+ ".tmp_source.zig:2:33: error: integer value 71615590737044764481 cannot be implicitly casted to type 'usize'",
+ ".tmp_source.zig:6:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'",
+ );
+
cases.addTest(
"C pointer pointing to non C ABI compatible type",
\\const Foo = struct {};
--
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/ir.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 d4d2718bca9e23ceec029bb505c0ea1b91c875b6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 00:40:39 -0500
Subject: comptime detection of casting null to pointer
See #1059
---
src/ir.cpp | 17 ++++++++++++++---
test/compile_errors.zig | 20 ++++++++++++++++++++
2 files changed, 34 insertions(+), 3 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 19bec193d5..f064adb128 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -20645,12 +20645,23 @@ 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);
+ bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type);
+ UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad;
+ ConstExprValue *val = ir_resolve_const(ira, ptr, is_undef_allowed);
if (!val)
return ira->codegen->invalid_instruction;
+ if (val->special == ConstValSpecialStatic) {
+ bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull ||
+ (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
+ val->data.x_ptr.data.hard_coded_addr.addr == 0);
+ if (is_addr_zero && !dest_allows_addr_zero) {
+ ir_add_error(ira, source_instr,
+ buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+ }
+
IrInstruction *result = ir_const(ira, source_instr, dest_type);
copy_const_val(&result->value, val, false);
result->value.type = dest_type;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index c51a65cadf..71ee4901ff 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,26 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "implicit casting null c pointer to zig pointer",
+ \\comptime {
+ \\ var c_ptr: [*c]u8 = 0;
+ \\ var zig_ptr: *u8 = c_ptr;
+ \\}
+ ,
+ ".tmp_source.zig:3:24: error: null pointer casted to type '*u8'",
+ );
+
+ cases.addTest(
+ "implicit casting undefined c pointer to zig pointer",
+ \\comptime {
+ \\ var c_ptr: [*c]u8 = undefined;
+ \\ var zig_ptr: *u8 = c_ptr;
+ \\}
+ ,
+ ".tmp_source.zig:3:24: error: use of undefined value here causes undefined behavior",
+ );
+
cases.addTest(
"implicit casting C pointers which would mess up null semantics",
\\export fn entry() void {
--
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/ir.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 c58b80203443dcbf8b737ebdaa1f17fb20c77711 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 10:51:59 -0500
Subject: remove the "top of the comptime stack" compile error
It's still best practice to put `@setEvalBranchQuota` at the top of
the comptime stack, but as Jimmi notes in #1949, when a function
can be called at comptime and also can be the top of the comptime stack,
this compile error is fundamentally unsound.
So now it's gone.
closes #1949
---
src/ir.cpp | 6 ------
test/compile_errors.zig | 14 --------------
2 files changed, 20 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 5d4013b4b9..03dea10b10 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -18172,12 +18172,6 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira,
static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira,
IrInstructionSetEvalBranchQuota *instruction)
{
- if (ira->new_irb.exec->parent_exec != nullptr && !ira->new_irb.exec->is_generic_instantiation) {
- ir_add_error(ira, &instruction->base,
- buf_sprintf("@setEvalBranchQuota must be called from the top of the comptime stack"));
- return ira->codegen->invalid_instruction;
- }
-
uint64_t new_quota;
if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota))
return ira->codegen->invalid_instruction;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index b895e2c2d1..6a2ded1844 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -4554,20 +4554,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
".tmp_source.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal",
);
- cases.add(
- "@setEvalBranchQuota in non-root comptime execution context",
- \\comptime {
- \\ foo();
- \\}
- \\fn foo() void {
- \\ @setEvalBranchQuota(1001);
- \\}
- ,
- ".tmp_source.zig:5:5: error: @setEvalBranchQuota must be called from the top of the comptime stack",
- ".tmp_source.zig:2:8: note: called from here",
- ".tmp_source.zig:1:10: note: called from here",
- );
-
cases.add(
"wrong pointer implicitly casted to pointer to @OpaqueType()",
\\const Derp = @OpaqueType();
--
cgit v1.2.3
From e03c770145b5dc7b428d53b3cac97c2733fb84d8 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 12:28:50 -0500
Subject: compile error tests for implicit C pointer casting
See #1059
---
src/ir.cpp | 38 ++++++++++++++++++--------------------
test/compile_errors.zig | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 20 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index dfbc36e02c..89528db185 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8683,16 +8683,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
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)
@@ -8705,6 +8695,16 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
return result;
}
+ 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;
+ }
if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
result.id = ConstCastResultIdInvalid;
return result;
@@ -10846,22 +10846,20 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
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;
+ ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type;
+ ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type;
+ bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
+ bool actual_allows_zero = ptr_allows_addr_zero(actual_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)));
+ buf_ptr(&actual_type->name),
+ buf_ptr(&wanted_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)));
+ buf_ptr(&wanted_type->name),
+ buf_ptr(&actual_type->name)));
}
break;
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 71ee4901ff..630386aa4f 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,42 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "implicit cast between C pointer and Zig pointer - bad const/align/child",
+ \\export fn a() void {
+ \\ var x: [*c]u8 = undefined;
+ \\ var y: *align(4) u8 = x;
+ \\}
+ \\export fn b() void {
+ \\ var x: [*c]const u8 = undefined;
+ \\ var y: *u8 = x;
+ \\}
+ \\export fn c() void {
+ \\ var x: [*c]u8 = undefined;
+ \\ var y: *u32 = x;
+ \\}
+ \\export fn d() void {
+ \\ var y: *align(1) u32 = undefined;
+ \\ var x: [*c]u32 = y;
+ \\}
+ \\export fn e() void {
+ \\ var y: *const u8 = undefined;
+ \\ var x: [*c]u8 = y;
+ \\}
+ \\export fn f() void {
+ \\ var y: *u8 = undefined;
+ \\ var x: [*c]u32 = y;
+ \\}
+ ,
+ ".tmp_source.zig:3:27: error: cast increases pointer alignment",
+ ".tmp_source.zig:7:18: error: cast discards const qualifier",
+ ".tmp_source.zig:11:19: error: expected type '*u32', found '[*c]u8'",
+ ".tmp_source.zig:11:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32'",
+ ".tmp_source.zig:15:22: error: cast increases pointer alignment",
+ ".tmp_source.zig:19:21: error: cast discards const qualifier",
+ ".tmp_source.zig:23:22: error: expected type '[*c]u32', found '*u8'",
+ );
+
cases.addTest(
"implicit casting null c pointer to zig pointer",
\\comptime {
@@ -39,6 +75,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\}
,
".tmp_source.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'",
+ ".tmp_source.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]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'",
--
cgit v1.2.3
From 6769183a9d5f5ec69747f46d4d13c0f8709b2f46 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 15:48:28 -0500
Subject: fix implicit cast error unions with non-optional to optional pointer
and update self hosted compiler for C pointers
See #1059
---
doc/docgen.zig | 1 +
src-self-hosted/codegen.zig | 42 ++++-----
src-self-hosted/compilation.zig | 22 ++---
src-self-hosted/ir.zig | 20 ++---
src-self-hosted/llvm.zig | 181 +++++++++++++++++++++++++++-----------
src-self-hosted/scope.zig | 2 +-
src-self-hosted/target.zig | 4 +-
src-self-hosted/type.zig | 42 ++++-----
src-self-hosted/value.zig | 16 ++--
src/ir.cpp | 2 +-
std/hash_map.zig | 2 +
test/compile_errors.zig | 8 +-
test/stage1/behavior/pointers.zig | 17 ++++
13 files changed, 228 insertions(+), 131 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/doc/docgen.zig b/doc/docgen.zig
index 45f6dc2684..082f308a57 100644
--- a/doc/docgen.zig
+++ b/doc/docgen.zig
@@ -916,6 +916,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
std.zig.Token.Id.AngleBracketAngleBracketRightEqual,
std.zig.Token.Id.Tilde,
std.zig.Token.Id.BracketStarBracket,
+ std.zig.Token.Id.BracketStarCBracket,
=> try writeEscaped(out, src[token.start..token.end]),
std.zig.Token.Id.Invalid => return parseError(
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig
index 45cfa98942..1c671b61e2 100644
--- a/src-self-hosted/codegen.zig
+++ b/src-self-hosted/codegen.zig
@@ -137,10 +137,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
pub const ObjectFile = struct {
comp: *Compilation,
- module: llvm.ModuleRef,
- builder: llvm.BuilderRef,
+ module: *llvm.Module,
+ builder: *llvm.Builder,
dibuilder: *llvm.DIBuilder,
- context: llvm.ContextRef,
+ context: *llvm.Context,
lock: event.Lock,
arena: *std.mem.Allocator,
@@ -323,7 +323,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
fn addLLVMAttr(
ofile: *ObjectFile,
- val: llvm.ValueRef,
+ val: *llvm.Value,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
) !void {
@@ -335,7 +335,7 @@ fn addLLVMAttr(
fn addLLVMAttrStr(
ofile: *ObjectFile,
- val: llvm.ValueRef,
+ val: *llvm.Value,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
attr_val: []const u8,
@@ -351,7 +351,7 @@ fn addLLVMAttrStr(
}
fn addLLVMAttrInt(
- val: llvm.ValueRef,
+ val: *llvm.Value,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
attr_val: u64,
@@ -362,25 +362,25 @@ fn addLLVMAttrInt(
llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
}
-fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void {
+fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8) !void {
return addLLVMAttr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name);
}
-fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void {
+fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: []const u8) !void {
return addLLVMAttrStr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val);
}
-fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
+fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: u64) !void {
return addLLVMAttrInt(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val);
}
fn renderLoadUntyped(
ofile: *ObjectFile,
- ptr: llvm.ValueRef,
+ ptr: *llvm.Value,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
name: [*]const u8,
-) !llvm.ValueRef {
+) !*llvm.Value {
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
@@ -390,11 +390,11 @@ fn renderLoadUntyped(
return result;
}
-fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef {
+fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value {
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
}
-pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef {
+pub fn getHandleValue(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer) !?*llvm.Value {
const child_type = ptr_type.key.child_type;
if (!child_type.hasBits()) {
return null;
@@ -407,11 +407,11 @@ pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Po
pub fn renderStoreUntyped(
ofile: *ObjectFile,
- value: llvm.ValueRef,
- ptr: llvm.ValueRef,
+ value: *llvm.Value,
+ ptr: *llvm.Value,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
-) !llvm.ValueRef {
+) !*llvm.Value {
const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
@@ -423,10 +423,10 @@ pub fn renderStoreUntyped(
pub fn renderStore(
ofile: *ObjectFile,
- value: llvm.ValueRef,
- ptr: llvm.ValueRef,
+ value: *llvm.Value,
+ ptr: *llvm.Value,
ptr_type: *Type.Pointer,
-) !llvm.ValueRef {
+) !*llvm.Value {
return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
}
@@ -435,7 +435,7 @@ pub fn renderAlloca(
var_type: *Type,
name: []const u8,
alignment: Type.Pointer.Align,
-) !llvm.ValueRef {
+) !*llvm.Value {
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
@@ -443,7 +443,7 @@ pub fn renderAlloca(
return result;
}
-pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 {
+pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: *llvm.Type) u32 {
return switch (alignment) {
Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
Type.Pointer.Align.Override => |a| a,
diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig
index e55d8ccda6..de956f1525 100644
--- a/src-self-hosted/compilation.zig
+++ b/src-self-hosted/compilation.zig
@@ -37,7 +37,7 @@ const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
/// Data that is local to the event loop.
pub const ZigCompiler = struct {
loop: *event.Loop,
- llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
+ llvm_handle_pool: std.atomic.Stack(*llvm.Context),
lld_lock: event.Lock,
/// TODO pool these so that it doesn't have to lock
@@ -60,7 +60,7 @@ pub const ZigCompiler = struct {
return ZigCompiler{
.loop = loop,
.lld_lock = event.Lock.init(loop),
- .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
+ .llvm_handle_pool = std.atomic.Stack(*llvm.Context).init(),
.prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
.native_libc = event.Future(LibCInstallation).init(loop),
};
@@ -70,7 +70,7 @@ pub const ZigCompiler = struct {
fn deinit(self: *ZigCompiler) void {
self.lld_lock.deinit();
while (self.llvm_handle_pool.pop()) |node| {
- c.LLVMContextDispose(node.data);
+ llvm.ContextDispose(node.data);
self.loop.allocator.destroy(node);
}
}
@@ -80,11 +80,11 @@ pub const ZigCompiler = struct {
pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
- const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
- errdefer c.LLVMContextDispose(context_ref);
+ const context_ref = llvm.ContextCreate() orelse return error.OutOfMemory;
+ errdefer llvm.ContextDispose(context_ref);
- const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node);
- node.* = std.atomic.Stack(llvm.ContextRef).Node{
+ const node = try self.loop.allocator.create(std.atomic.Stack(*llvm.Context).Node);
+ node.* = std.atomic.Stack(*llvm.Context).Node{
.next = undefined,
.data = context_ref,
};
@@ -114,7 +114,7 @@ pub const ZigCompiler = struct {
};
pub const LlvmHandle = struct {
- node: *std.atomic.Stack(llvm.ContextRef).Node,
+ node: *std.atomic.Stack(*llvm.Context).Node,
pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
zig_compiler.llvm_handle_pool.push(self.node);
@@ -128,7 +128,7 @@ pub const Compilation = struct {
llvm_triple: Buffer,
root_src_path: ?[]const u8,
target: Target,
- llvm_target: llvm.TargetRef,
+ llvm_target: *llvm.Target,
build_mode: builtin.Mode,
zig_lib_dir: []const u8,
zig_std_dir: []const u8,
@@ -212,8 +212,8 @@ pub const Compilation = struct {
false_value: *Value.Bool,
noreturn_value: *Value.NoReturn,
- target_machine: llvm.TargetMachineRef,
- target_data_ref: llvm.TargetDataRef,
+ target_machine: *llvm.TargetMachine,
+ target_data_ref: *llvm.TargetData,
target_layout_str: [*]u8,
target_ptr_bits: u32,
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
index 0362bb4ef8..dc1b0dc943 100644
--- a/src-self-hosted/ir.zig
+++ b/src-self-hosted/ir.zig
@@ -67,7 +67,7 @@ pub const Inst = struct {
parent: ?*Inst,
/// populated durign codegen
- llvm_value: ?llvm.ValueRef,
+ llvm_value: ?*llvm.Value,
pub fn cast(base: *Inst, comptime T: type) ?*T {
if (base.id == comptime typeToId(T)) {
@@ -129,7 +129,7 @@ pub const Inst = struct {
}
}
- pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {
+ pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?*llvm.Value) {
switch (base.id) {
Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
@@ -313,10 +313,10 @@ pub const Inst = struct {
return new_inst;
}
- pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
+ pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
const fn_ref = self.params.fn_ref.llvm_value.?;
- const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len);
+ const args = try ofile.arena.alloc(*llvm.Value, self.params.args.len);
for (self.params.args) |arg, i| {
args[i] = arg.llvm_value.?;
}
@@ -360,7 +360,7 @@ pub const Inst = struct {
return new_inst;
}
- pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
+ pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
return self.base.val.KnownValue.getLlvmConst(ofile);
}
};
@@ -392,7 +392,7 @@ pub const Inst = struct {
return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
}
- pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
+ pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
const value = self.params.return_value.llvm_value;
const return_type = self.params.return_value.getKnownType();
@@ -540,7 +540,7 @@ pub const Inst = struct {
}
}
- pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef {
+ pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) *llvm.Value {
switch (self.params.var_scope.data) {
Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass
Scope.Var.Data.Param => |param| return param.llvm_value,
@@ -596,7 +596,7 @@ pub const Inst = struct {
return new_inst;
}
- pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
+ pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?*llvm.Value {
const child_type = self.base.getKnownType();
if (!child_type.hasBits()) {
return null;
@@ -935,8 +935,8 @@ pub const BasicBlock = struct {
ref_instruction: ?*Inst,
/// for codegen
- llvm_block: llvm.BasicBlockRef,
- llvm_exit_block: llvm.BasicBlockRef,
+ llvm_block: *llvm.BasicBlock,
+ llvm_exit_block: *llvm.BasicBlock,
/// the basic block that is derived from this one in analysis
child: ?*BasicBlock,
diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig
index 778d3fae07..704e83c3c6 100644
--- a/src-self-hosted/llvm.zig
+++ b/src-self-hosted/llvm.zig
@@ -11,45 +11,31 @@ const assert = @import("std").debug.assert;
pub const AttributeIndex = c_uint;
pub const Bool = c_int;
-pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
-pub const ContextRef = removeNullability(c.LLVMContextRef);
-pub const ModuleRef = removeNullability(c.LLVMModuleRef);
-pub const ValueRef = removeNullability(c.LLVMValueRef);
-pub const TypeRef = removeNullability(c.LLVMTypeRef);
-pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
-pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
-pub const TargetRef = removeNullability(c.LLVMTargetRef);
-pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
-pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
+pub const Builder = c.LLVMBuilderRef.Child;
+pub const Context = c.LLVMContextRef.Child;
+pub const Module = c.LLVMModuleRef.Child;
+pub const Value = c.LLVMValueRef.Child;
+pub const Type = c.LLVMTypeRef.Child;
+pub const BasicBlock = c.LLVMBasicBlockRef.Child;
+pub const Attribute = c.LLVMAttributeRef.Child;
+pub const Target = c.LLVMTargetRef.Child;
+pub const TargetMachine = c.LLVMTargetMachineRef.Child;
+pub const TargetData = c.LLVMTargetDataRef.Child;
pub const DIBuilder = c.ZigLLVMDIBuilder;
+pub const DIFile = c.ZigLLVMDIFile;
+pub const DICompileUnit = c.ZigLLVMDICompileUnit;
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
-pub const AddFunction = c.LLVMAddFunction;
-pub const AddGlobal = c.LLVMAddGlobal;
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
-pub const ArrayType = c.LLVMArrayType;
-pub const BuildLoad = c.LLVMBuildLoad;
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstArray = c.LLVMConstArray;
pub const ConstBitCast = c.LLVMConstBitCast;
-pub const ConstInt = c.LLVMConstInt;
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
pub const ConstNeg = c.LLVMConstNeg;
-pub const ConstNull = c.LLVMConstNull;
-pub const ConstStringInContext = c.LLVMConstStringInContext;
pub const ConstStructInContext = c.LLVMConstStructInContext;
-pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
-pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
-pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
-pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
-pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
-pub const CreateFile = c.ZigLLVMCreateFile;
-pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
-pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
-pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
pub const DisposeBuilder = c.LLVMDisposeBuilder;
pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
@@ -62,9 +48,7 @@ pub const DumpModule = c.LLVMDumpModule;
pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
-pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
-pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
pub const GetUndef = c.LLVMGetUndef;
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
@@ -81,14 +65,11 @@ pub const Int64TypeInContext = c.LLVMInt64TypeInContext;
pub const Int8TypeInContext = c.LLVMInt8TypeInContext;
pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext;
pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext;
-pub const IntTypeInContext = c.LLVMIntTypeInContext;
pub const LabelTypeInContext = c.LLVMLabelTypeInContext;
pub const MDNodeInContext = c.LLVMMDNodeInContext;
pub const MDStringInContext = c.LLVMMDStringInContext;
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
-pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
-pub const PointerType = c.LLVMPointerType;
pub const SetAlignment = c.LLVMSetAlignment;
pub const SetDataLayout = c.LLVMSetDataLayout;
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
@@ -99,50 +80,146 @@ pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
pub const SetVolatile = c.LLVMSetVolatile;
pub const StructTypeInContext = c.LLVMStructTypeInContext;
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
-pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
+pub const AddGlobal = LLVMAddGlobal;
+extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*]const u8) ?*Value;
+
+pub const ConstStringInContext = LLVMConstStringInContext;
+extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
+
+pub const ConstInt = LLVMConstInt;
+extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value;
+
+pub const BuildLoad = LLVMBuildLoad;
+extern fn LLVMBuildLoad(arg0: *Builder, PointerVal: *Value, Name: [*]const u8) ?*Value;
+
+pub const ConstNull = LLVMConstNull;
+extern fn LLVMConstNull(Ty: *Type) ?*Value;
+
+pub const CreateStringAttribute = LLVMCreateStringAttribute;
+extern fn LLVMCreateStringAttribute(
+ C: *Context,
+ K: [*]const u8,
+ KLength: c_uint,
+ V: [*]const u8,
+ VLength: c_uint,
+) ?*Attribute;
+
+pub const CreateEnumAttribute = LLVMCreateEnumAttribute;
+extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) ?*Attribute;
+
+pub const AddFunction = LLVMAddFunction;
+extern fn LLVMAddFunction(M: *Module, Name: [*]const u8, FunctionTy: *Type) ?*Value;
+
+pub const CreateCompileUnit = ZigLLVMCreateCompileUnit;
+extern fn ZigLLVMCreateCompileUnit(
+ dibuilder: *DIBuilder,
+ lang: c_uint,
+ difile: *DIFile,
+ producer: [*]const u8,
+ is_optimized: bool,
+ flags: [*]const u8,
+ runtime_version: c_uint,
+ split_name: [*]const u8,
+ dwo_id: u64,
+ emit_debug_info: bool,
+) ?*DICompileUnit;
+
+pub const CreateFile = ZigLLVMCreateFile;
+extern fn ZigLLVMCreateFile(dibuilder: *DIBuilder, filename: [*]const u8, directory: [*]const u8) ?*DIFile;
+
+pub const ArrayType = LLVMArrayType;
+extern fn LLVMArrayType(ElementType: *Type, ElementCount: c_uint) ?*Type;
+
+pub const CreateDIBuilder = ZigLLVMCreateDIBuilder;
+extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) ?*DIBuilder;
+
+pub const PointerType = LLVMPointerType;
+extern fn LLVMPointerType(ElementType: *Type, AddressSpace: c_uint) ?*Type;
+
+pub const CreateBuilderInContext = LLVMCreateBuilderInContext;
+extern fn LLVMCreateBuilderInContext(C: *Context) ?*Builder;
+
+pub const IntTypeInContext = LLVMIntTypeInContext;
+extern fn LLVMIntTypeInContext(C: *Context, NumBits: c_uint) ?*Type;
+
+pub const ModuleCreateWithNameInContext = LLVMModuleCreateWithNameInContext;
+extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*]const u8, C: *Context) ?*Module;
+
+pub const VoidTypeInContext = LLVMVoidTypeInContext;
+extern fn LLVMVoidTypeInContext(C: *Context) ?*Type;
+
+pub const ContextCreate = LLVMContextCreate;
+extern fn LLVMContextCreate() ?*Context;
+
+pub const ContextDispose = LLVMContextDispose;
+extern fn LLVMContextDispose(C: *Context) void;
+
+pub const CopyStringRepOfTargetData = LLVMCopyStringRepOfTargetData;
+extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) ?[*]u8;
+
+pub const CreateTargetDataLayout = LLVMCreateTargetDataLayout;
+extern fn LLVMCreateTargetDataLayout(T: *TargetMachine) ?*TargetData;
+
+pub const CreateTargetMachine = LLVMCreateTargetMachine;
+extern fn LLVMCreateTargetMachine(
+ T: *Target,
+ Triple: [*]const u8,
+ CPU: [*]const u8,
+ Features: [*]const u8,
+ Level: CodeGenOptLevel,
+ Reloc: RelocMode,
+ CodeModel: CodeModel,
+) ?*TargetMachine;
+
+pub const GetHostCPUName = LLVMGetHostCPUName;
+extern fn LLVMGetHostCPUName() ?[*]u8;
+
+pub const GetNativeFeatures = ZigLLVMGetNativeFeatures;
+extern fn ZigLLVMGetNativeFeatures() ?[*]u8;
+
pub const GetElementType = LLVMGetElementType;
-extern fn LLVMGetElementType(Ty: TypeRef) TypeRef;
+extern fn LLVMGetElementType(Ty: *Type) *Type;
pub const TypeOf = LLVMTypeOf;
-extern fn LLVMTypeOf(Val: ValueRef) TypeRef;
+extern fn LLVMTypeOf(Val: *Value) *Type;
pub const BuildStore = LLVMBuildStore;
-extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef;
+extern fn LLVMBuildStore(arg0: *Builder, Val: *Value, Ptr: *Value) ?*Value;
pub const BuildAlloca = LLVMBuildAlloca;
-extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef;
+extern fn LLVMBuildAlloca(arg0: *Builder, Ty: *Type, Name: ?[*]const u8) ?*Value;
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
-pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
+pub extern fn LLVMConstInBoundsGEP(ConstantVal: *Value, ConstantIndices: [*]*Value, NumIndices: c_uint) ?*Value;
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
-extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
+extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: **Target, ErrorMessage: ?*[*]u8) Bool;
pub const VerifyModule = LLVMVerifyModule;
-extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
+extern fn LLVMVerifyModule(M: *Module, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
pub const GetInsertBlock = LLVMGetInsertBlock;
-extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef;
+extern fn LLVMGetInsertBlock(Builder: *Builder) *BasicBlock;
pub const FunctionType = LLVMFunctionType;
extern fn LLVMFunctionType(
- ReturnType: TypeRef,
- ParamTypes: [*]TypeRef,
+ ReturnType: *Type,
+ ParamTypes: [*]*Type,
ParamCount: c_uint,
IsVarArg: Bool,
-) ?TypeRef;
+) ?*Type;
pub const GetParam = LLVMGetParam;
-extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef;
+extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value;
pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext;
-extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef;
+extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*]const u8) ?*BasicBlock;
pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd;
-extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void;
+extern fn LLVMPositionBuilderAtEnd(Builder: *Builder, Block: *BasicBlock) void;
pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction;
pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
@@ -190,17 +267,17 @@ pub const FnInline = extern enum {
};
fn removeNullability(comptime T: type) type {
- comptime assert(@typeId(T) == builtin.TypeId.Optional);
- return T.Child;
+ comptime assert(@typeInfo(T).Pointer.size == @import("builtin").TypeInfo.Pointer.Size.C);
+ return *T.Child;
}
pub const BuildRet = LLVMBuildRet;
-extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
+extern fn LLVMBuildRet(arg0: *Builder, V: ?*Value) ?*Value;
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
extern fn ZigLLVMTargetMachineEmitToFile(
- targ_machine_ref: TargetMachineRef,
- module_ref: ModuleRef,
+ targ_machine_ref: *TargetMachine,
+ module_ref: *Module,
filename: [*]const u8,
output_type: EmitOutputType,
error_message: *[*]u8,
@@ -209,6 +286,6 @@ extern fn ZigLLVMTargetMachineEmitToFile(
) bool;
pub const BuildCall = ZigLLVMBuildCall;
-extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
+extern fn ZigLLVMBuildCall(B: *Builder, Fn: *Value, Args: [*]*Value, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?*Value;
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;
diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig
index b14c073a9e..9a84ad256e 100644
--- a/src-self-hosted/scope.zig
+++ b/src-self-hosted/scope.zig
@@ -362,7 +362,7 @@ pub const Scope = struct {
pub const Param = struct {
index: usize,
typ: *Type,
- llvm_value: llvm.ValueRef,
+ llvm_value: *llvm.Value,
};
pub fn createParam(
diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig
index 36381b820d..121242b505 100644
--- a/src-self-hosted/target.zig
+++ b/src-self-hosted/target.zig
@@ -457,8 +457,8 @@ pub const Target = union(enum) {
}
}
- pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
- var result: llvm.TargetRef = undefined;
+ pub fn llvmTargetFromTriple(triple: std.Buffer) !*llvm.Target {
+ var result: *llvm.Target = undefined;
var err_msg: [*]u8 = undefined;
if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
index 790b51b7be..7d611bb787 100644
--- a/src-self-hosted/type.zig
+++ b/src-self-hosted/type.zig
@@ -51,8 +51,8 @@ pub const Type = struct {
pub fn getLlvmType(
base: *Type,
allocator: *Allocator,
- llvm_context: llvm.ContextRef,
- ) (error{OutOfMemory}!llvm.TypeRef) {
+ llvm_context: *llvm.Context,
+ ) (error{OutOfMemory}!*llvm.Type) {
switch (base.id) {
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
@@ -196,7 +196,7 @@ pub const Type = struct {
}
/// If you have an llvm conext handy, you can use it here.
- pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
+ pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
@@ -205,7 +205,7 @@ pub const Type = struct {
}
/// Lower level function that does the work. See getAbiAlignment.
- async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
+ async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
}
@@ -218,7 +218,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -496,13 +496,13 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
+ pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
const normal = &self.key.data.Normal;
const llvm_return_type = switch (normal.return_type.id) {
Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
else => try normal.return_type.getLlvmType(allocator, llvm_context),
};
- const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len);
+ const llvm_param_types = try allocator.alloc(*llvm.Type, normal.params.len);
defer allocator.free(llvm_param_types);
for (llvm_param_types) |*llvm_param_type, i| {
llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
@@ -559,7 +559,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -658,7 +658,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
+ pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
}
};
@@ -670,7 +670,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -836,7 +836,7 @@ pub const Type = struct {
return self;
}
- pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
+ pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
}
@@ -904,7 +904,7 @@ pub const Type = struct {
return self;
}
- pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
+ pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: *llvm.Context) !*llvm.Type {
const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
}
@@ -917,7 +917,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -967,7 +967,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -979,7 +979,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -991,7 +991,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -1003,7 +1003,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -1015,7 +1015,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -1035,7 +1035,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -1055,7 +1055,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
@@ -1067,7 +1067,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
+ pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: *llvm.Context) *llvm.Type {
@panic("TODO");
}
};
diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig
index 9431c614b9..d9d4c3d1d9 100644
--- a/src-self-hosted/value.zig
+++ b/src-self-hosted/value.zig
@@ -57,7 +57,7 @@ pub const Value = struct {
std.debug.warn("{}", @tagName(base.id));
}
- pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
+ pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?*llvm.Value) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
@@ -153,7 +153,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
+ pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?*llvm.Value {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
@@ -238,7 +238,7 @@ pub const Value = struct {
/// We know that the function definition will end up in an .o file somewhere.
/// Here, all we have to do is generate a global prototype.
/// TODO cache the prototype per ObjectFile
- pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef {
+ pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?*llvm.Value {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
@@ -283,7 +283,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
+ pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?*llvm.Value {
const llvm_type = llvm.Int1TypeInContext(ofile.context);
if (self.x) {
return llvm.ConstAllOnes(llvm_type);
@@ -381,7 +381,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
+ pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?*llvm.Value {
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
switch (self.special) {
@@ -391,7 +391,7 @@ pub const Value = struct {
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
- const indices = []llvm.ValueRef{
+ const indices = []*llvm.Value{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
@@ -459,7 +459,7 @@ pub const Value = struct {
comp.gpa().destroy(self);
}
- pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
+ pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?*llvm.Value {
switch (self.special) {
Special.Undefined => {
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
@@ -534,7 +534,7 @@ pub const Value = struct {
return self;
}
- pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
+ pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?*llvm.Value {
switch (self.base.typ.id) {
Type.Id.Int => {
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
diff --git a/src/ir.cpp b/src/ir.cpp
index 89528db185..7c46b21717 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -8696,7 +8696,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
return result;
}
bool ok_allows_zero = (wanted_allows_zero &&
- (actual_allows_zero || wanted_ptr_type->data.pointer.is_const)) ||
+ (actual_allows_zero || !wanted_is_mutable)) ||
(!wanted_allows_zero && !actual_allows_zero);
if (!ok_allows_zero) {
result.id = ConstCastResultIdBadAllowsZero;
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 716f04ff34..4519890bb7 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -496,6 +496,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
builtin.TypeId.Pointer => |info| switch (info.size) {
builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
+ builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto hash C pointers"),
builtin.TypeInfo.Pointer.Size.Slice => {
const interval = std.math.max(1, key.len / 256);
var i: usize = 0;
@@ -543,6 +544,7 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
builtin.TypeId.Pointer => |info| switch (info.size) {
builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
+ builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto eql for C pointers"),
builtin.TypeInfo.Pointer.Size.Slice => {
if (a.len != b.len) return false;
for (a) |a_item, i| {
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index ac8d413d2c..5e9b691641 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -92,15 +92,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ 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;
+ \\ var c_ptr: [*c][*c]const 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: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]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'",
+ ".tmp_source.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'",
+ ".tmp_source.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'",
+ ".tmp_source.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'",
);
cases.addTest(
diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig
index 8d87fe2a20..eed7a765d7 100644
--- a/test/stage1/behavior/pointers.zig
+++ b/test/stage1/behavior/pointers.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const expect = std.testing.expect;
+const expectError = std.testing.expectError;
test "dereference pointer" {
comptime testDerefPtr();
@@ -107,3 +108,19 @@ test "implicit casting between C pointer and optional non-C pointer" {
ptr_opt_many_ptr = c_ptr;
expect(ptr_opt_many_ptr.*.?[1] == 'o');
}
+
+test "implicit cast error unions with non-optional to optional pointer" {
+ const S = struct {
+ fn doTheTest() void {
+ expectError(error.Fail, foo());
+ }
+ fn foo() anyerror!?*u8 {
+ return bar() orelse error.Fail;
+ }
+ fn bar() ?*u8 {
+ return null;
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
--
cgit v1.2.3
From cc7060d0d934135d797bd2bc24288ecab095051a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 19:53:46 -0500
Subject: compile error for C pointer with align attribute
See #1059
---
src/ir.cpp | 5 +++++
test/compile_errors.zig | 12 ++++++++----
test/stage1/behavior/type_info.zig | 4 ++--
3 files changed, 15 insertions(+), 6 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 7c46b21717..6e190adf6f 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -5057,6 +5057,11 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode
IrInstruction *align_value;
if (align_expr != nullptr) {
+ if (ptr_len == PtrLenC) {
+ exec_add_error_node(irb->codegen, irb->exec, node,
+ buf_sprintf("[*c] pointers may not have align attribute"));
+ return irb->codegen->invalid_instruction;
+ }
align_value = ir_gen_node(irb, align_expr, scope);
if (align_value == irb->codegen->invalid_instruction)
return align_value;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 5e9b691641..ab9eda3f15 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -118,13 +118,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
);
cases.addTest(
- "C pointer pointing to non C ABI compatible type",
+ "C pointer pointing to non C ABI compatible type or has align attr",
\\const Foo = struct {};
- \\export fn entry() [*c]Foo {
- \\ return undefined;
+ \\export fn a() void {
+ \\ const T = [*c]Foo;
+ \\}
+ \\export fn b() void {
+ \\ const T = [*c]align(4) u8;
\\}
,
- ".tmp_source.zig:2:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'",
+ ".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'",
+ ".tmp_source.zig:6:15: error: [*c] pointers may not have align attribute",
);
cases.addTest(
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index dc185cc960..52e03c2d73 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -67,12 +67,12 @@ test "type info: C pointer type info" {
}
fn testCPtr() void {
- const ptr_info = @typeInfo([*c]align(4) const i8);
+ const ptr_info = @typeInfo([*c]const i8);
expect(TypeId(ptr_info) == TypeId.Pointer);
expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C);
expect(ptr_info.Pointer.is_const);
expect(!ptr_info.Pointer.is_volatile);
- expect(ptr_info.Pointer.alignment == 4);
+ expect(ptr_info.Pointer.alignment == 1);
expect(ptr_info.Pointer.child == i8);
}
--
cgit v1.2.3
From d5bbd748711abc82272199869cf70faf1ea30f52 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 14 Feb 2019 20:04:13 -0500
Subject: allow C pointers to have alignment
clang/gcc support pointer alignment attribute:
https://clang.llvm.org/docs/AttributeReference.html#align-value
---
src/ir.cpp | 5 -----
test/compile_errors.zig | 4 ----
test/stage1/behavior/type_info.zig | 4 ++--
3 files changed, 2 insertions(+), 11 deletions(-)
(limited to 'src/ir.cpp')
diff --git a/src/ir.cpp b/src/ir.cpp
index 6e190adf6f..7c46b21717 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -5057,11 +5057,6 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode
IrInstruction *align_value;
if (align_expr != nullptr) {
- if (ptr_len == PtrLenC) {
- exec_add_error_node(irb->codegen, irb->exec, node,
- buf_sprintf("[*c] pointers may not have align attribute"));
- return irb->codegen->invalid_instruction;
- }
align_value = ir_gen_node(irb, align_expr, scope);
if (align_value == irb->codegen->invalid_instruction)
return align_value;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index ab9eda3f15..a2fd901197 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -123,12 +123,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\export fn a() void {
\\ const T = [*c]Foo;
\\}
- \\export fn b() void {
- \\ const T = [*c]align(4) u8;
- \\}
,
".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'",
- ".tmp_source.zig:6:15: error: [*c] pointers may not have align attribute",
);
cases.addTest(
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index 52e03c2d73..dc185cc960 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -67,12 +67,12 @@ test "type info: C pointer type info" {
}
fn testCPtr() void {
- const ptr_info = @typeInfo([*c]const i8);
+ const ptr_info = @typeInfo([*c]align(4) const i8);
expect(TypeId(ptr_info) == TypeId.Pointer);
expect(ptr_info.Pointer.size == TypeInfo.Pointer.Size.C);
expect(ptr_info.Pointer.is_const);
expect(!ptr_info.Pointer.is_volatile);
- expect(ptr_info.Pointer.alignment == 1);
+ expect(ptr_info.Pointer.alignment == 4);
expect(ptr_info.Pointer.child == i8);
}
--
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/ir.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/ir.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/ir.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