From b8cbe3872e702ab8ec388e75cb711330a45825b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 00:14:30 -0500 Subject: [PATCH 01/27] 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 | 262 ++++++++++++++++++++---------- src/parser.cpp | 8 + src/tokenizer.cpp | 19 ++- src/tokenizer.hpp | 1 + test/stage1/behavior/pointers.zig | 6 + 7 files changed, 223 insertions(+), 88 deletions(-) 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,17 +20744,38 @@ 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; +static bool type_can_bit_cast(ZigType *t) { + switch (t->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdNamespace: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdPointer: + return false; + default: + // TODO list these types out explicitly, there are probably some other invalid ones here + return true; + } +} + +static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *dest_type) +{ + Error err; - IrInstruction *value = instruction->value->child; ZigType *src_type = value->value.type; - if (type_is_invalid(src_type)) - return ira->codegen->invalid_instruction; + 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; @@ -20692,60 +20783,11 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct 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) { - 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 from type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } - - 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))); - 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,20 +20809,86 @@ 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_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; + + 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 (!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 (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))); + return ira->codegen->invalid_instruction; + } + + 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)) + return ira->codegen->invalid_instruction; + + if (instr_is_comptime(casted_int)) { + ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + 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, 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; @@ -20802,30 +20910,12 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; } + IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); - if (type_is_invalid(casted_int->value.type)) - return ira->codegen->invalid_instruction; - - if (instr_is_comptime(casted_int)) { - ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; - - IrInstruction *result = ir_const(ira, &instruction->base, dest_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; - return result; + return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type); } static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, 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; +} From 73e8e46257ac8f34e941a357a860d19e0d38dbac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 01:11:30 -0500 Subject: [PATCH 02/27] 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(-) 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); +} From 661fc79fba242f2ad72ede06d4e23e635a1b1452 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 12:02:38 -0500 Subject: [PATCH 03/27] langref: update grammar with c pointers See #1059 --- doc/langref.html.in | 7 +++++-- src/parser.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 779eb6a31b..82ec13c9bb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8164,7 +8164,8 @@ ArrayTypeStart <- LBRACKET Expr? RBRACKET PtrTypeStart <- ASTERISK / ASTERISK2 - / LBRACKET ASTERISK RBRACKET + / PTRUNKNOWN + / PTRC # ContainerDecl specific ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE @@ -8262,7 +8263,7 @@ LARROW2 <- '<<' ![=] skip LARROW2EQUAL <- '<<=' skip LARROWEQUAL <- '<=' skip LBRACE <- '{' skip -LBRACKET <- '[' skip +LBRACKET <- '[' ![*] skip LPAREN <- '(' skip MINUS <- '-' ![%=>] skip MINUSEQUAL <- '-=' skip @@ -8279,6 +8280,8 @@ PLUS2 <- '++' skip PLUSEQUAL <- '+=' skip PLUSPERCENT <- '+%' ![=] skip PLUSPERCENTEQUAL <- '+%=' skip +PTRC <- '[*c]' skip +PTRUNKNOWN <- '[*]' skip QUESTIONMARK <- '?' skip RARROW <- '>' ![>=] skip RARROW2 <- '>>' ![=] skip diff --git a/src/parser.cpp b/src/parser.cpp index 160a7268b0..3a6ce04647 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2778,8 +2778,8 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // PtrTypeStart // <- ASTERISK // / ASTERISK2 -// / LBRACKET ASTERISK RBRACKET -// / LBRACKET ASTERISK C RBRACKET +// / PTRUNKNOWN +// / PTRC static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { Token *asterisk = eat_token_if(pc, TokenIdStar); if (asterisk != nullptr) { From 4a1b910e03f931b7d8d3cb8c810b084e6e152e18 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 14:07:19 -0500 Subject: [PATCH 04/27] zig fmt: support C pointers See #1059 --- std/zig/parse.zig | 7 ++++++- std/zig/parser_test.zig | 7 +++++++ std/zig/tokenizer.zig | 23 ++++++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/std/zig/parse.zig b/std/zig/parse.zig index ae3e00eb4b..867dd11592 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -3525,7 +3525,12 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op { Token.Id.Minus => ast.Node.PrefixOp.Op{ .Negation = void{} }, Token.Id.MinusPercent => ast.Node.PrefixOp.Op{ .NegationWrap = void{} }, Token.Id.Ampersand => ast.Node.PrefixOp.Op{ .AddressOf = void{} }, - Token.Id.Asterisk, Token.Id.AsteriskAsterisk, Token.Id.BracketStarBracket => ast.Node.PrefixOp.Op{ + + Token.Id.Asterisk, + Token.Id.AsteriskAsterisk, + Token.Id.BracketStarBracket, + Token.Id.BracketStarCBracket, + => ast.Node.PrefixOp.Op{ .PtrType = ast.Node.PrefixOp.PtrInfo{ .align_info = null, .const_token = null, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 93d5ce3437..5b7b7aa2a9 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,10 @@ +test "zig fmt: C pointers" { + try testCanonical( + \\const Ptr = [*c]i32; + \\ + ); +} + test "zig fmt: threadlocal" { try testCanonical( \\threadlocal var x: i32 = 1234; diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 08ffa28fce..877e4e8db3 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -141,6 +141,7 @@ pub const Token = struct { LineComment, DocComment, BracketStarBracket, + BracketStarCBracket, ShebangLine, Keyword_align, Keyword_and, @@ -279,6 +280,7 @@ pub const Tokenizer = struct { SawAtSign, LBracket, LBracketStar, + LBracketStarC, }; pub fn next(self: *Tokenizer) Token { @@ -456,6 +458,9 @@ pub const Tokenizer = struct { }, State.LBracketStar => switch (c) { + 'c' => { + state = State.LBracketStarC; + }, ']' => { result.id = Token.Id.BracketStarBracket; self.index += 1; @@ -467,6 +472,18 @@ pub const Tokenizer = struct { }, }, + State.LBracketStarC => switch (c) { + ']' => { + result.id = Token.Id.BracketStarCBracket; + self.index += 1; + break; + }, + else => { + result.id = Token.Id.Invalid; + break; + }, + }, + State.Ampersand => switch (c) { '=' => { result.id = Token.Id.AmpersandEqual; @@ -1035,6 +1052,7 @@ pub const Tokenizer = struct { State.CharLiteralEnd, State.StringLiteralBackslash, State.LBracketStar, + State.LBracketStarC, => { result.id = Token.Id.Invalid; }, @@ -1169,12 +1187,15 @@ test "tokenizer" { testTokenize("test", []Token.Id{Token.Id.Keyword_test}); } -test "tokenizer - unknown length pointer" { +test "tokenizer - unknown length pointer and then c pointer" { testTokenize( \\[*]u8 + \\[*c]u8 , []Token.Id{ Token.Id.BracketStarBracket, Token.Id.Identifier, + Token.Id.BracketStarCBracket, + Token.Id.Identifier, }); } From d9e01be97386f008e4a4b4281658f25b50ff80f1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 14:56:59 -0500 Subject: [PATCH 05/27] translate-c: use C pointer type everywhere See #1059 --- src/analyze.cpp | 9 +++++++ src/analyze.hpp | 1 + src/ast_render.cpp | 27 +++++++++---------- src/ast_render.hpp | 3 --- src/translate_c.cpp | 50 ++++++++++++----------------------- test/translate_c.zig | 62 ++++++++++++++++++++++---------------------- 6 files changed, 72 insertions(+), 80 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index e561050e0d..691579200e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6872,3 +6872,12 @@ Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_no return ErrorNone; } + +const char *container_string(ContainerKind kind) { + switch (kind) { + case ContainerKindEnum: return "enum"; + case ContainerKindStruct: return "struct"; + case ContainerKindUnion: return "union"; + } + zig_unreachable(); +} diff --git a/src/analyze.hpp b/src/analyze.hpp index 9773782510..1e4f2f2ce7 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -215,6 +215,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty); bool type_is_c_abi_int(CodeGen *g, ZigType *ty); bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id); +const char *container_string(ContainerKind kind); uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 34a7faa2a5..7b57841205 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -136,13 +136,19 @@ static const char *thread_local_string(Token *tok) { return (tok == nullptr) ? "" : "threadlocal "; } -const char *container_string(ContainerKind kind) { - switch (kind) { - case ContainerKindEnum: return "enum"; - case ContainerKindStruct: return "struct"; - case ContainerKindUnion: return "union"; +static const char *token_to_ptr_len_str(Token *tok) { + assert(tok != nullptr); + switch (tok->id) { + case TokenIdStar: + case TokenIdStarStar: + return "*"; + case TokenIdBracketStarBracket: + return "[*]"; + case TokenIdBracketStarCBracket: + return "[*c]"; + default: + zig_unreachable(); } - zig_unreachable(); } static const char *node_type_str(NodeType node_type) { @@ -644,13 +650,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypePointerType: { if (!grouped) fprintf(ar->f, "("); - const char *star = "[*]"; - if (node->data.pointer_type.star_token != nullptr && - (node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar)) - { - star = "*"; - } - fprintf(ar->f, "%s", star); + const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token); + fprintf(ar->f, "%s", ptr_len_str); if (node->data.pointer_type.align_expr != nullptr) { fprintf(ar->f, "align("); render_node_grouped(ar, node->data.pointer_type.align_expr); diff --git a/src/ast_render.hpp b/src/ast_render.hpp index d37002d8c7..1652156eee 100644 --- a/src/ast_render.hpp +++ b/src/ast_render.hpp @@ -17,7 +17,4 @@ void ast_print(FILE *f, AstNode *node, int indent); void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size); -const char *container_string(ContainerKind kind); - #endif - diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 02fa3b24be..b06a28d12d 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -291,11 +291,22 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod node); } +static TokenId ptr_len_to_token_id(PtrLen ptr_len) { + switch (ptr_len) { + case PtrLenSingle: + return TokenIdStar; + case PtrLenUnknown: + return TokenIdBracketStarBracket; + case PtrLenC: + return TokenIdBracketStarCBracket; + } + zig_unreachable(); +} + static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) { AstNode *node = trans_create_node(c, NodeTypePointerType); node->data.pointer_type.star_token = allocate(1); - node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket; - node->data.pointer_type.is_const = is_const; + node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len); node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_volatile = is_volatile; node->data.pointer_type.op_expr = child_node; @@ -752,30 +763,6 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) { } } -static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) { - switch (ty->getTypeClass()) { - case Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); - return builtin_ty->getKind() == BuiltinType::Void; - } - case Type::Record: { - const RecordType *record_ty = static_cast(ty); - return record_ty->getDecl()->getDefinition() == nullptr; - } - case Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); - return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc); - } - case Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); - return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc); - } - default: - return false; - } -} - static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { switch (ty->getTypeClass()) { case Type::Builtin: @@ -925,11 +912,8 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return trans_create_node_prefix_op(c, PrefixOpOptional, child_node); } - PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown; - - AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_node, ptr_len); - return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node); + return trans_create_node_ptr_type(c, child_qt.isConstQualified(), + child_qt.isVolatileQualified(), child_node, PtrLenC); } case Type::Typedef: { @@ -1113,7 +1097,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return nullptr; } AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), - child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown); + child_qt.isVolatileQualified(), child_type_node, PtrLenC); return pointer_node; } case Type::BlockPointer: @@ -4568,7 +4552,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t } else if (first_tok->id == CTokIdAsterisk) { *tok_i += 1; - node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown); + node = trans_create_node_ptr_type(c, false, false, node, PtrLenC); } else { return node; } diff --git a/test/translate_c.zig b/test/translate_c.zig index 13f2a964d0..746fa60b18 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -117,11 +117,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ a: ?[*]Foo, + \\ a: [*c]Foo, \\}; \\pub const Foo = struct_Foo; \\pub const struct_Bar = extern struct { - \\ a: ?[*]Foo, + \\ a: [*c]Foo, \\}; ); @@ -202,7 +202,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("restrict -> noalias", \\void foo(void *restrict bar, void *restrict); , - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; + \\pub extern fn foo(noalias bar: [*c]c_void, noalias arg1: [*c]c_void) void; ); cases.add("simple struct", @@ -213,7 +213,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\const struct_Foo = extern struct { \\ x: c_int, - \\ y: ?[*]u8, + \\ y: [*c]u8, \\}; , \\pub const Foo = struct_Foo; @@ -244,7 +244,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const BarB = enum_Bar.B; , - \\pub extern fn func(a: ?[*]struct_Foo, b: ?[*](?[*]enum_Bar)) void; + \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; , \\pub const Foo = struct_Foo; , @@ -254,7 +254,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("constant size array", \\void func(int array[20]); , - \\pub extern fn func(array: ?[*]c_int) void; + \\pub extern fn func(array: [*c]c_int) void; ); cases.add("self referential struct with function pointer", @@ -263,7 +263,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Foo = extern struct { - \\ derp: ?extern fn(?[*]struct_Foo) void, + \\ derp: ?extern fn([*c]struct_Foo) void, \\}; , \\pub const Foo = struct_Foo; @@ -275,7 +275,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const struct_Foo = @OpaqueType(); , - \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo; + \\pub extern fn some_func(foo: [*c]struct_Foo, x: c_int) [*c]struct_Foo; , \\pub const Foo = struct_Foo; ); @@ -322,11 +322,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; , \\pub const struct_Bar = extern struct { - \\ next: ?[*]struct_Foo, + \\ next: [*c]struct_Foo, \\}; , \\pub const struct_Foo = extern struct { - \\ next: ?[*]struct_Bar, + \\ next: [*c]struct_Bar, \\}; ); @@ -336,7 +336,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const Foo = c_void; , - \\pub extern fn fun(a: ?*Foo) Foo; + \\pub extern fn fun(a: [*c]Foo) Foo; ); cases.add("generate inline func for #define global extern fn", @@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 6; \\} , - \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int { \\ if ((a != 0) and (b != 0)) return 0; \\ if ((b != 0) and (c != null)) return 1; \\ if ((a != 0) and (c != null)) return 2; @@ -710,7 +710,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const struct_Foo = extern struct { \\ field: c_int, \\}; - \\pub export fn read_field(foo: ?[*]struct_Foo) c_int { + \\pub export fn read_field(foo: [*c]struct_Foo) c_int { \\ return foo.?.field; \\} ); @@ -756,8 +756,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return x; \\} , - \\pub export fn foo(x: ?[*]c_ushort) ?*c_void { - \\ return @ptrCast(?*c_void, x); + \\pub export fn foo(x: [*c]c_ushort) [*c]c_void { + \\ return @ptrCast([*c]c_void, x); \\} ); @@ -777,7 +777,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 0; \\} , - \\pub export fn foo() ?[*]c_int { + \\pub export fn foo() [*c]c_int { \\ return null; \\} ); @@ -1086,7 +1086,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ *x = 1; \\} , - \\pub export fn foo(x: ?[*]c_int) void { + \\pub export fn foo(x: [*c]c_int) void { \\ x.?.* = 1; \\} ); @@ -1114,7 +1114,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub fn foo() c_int { \\ var x: c_int = 1234; - \\ var ptr: ?[*]c_int = &x; + \\ var ptr: [*c]c_int = &x; \\ return ptr.?.*; \\} ); @@ -1124,7 +1124,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return "bar"; \\} , - \\pub fn foo() ?[*]const u8 { + \\pub fn foo() [*c]const u8 { \\ return c"bar"; \\} ); @@ -1253,8 +1253,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return (float *)a; \\} , - \\fn ptrcast(a: ?[*]c_int) ?[*]f32 { - \\ return @ptrCast(?[*]f32, a); + \\fn ptrcast(a: [*c]c_int) [*c]f32 { + \\ return @ptrCast([*c]f32, a); \\} ); @@ -1276,7 +1276,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return !c; \\} , - \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn foo(a: c_int, b: f32, c: [*c]c_void) c_int { \\ return !(a == 0); \\ return !(a != 0); \\ return !(b != 0); @@ -1297,7 +1297,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("const ptr initializer", \\static const char *v0 = "0.0.0"; , - \\pub var v0: ?[*]const u8 = c"0.0.0"; + \\pub var v0: [*c]const u8 = c"0.0.0"; ); cases.add("static incomplete array inside function", @@ -1306,17 +1306,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ const v2: [*]const u8 = c"2.2.2"; + \\ const v2: [*c]const u8 = c"2.2.2"; \\} ); cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , - \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*]NRF_GPIO_Type)(NRF_GPIO_BASE); + \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else ([*c]NRF_GPIO_Type)(NRF_GPIO_BASE); ); - cases.add("if on none bool", + cases.add("if on non-bool", \\enum SomeEnum { A, B, C }; \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { \\ if (a) return 0; @@ -1334,7 +1334,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ C, \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\pub fn if_none_bool(a: c_int, b: f32, c: [*c]c_void, d: enum_SomeEnum) c_int { \\ if (a != 0) return 0; \\ if (b != 0) return 1; \\ if (c != null) return 2; @@ -1343,7 +1343,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} ); - cases.add("while on none bool", + cases.add("while on non-bool", \\int while_none_bool(int a, float b, void *c) { \\ while (a) return 0; \\ while (b) return 1; @@ -1351,7 +1351,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn while_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; @@ -1359,7 +1359,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} ); - cases.add("for on none bool", + cases.add("for on non-bool", \\int for_none_bool(int a, float b, void *c) { \\ for (;a;) return 0; \\ for (;b;) return 1; @@ -1367,7 +1367,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return 3; \\} , - \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\pub fn for_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int { \\ while (a != 0) return 0; \\ while (b != 0) return 1; \\ while (c != null) return 2; From 342bca7f4627454435e9f6c2d12b099f95a2fd47 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 15:31:09 -0500 Subject: [PATCH 06/27] 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(-) 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; \\} ); From 90b8cd4a45bcb2ca131b6ed6466f799aaa162d13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 16:07:40 -0500 Subject: [PATCH 07/27] 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(-) 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(); From 57a7ab0d330416f15c0288004b67101c1c3e9629 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 19:12:01 -0500 Subject: [PATCH 08/27] 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(-) 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(); } From 069fc1a26990b3946cf788b4ebe5edefca5a3bfd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 19:21:59 -0500 Subject: [PATCH 09/27] peer type resolution with C pointers See #1059 --- src/ir.cpp | 18 ++++++++++++++++++ test/stage1/behavior/pointers.zig | 15 +++++++++++++++ 2 files changed, 33 insertions(+) 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); +} From 0abe6d668eb52aefa59c75a8d8f782f2372f8600 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Feb 2019 00:39:08 -0500 Subject: [PATCH 10/27] C pointers: delete dead code in ir_num_lit_fits_in_other_type --- src/ir.cpp | 14 -------------- 1 file changed, 14 deletions(-) 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(); From 285e2f62ba0648d6d8e7ff64d1ee7d2900481e2f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Feb 2019 00:51:06 -0500 Subject: [PATCH 11/27] 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(-) 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 { From 6f05e8d1be083a429673e717e8b3c736c7ddb8d2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Feb 2019 01:38:11 -0500 Subject: [PATCH 12/27] 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(-) 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'); +} From 270933b1e997c91a9c2d28b6896d625c0ae1b163 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Feb 2019 10:25:21 -0500 Subject: [PATCH 13/27] 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(-) 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 {}; From 5699ab5e77f8d13cac1e34775e6e51358119965c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 12 Feb 2019 18:20:00 -0500 Subject: [PATCH 14/27] 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(-) 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( From d4d2718bca9e23ceec029bb505c0ea1b91c875b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 00:40:39 -0500 Subject: [PATCH 15/27] 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(-) 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 { From 59de24817e8538434f35a20a401f40c2f0231a9a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 01:09:33 -0500 Subject: [PATCH 16/27] 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(-) 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); From e03c770145b5dc7b428d53b3cac97c2733fb84d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 12:28:50 -0500 Subject: [PATCH 17/27] 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(-) 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'", From 52c03de5c2495b369ae730ff203e5342e4f33a36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 13:07:51 -0500 Subject: [PATCH 18/27] add missing compile error for OpaqueType inside structs/unions closes #1862 --- src/analyze.cpp | 14 ++++++++++++++ test/compile_errors.zig | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/analyze.cpp b/src/analyze.cpp index 6a8090a843..90ce3d3371 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2679,6 +2679,13 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) { buf_sprintf("enums, not structs, support field assignment")); } + if (field_type->id == ZigTypeIdOpaque) { + add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in structs")); + struct_type->data.structure.resolve_status = ResolveStatusInvalid; + continue; + } + switch (type_requires_comptime(g, field_type)) { case ReqCompTimeYes: struct_type->data.structure.requires_comptime = true; @@ -2963,6 +2970,13 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { } union_field->type_entry = field_type; + if (field_type->id == ZigTypeIdOpaque) { + add_node_error(g, field_node->data.struct_field.type, + buf_sprintf("opaque types have unknown size and therefore cannot be directly embedded in unions")); + union_type->data.unionation.is_invalid = true; + continue; + } + switch (type_requires_comptime(g, field_type)) { case ReqCompTimeInvalid: union_type->data.unionation.is_invalid = true; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 630386aa4f..ac8d413d2c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "directly embedding opaque type in struct and union", + \\const O = @OpaqueType(); + \\const Foo = struct { + \\ o: O, + \\}; + \\const Bar = union { + \\ One: i32, + \\ Two: O, + \\}; + \\export fn a() void { + \\ var foo: Foo = undefined; + \\} + \\export fn b() void { + \\ var bar: Bar = undefined; + \\} + , + ".tmp_source.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs", + ".tmp_source.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions", + ); + cases.addTest( "implicit cast between C pointer and Zig pointer - bad const/align/child", \\export fn a() void { From 6769183a9d5f5ec69747f46d4d13c0f8709b2f46 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 15:48:28 -0500 Subject: [PATCH 19/27] 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(-) 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(); +} From df87044fd6588452755014d5909e0db1b776deb2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 16:10:12 -0500 Subject: [PATCH 20/27] omit nonnull attribute for C pointers See #1059 --- src/analyze.cpp | 4 ++++ src/analyze.hpp | 1 + src/codegen.cpp | 19 ++++++++++++++++--- src/target.cpp | 4 ++++ src/target.hpp | 1 + 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 90ce3d3371..55deafb3a8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4100,6 +4100,10 @@ ZigType *get_codegen_ptr_type(ZigType *type) { return ty; } +bool type_is_nonnull_ptr(ZigType *type) { + return type_is_codegen_pointer(type) && !ptr_allows_addr_zero(type); +} + bool type_is_codegen_pointer(ZigType *type) { return get_codegen_ptr_type(type) == type; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 50e841baa1..845fb62534 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -46,6 +46,7 @@ void find_libc_lib_path(CodeGen *g); bool type_has_bits(ZigType *type_entry); bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry); bool ptr_allows_addr_zero(ZigType *ptr_type); +bool type_is_nonnull_ptr(ZigType *type); ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code); diff --git a/src/codegen.cpp b/src/codegen.cpp index dbb13ca885..bae9abe06d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -617,9 +617,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { unsigned init_gen_i = 0; if (!type_has_bits(return_type)) { // nothing to do - } else if (type_is_codegen_pointer(return_type)) { + } else if (type_is_nonnull_ptr(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { + // Sret pointers must not be address 0 addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); if (cc_want_sret_attr(cc)) { @@ -637,6 +638,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); if (err_ret_trace_arg_index != UINT32_MAX) { + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); } @@ -1246,6 +1249,8 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); if (g->build_mode == BuildModeDebug) { ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); @@ -1320,9 +1325,13 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); addLLVMArgAttr(fn_val, (unsigned)0, "noalias"); addLLVMArgAttr(fn_val, (unsigned)0, "writeonly"); + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)1, "nonnull"); addLLVMArgAttr(fn_val, (unsigned)1, "noalias"); addLLVMArgAttr(fn_val, (unsigned)1, "readonly"); @@ -1450,6 +1459,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); addLLVMFnAttr(fn_val, "nounwind"); add_uwtable_attr(g, fn_val); + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); if (g->build_mode == BuildModeDebug) { ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true"); @@ -2051,7 +2062,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ case FnWalkIdAttrs: { ZigType *ptr_type = get_codegen_ptr_type(ty); if (ptr_type != nullptr) { - if (ty->id != ZigTypeIdOptional) { + if (type_is_nonnull_ptr(ty)) { addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); } if (ptr_type->data.pointer.is_const) { @@ -2095,6 +2106,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ assert(handle_is_ptr(ty)); switch (fn_walk->id) { case FnWalkIdAttrs: + // arrays passed to C ABI functions may not be at address 0 addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); fn_walk->data.attrs.gen_i += 1; @@ -2134,6 +2146,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ case FnWalkIdAttrs: addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); + // Byvalue parameters must not have address 0 addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); fn_walk->data.attrs.gen_i += 1; break; @@ -2266,7 +2279,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly"); } - if (param_type->id == ZigTypeIdPointer) { + if (type_is_nonnull_ptr(param_type)) { addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); } break; diff --git a/src/target.cpp b/src/target.cpp index 6fea79518c..b1434c6871 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -807,6 +807,10 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { zig_unreachable(); } +bool target_allows_addr_zero(const ZigTarget *target) { + return target->os == OsFreestanding; +} + const char *target_o_file_ext(ZigTarget *target) { if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) { return ".obj"; diff --git a/src/target.hpp b/src/target.hpp index a87b12351a..99d1cadf56 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -135,5 +135,6 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target ZigLLVM_OSType get_llvm_os_type(Os os_type); bool target_is_arm(const ZigTarget *target); +bool target_allows_addr_zero(const ZigTarget *target); #endif From 973a93d43b8b44d2ee8bfde09e78f8295470f337 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 18:59:20 -0500 Subject: [PATCH 21/27] add docs for C pointers --- doc/langref.html.in | 60 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 82ec13c9bb..e5a60b0bc1 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1694,7 +1694,7 @@ test "comptime @intToPtr" { } } {#code_end#} - {#see_also|Optional Pointers#} + {#see_also|Optional Pointers|@intToPtr|@ptrToInt#} {#header_open|volatile#}

Loads and stores are assumed to not have side effects. If a given load or store should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}. @@ -1823,7 +1823,9 @@ fn foo(bytes: []u8) u32 { } {#code_end#} {#header_close#} + {#see_also|C Pointers#} {#header_close#} + {#header_open|Slices#} {#code_begin|test_safety|index out of bounds#} const assert = @import("std").debug.assert; @@ -3981,7 +3983,7 @@ test "implicit cast - invoke a type as a function" { {#code_end#}

Implicit casts are only allowed when it is completely unambiguous how to get from one type to another, - and the transformation is guaranteed to be safe. + and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.

{#header_open|Implicit Cast: Stricter Qualification#}

@@ -6104,6 +6106,10 @@ test "call foo" {

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

+

+ {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} + to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. +

{#header_close#} {#header_open|@ptrToInt#} @@ -7345,10 +7351,27 @@ fn bar(f: *Foo) void { {#code_end#} {#header_close#} - {#header_open|Out of Bounds Float To Integer Cast#} + {#header_open|Out of Bounds Float to Integer Cast#}

TODO

{#header_close#} + {#header_open|Pointer Cast Invalid Null#} +

At compile-time:

+ {#code_begin|test_err|null pointer casted to type#} +comptime { + const opt_ptr: ?*i32 = null; + const ptr = @ptrCast(*i32, opt_ptr); +} + {#code_end#} +

At runtime:

+ {#code_begin|exe_err#} +pub fn main() void { + var opt_ptr: ?*i32 = null; + var ptr = @ptrCast(*i32, opt_ptr); +} + {#code_end#} + {#header_close#} + {#header_close#} {#header_open|Memory#}

TODO: explain no default allocator in zig

@@ -7439,6 +7462,7 @@ pub fn main() void { {#code_end#} {#see_also|String Literals#} {#header_close#} + {#header_open|Import from C Header File#}

The {#syntax#}@cImport{#endsyntax#} builtin function can be used @@ -7477,6 +7501,36 @@ const c = @cImport({ {#code_end#} {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} {#header_close#} + + {#header_open|C Pointers#} +

+ This type is to be avoided whenever possible. The only valid reason for using a C pointer is in + auto-generated code from translating C code. +

+

+ When importing C header files, it is ambiguous whether pointers should be translated as + single-item pointers ({#syntax#}*T{#endsyntax#}) or unknown-length pointers ({#syntax#}[*]T{#endsyntax#}). + C pointers are a compromise so that Zig code can utilize translated header files directly. +

+

{#syntax#}[*c]T{#endsyntax#} - C pointer.

+
    +
  • Supports all the syntax of the other two pointer types.
  • +
  • Implicitly casts to other pointer types, as well as {#link|Optional Pointers#}. + When a C pointer is implicitly casted to a non-optional pointer, safety-checked + {#link|Undefined Behavior#} occurs if the address is 0. +
  • +
  • Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked + {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of + null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer + is unnecessary as one can use normal {#link|Optional Pointers#}. +
  • +
  • Supports {#link|implicit casting|Implicit Casts#} to and from integers.
  • +
  • Supports comparison with integers.
  • +
  • Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#} + please!
  • +
+ {#header_close#} + {#header_open|Exporting a C Library#}

One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages From cc7060d0d934135d797bd2bc24288ecab095051a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 19:53:46 -0500 Subject: [PATCH 22/27] 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(-) 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); } From d5bbd748711abc82272199869cf70faf1ea30f52 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 20:04:13 -0500 Subject: [PATCH 23/27] 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(-) 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); } From d6e0d82c328b4f9d733364382cce0941a601e91a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 23:09:12 -0500 Subject: [PATCH 24/27] 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(-) 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; From e025c70166d96d7a75a5039415321745f51f1f58 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 23:17:11 -0500 Subject: [PATCH 25/27] stage2: fix llvm.zig with opaque types back to single-item pointer --- src-self-hosted/llvm.zig | 20 ++++++++++---------- src-self-hosted/value.zig | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 704e83c3c6..5cb95682ab 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -11,16 +11,16 @@ const assert = @import("std").debug.assert; pub const AttributeIndex = c_uint; pub const Bool = c_int; -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 Builder = c.LLVMBuilderRef.Child.Child; +pub const Context = c.LLVMContextRef.Child.Child; +pub const Module = c.LLVMModuleRef.Child.Child; +pub const Value = c.LLVMValueRef.Child.Child; +pub const Type = c.LLVMTypeRef.Child.Child; +pub const BasicBlock = c.LLVMBasicBlockRef.Child.Child; +pub const Attribute = c.LLVMAttributeRef.Child.Child; +pub const Target = c.LLVMTargetRef.Child.Child; +pub const TargetMachine = c.LLVMTargetMachineRef.Child.Child; +pub const TargetData = c.LLVMTargetDataRef.Child.Child; pub const DIBuilder = c.ZigLLVMDIBuilder; pub const DIFile = c.ZigLLVMDIFile; pub const DICompileUnit = c.ZigLLVMDICompileUnit; diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index d9d4c3d1d9..d8c0f7b5c8 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -283,8 +283,8 @@ pub const Value = struct { comp.gpa().destroy(self); } - pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?*llvm.Value { - const llvm_type = llvm.Int1TypeInContext(ofile.context); + pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) !?*llvm.Value { + const llvm_type = llvm.Int1TypeInContext(ofile.context) orelse return error.OutOfMemory; if (self.x) { return llvm.ConstAllOnes(llvm_type); } else { From 71d7100aa830652490d3995bf4e1319f31b5e647 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 23:38:14 -0500 Subject: [PATCH 26/27] darwin: fix pointer cast in mmap --- std/os/darwin.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/darwin.zig b/std/os/darwin.zig index c64ce807b6..3e883abbab 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -665,7 +665,7 @@ pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { const ptr_result = c.mmap( - @ptrCast(*c_void, address), + @ptrCast(?*c_void, address), length, @bitCast(c_int, @intCast(c_uint, prot)), @bitCast(c_int, c_uint(flags)), From 99b19adeb31469cbc4a906f036bb4d70d8730916 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 23:46:53 -0500 Subject: [PATCH 27/27] stage2: fix windows regressions --- src-self-hosted/libc_installation.zig | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index edcb9dc579..d3a461bcd9 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -154,8 +154,8 @@ pub const LibCInstallation = struct { c.ZigFindWindowsSdkError.None => { windows_sdk = sdk; - if (sdk.msvc_lib_dir_ptr) |ptr| { - self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]); + if (sdk.msvc_lib_dir_ptr != 0) { + self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]); } try group.call(findNativeKernel32LibDir, self, loop, sdk); try group.call(findNativeIncludeDirWindows, self, loop, sdk); @@ -437,20 +437,20 @@ const Search = struct { fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { var search_end: usize = 0; - if (sdk.path10_ptr) |path10_ptr| { - if (sdk.version10_ptr) |ver10_ptr| { + if (sdk.path10_ptr != 0) { + if (sdk.version10_ptr != 0) { search_buf[search_end] = Search{ - .path = path10_ptr[0..sdk.path10_len], - .version = ver10_ptr[0..sdk.version10_len], + .path = sdk.path10_ptr[0..sdk.path10_len], + .version = sdk.version10_ptr[0..sdk.version10_len], }; search_end += 1; } } - if (sdk.path81_ptr) |path81_ptr| { - if (sdk.version81_ptr) |ver81_ptr| { + if (sdk.path81_ptr != 0) { + if (sdk.version81_ptr != 0) { search_buf[search_end] = Search{ - .path = path81_ptr[0..sdk.path81_len], - .version = ver81_ptr[0..sdk.version81_len], + .path = sdk.path81_ptr[0..sdk.path81_len], + .version = sdk.version81_ptr[0..sdk.version81_len], }; search_end += 1; }