diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2019-02-15 02:20:42 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-15 02:20:42 -0500 |
| commit | 567c9b688effdb64e3995df09af4b45105515c2c (patch) | |
| tree | 11bc01aa7484d427455a12502710b0ee286fb6aa /src | |
| parent | ee5e196f8832359cfe05808677f143d4f460f6bc (diff) | |
| parent | 99b19adeb31469cbc4a906f036bb4d70d8730916 (diff) | |
| download | zig-567c9b688effdb64e3995df09af4b45105515c2c.tar.gz zig-567c9b688effdb64e3995df09af4b45105515c2c.zip | |
Merge pull request #1965 from ziglang/c-pointer-type
implement C pointers
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 16 | ||||
| -rw-r--r-- | src/analyze.cpp | 97 | ||||
| -rw-r--r-- | src/analyze.hpp | 5 | ||||
| -rw-r--r-- | src/ast_render.cpp | 27 | ||||
| -rw-r--r-- | src/ast_render.hpp | 3 | ||||
| -rw-r--r-- | src/codegen.cpp | 43 | ||||
| -rw-r--r-- | src/ir.cpp | 617 | ||||
| -rw-r--r-- | src/parser.cpp | 10 | ||||
| -rw-r--r-- | src/target.cpp | 4 | ||||
| -rw-r--r-- | src/target.hpp | 1 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 19 | ||||
| -rw-r--r-- | src/tokenizer.hpp | 1 | ||||
| -rw-r--r-- | src/translate_c.cpp | 40 |
13 files changed, 662 insertions, 221 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index 908c0e327c..bafe316c3d 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; }; @@ -1038,6 +1040,7 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b); enum PtrLen { PtrLenUnknown, PtrLenSingle, + PtrLenC, }; struct ZigTypePointer { @@ -1049,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 { @@ -1484,6 +1488,7 @@ enum PanicMsgId { PanicMsgIdBadUnionField, PanicMsgIdBadEnumValue, PanicMsgIdFloatToInt, + PanicMsgIdPtrCastNull, PanicMsgIdCount, }; @@ -1498,11 +1503,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; @@ -2591,6 +2597,7 @@ struct IrInstructionPtrType { PtrLen ptr_len; bool is_const; bool is_volatile; + bool allow_zero; }; struct IrInstructionPromiseType { @@ -2606,6 +2613,7 @@ struct IrInstructionSliceType { IrInstruction *child_type; bool is_const; bool is_volatile; + bool allow_zero; }; struct IrInstructionAsm { @@ -2994,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 b3b4c36cf1..e2a96da7c3 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -417,10 +417,25 @@ 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) { + // 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 == PtrLenSingle || child_type->id != ZigTypeIdOpaque); @@ -440,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; @@ -449,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) @@ -466,21 +482,31 @@ 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 " : ""; + 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); @@ -488,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; @@ -522,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; @@ -838,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); @@ -861,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, @@ -1457,7 +1486,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(); @@ -2650,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; @@ -2934,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; @@ -4041,7 +4084,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; } @@ -4055,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; } @@ -6300,6 +6349,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); @@ -6350,6 +6400,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; @@ -6883,3 +6934,21 @@ 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(); +} + +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 9773782510..845fb62534 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -44,7 +44,9 @@ 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); +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); @@ -215,6 +217,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/codegen.cpp b/src/codegen.cpp index 192c0f0519..d2662b10d2 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"); } @@ -950,6 +953,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(); } @@ -1244,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"); @@ -1318,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"); @@ -1448,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"); @@ -2049,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) { @@ -2093,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; @@ -2132,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; @@ -2264,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; @@ -2657,7 +2672,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 +2731,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."); @@ -3028,7 +3043,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, @@ -7294,6 +7324,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 03dea10b10..c9262038e0 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<ErrorTableEntry *> missing_errors; }; +struct ConstCastBadAllowsZero { + ZigType *wanted_type; + ZigType *actual_type; +}; + + enum UndefAllowed { UndefOk, UndefBad, @@ -164,11 +172,15 @@ 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); 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); @@ -2190,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<IrInstructionPtrCastSrc>( 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); @@ -2204,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<IrInstructionPtrCastGen>( &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); @@ -2458,7 +2472,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, @@ -4493,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: @@ -5019,10 +5033,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; @@ -6715,14 +6742,15 @@ 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, 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); @@ -6793,14 +6821,15 @@ 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, 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); @@ -6916,7 +6945,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); @@ -6959,7 +6988,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); @@ -6991,7 +7020,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); @@ -7338,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); @@ -7362,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); @@ -7440,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); @@ -7492,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); @@ -8619,7 +8652,6 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp return err_set_type; } - static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type, ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable) { @@ -8632,53 +8664,63 @@ 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<ConstCastOnly>(1); - *result.data.null_wrap_ptr_child = child; - } - return result; - } - - // 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); + // 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; + 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) { + 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<ConstCastPointerMismatch>(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; + } + bool ok_allows_zero = (wanted_allows_zero && + (actual_allows_zero || !wanted_is_mutable)) || + (!wanted_allows_zero && !actual_allows_zero); + if (!ok_allows_zero) { + result.id = ConstCastResultIdBadAllowsZero; + result.data.bad_allows_zero = allocate_nonzero<ConstCastBadAllowsZero>(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_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) && + 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 && + 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; } @@ -8912,7 +8954,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]; @@ -9229,6 +9273,37 @@ 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 (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; } @@ -9864,7 +9939,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; } } @@ -10770,6 +10845,24 @@ 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: { + 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_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(&wanted_type->name), + buf_ptr(&actual_type->name))); + } + break; + } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO @@ -10780,7 +10873,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; } } @@ -10811,6 +10903,39 @@ 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 '%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; + } + + 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) { @@ -11182,7 +11307,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); } } @@ -11217,6 +11342,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_array_to_vector(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, true); + } + + // 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 if (actual_type->id == ZigTypeIdUndefined) { return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); @@ -11588,28 +11730,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 { @@ -11773,7 +11921,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: @@ -11785,6 +11932,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 == PtrLenC); + break; + case ZigTypeIdUnreachable: case ZigTypeIdArray: case ZigTypeIdStruct: @@ -11832,15 +11983,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) { @@ -11848,9 +12022,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 @@ -12245,7 +12418,29 @@ 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) { + Error err; + IrInstruction *op1 = instruction->op1->child; if (type_is_invalid(op1->value.type)) return ira->codegen->invalid_instruction; @@ -12257,13 +12452,44 @@ 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) + 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; @@ -16366,7 +16592,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; } @@ -17116,7 +17342,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)) @@ -17290,7 +17516,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. @@ -17469,6 +17695,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; @@ -17478,7 +17716,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(); } @@ -20236,7 +20474,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; @@ -20369,7 +20607,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; @@ -20379,12 +20617,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; @@ -20396,10 +20636,23 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ } if (instr_is_comptime(ptr)) { - 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; @@ -20423,7 +20676,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, @@ -20460,7 +20713,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) { @@ -20668,32 +20922,10 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { - Error err; - IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_instruction; - - IrInstruction *value = instruction->value->child; - ZigType *src_type = value->value.type; - if (type_is_invalid(src_type)) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - - if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; - - if (get_codegen_ptr_type(src_type) != nullptr) { - ir_add_error(ira, value, - buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; - } - - switch (src_type->id) { +static bool type_can_bit_cast(ZigType *t) { + switch (t->id) { case ZigTypeIdInvalid: + zig_unreachable(); case ZigTypeIdMetaType: case ZigTypeIdOpaque: case ZigTypeIdBoundFn: @@ -20704,42 +20936,36 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct case ZigTypeIdComptimeInt: case ZigTypeIdUndefined: case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_instruction; + case ZigTypeIdPointer: + return false; default: - break; + // TODO list these types out explicitly, there are probably some other invalid ones here + return true; } +} - if (get_codegen_ptr_type(dest_type) != nullptr) { - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); +static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *dest_type) +{ + Error err; + + ZigType *src_type = value->value.type; + assert(get_codegen_ptr_type(src_type) == nullptr); + assert(type_can_bit_cast(src_type)); + assert(get_codegen_ptr_type(dest_type) == nullptr); + assert(type_can_bit_cast(dest_type)); + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; + + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; - } - switch (dest_type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdArgTuple: - case ZigTypeIdNamespace: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - ir_add_error(ira, dest_type_value, - buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - default: - break; - } uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); uint64_t src_size_bytes = type_size(ira->codegen, src_type); if (dest_size_bytes != src_size_bytes) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, source_instr, buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, buf_ptr(&dest_type->name), dest_size_bytes, buf_ptr(&src_type->name), src_size_bytes)); @@ -20749,7 +20975,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)); @@ -20761,44 +20987,63 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); + IrInstruction *result = ir_const(ira, source_instr, dest_type); uint8_t *buf = allocate_nonzero<uint8_t>(src_size_bytes); buf_write_value_bytes(ira->codegen, buf, val); - if ((err = buf_read_value_bytes(ira, ira->codegen, instruction->base.source_node, buf, &result->value))) + if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value))) return ira->codegen->invalid_instruction; return result; } - IrInstruction *result = ir_build_bit_cast(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, value); + IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope, + source_instr->source_node, nullptr, value); result->value.type = dest_type; return result; } -static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { - Error err; +static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - // We explicitly check for the size, so we can use get_src_ptr_type - if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + IrInstruction *value = instruction->value->child; + ZigType *src_type = value->value.type; + if (type_is_invalid(src_type)) + return ira->codegen->invalid_instruction; + + if (get_codegen_ptr_type(src_type) != nullptr) { + ir_add_error(ira, value, + buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + if (!type_can_bit_cast(src_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; - if (!type_has_bits(dest_type)) { + } + + if (get_codegen_ptr_type(dest_type) != nullptr) { ir_add_error(ira, dest_type_value, - buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); + buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } - IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + if (!type_can_bit_cast(dest_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; + } + + return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type); +} + +static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + ZigType *ptr_type) +{ + assert(get_src_ptr_type(ptr_type) != nullptr); + assert(type_has_bits(ptr_type)); IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); if (type_is_invalid(casted_int->value.type)) @@ -20809,19 +21054,48 @@ static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, &instruction->base, dest_type); + IrInstruction *result = ir_const(ira, source_instr, ptr_type); result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar; result->value.data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&val->data.x_bigint); return result; } - IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, casted_int); - result->value.type = dest_type; + IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, nullptr, casted_int); + result->value.type = ptr_type; return result; } +static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { + Error err; + IrInstruction *dest_type_value = instruction->dest_type->child; + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_instruction; + + // We explicitly check for the size, so we can use get_src_ptr_type + if (get_src_ptr_type(dest_type) == nullptr) { + ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_instruction; + if (!type_has_bits(dest_type)) { + ir_add_error(ira, dest_type_value, + buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + + IrInstruction *target = instruction->target->child; + if (type_is_invalid(target->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type); +} + static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstructionDeclRef *instruction) { @@ -20925,6 +21199,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) { + 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/parser.cpp b/src/parser.cpp index 1c1af87c51..3a6ce04647 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2778,7 +2778,8 @@ static AstNode *ast_parse_array_type_start(ParseContext *pc) { // PtrTypeStart // <- ASTERISK // / ASTERISK2 -// / LBRACKET ASTERISK RBRACKET +// / PTRUNKNOWN +// / PTRC 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/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 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 @@ -858,6 +858,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); t.state = TokenizeStateStart; @@ -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/src/translate_c.cpp b/src/translate_c.cpp index 02fa3b24be..42a7ab436d 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<ZigToken>(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; @@ -925,11 +936,14 @@ 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); + 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: { @@ -1113,7 +1127,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: @@ -1693,7 +1707,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; @@ -2425,7 +2439,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: @@ -2510,7 +2525,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: { @@ -4568,7 +4584,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; } |
