From dd5450e7b9f3a6eeb6c02b6a76c82fa3aff37180 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 26 Jan 2019 13:51:50 +0100 Subject: [PATCH 001/134] translate-c: get real child type of array type for incomplete initializers and/or multi-dimensional arrays. --- src/translate_c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 02fa3b24be..1cf30278fc 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4079,7 +4079,7 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const init_node->data.container_init_expr.type = arr_type_node; init_node->data.container_init_expr.kind = ContainerInitKindArray; - QualType child_qt = qt.getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType(); + QualType child_qt = qt.getTypePtr()->getAsArrayTypeUnsafe()->getElementType(); for (size_t i = 0; i < init_count; i += 1) { APValue &elem_ap_val = ap_value->getArrayInitializedElt(i); From b2662e443dac700efcc5c14bfb5ccb89a60cdb60 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 26 Jan 2019 15:38:17 +0100 Subject: [PATCH 002/134] translate-c: correct array concatenation for incomplete C array initializers. --- src/translate_c.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 1cf30278fc..54a97039d9 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4076,6 +4076,9 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const unsigned leftover_count = all_count - init_count; AstNode *init_node = trans_create_node(c, NodeTypeContainerInitExpr); AstNode *arr_type_node = trans_qual_type(c, qt, source_loc); + if (leftover_count != 0) { // We can't use the size of the final array for a partial initializer. + bigint_init_unsigned(arr_type_node->data.array_type.size->data.int_literal.bigint, init_count); + } init_node->data.container_init_expr.type = arr_type_node; init_node->data.container_init_expr.kind = ContainerInitKindArray; @@ -4097,10 +4100,14 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const if (filler_node == nullptr) return nullptr; + AstNode* filler_arr_type = trans_create_node(c, NodeTypeArrayType); + *filler_arr_type = *arr_type_node; + filler_arr_type->data.array_type.size = trans_create_node_unsigned(c, 1); + AstNode *filler_arr_1 = trans_create_node(c, NodeTypeContainerInitExpr); - init_node->data.container_init_expr.type = arr_type_node; - init_node->data.container_init_expr.kind = ContainerInitKindArray; - init_node->data.container_init_expr.entries.append(filler_node); + filler_arr_1->data.container_init_expr.type = filler_arr_type; + filler_arr_1->data.container_init_expr.kind = ContainerInitKindArray; + filler_arr_1->data.container_init_expr.entries.append(filler_node); AstNode *rhs_node; if (leftover_count == 1) { From 584cb1fcfea5ff9f708719e345d621af24fa141f Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 26 Jan 2019 15:53:19 +0100 Subject: [PATCH 003/134] translate-c: only detect ints as negative if they are signed. --- src/translate_c.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 54a97039d9..4cd8ed5a58 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -468,7 +468,7 @@ static const char *decl_name(const Decl *decl) { static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) { AstNode *node = trans_create_node(c, NodeTypeIntLiteral); node->data.int_literal.bigint = allocate(1); - bool is_negative = aps_int.isNegative(); + bool is_negative = aps_int.isSigned() && aps_int.isNegative(); if (!is_negative) { bigint_init_data(node->data.int_literal.bigint, aps_int.getRawData(), aps_int.getNumWords(), false); return node; From f0d35d78b602859583773326a5ed2b53dfd70161 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 26 Jan 2019 16:47:11 +0100 Subject: [PATCH 004/134] translate-c: avoid array concatenation if the init node is empty, for clarity. --- src/translate_c.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 4cd8ed5a58..574742631f 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4117,6 +4117,10 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const rhs_node = trans_create_node_bin_op(c, filler_arr_1, BinOpTypeArrayMult, amt_node); } + if (init_count == 0) { + return rhs_node; + } + return trans_create_node_bin_op(c, init_node, BinOpTypeArrayCat, rhs_node); } case APValue::LValue: { From 075fda3c732dc0e34d9917cc9c441b4dff75aa0e Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Tue, 5 Feb 2019 20:36:57 +0100 Subject: [PATCH 005/134] translate-c: add tests. Commented for now as the output is currently empty until #646 is fixed. --- test/translate_c.zig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/translate_c.zig b/test/translate_c.zig index 13f2a964d0..02020ddd73 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1416,4 +1416,34 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} ); + + // cases.add("empty array with initializer", + // "int a[4] = {};" + // , + // "pub var a: [4]c_int = [1]c_int{0} ** 4;" + // ); + + // cases.add("array with initialization", + // "int a[4] = {1, 2, 3, 4};" + // , + // "pub var a: [4]c_int = [4]c_int{1, 2, 3, 4};" + // ); + + // cases.add("array with incomplete initialization", + // "int a[4] = {3, 4};" + // , + // "pub var a: [4]c_int = [2]c_int{3, 4} ++ ([1]c_int{0} ** 2);" + // ); + + // cases.add("2D array with initialization", + // "int a[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };" + // , + // "pub var a: [3][3]c_int = [3][3]c_int{[3]c_int{1, 2, 3}, [3]c_int{4, 5, 6}, [3]c_int{7, 8, 9}};" + // ); + + // cases.add("2D array with incomplete initialization", + // "int a[3][3] = { {1, 2}, {4, 5, 6} };" + // , + // "pub var a: [3][3]c_int = [2][3]c_int{[2]c_int{1, 2} ++ [1]c_int{0}, [3]c_int{4, 5, 6}} ++ [1][3]c_int{[1]c_int{0} ** 3};" + // ); } From b8cbe3872e702ab8ec388e75cb711330a45825b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 00:14:30 -0500 Subject: [PATCH 006/134] 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 007/134] 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 8e68d43ad373e643797209d59e6f10aa12b4c038 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 10:58:00 -0500 Subject: [PATCH 008/134] avoid needlessly creating global constants This deletes some legacy cruft, and produces leaner object files. Example: ``` var x: i32 = 1234; export fn entry() i32 { return x; } ``` This produces: ``` @x = internal unnamed_addr global i32 1234, align 4 @0 = internal unnamed_addr constant i32* @x, align 8 ``` and @0 is never even used. After this commit, @0 is not produced. This fixes a bug: Zig was creating invalid LLVM IR when one of these globals that shouldn't exist takes the address of a thread local variable. In LLVM 8.0.0rc2, it would produce a linker error. But probably after my bug report is solved it will be caught by the IR verifier. https://bugs.llvm.org/show_bug.cgi?id=40652 --- src/codegen.cpp | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4868576b49..192c0f0519 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5762,81 +5762,71 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con zig_unreachable(); case ConstPtrSpecialRef: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; render_const_val(g, pointee, ""); render_const_val_global(g, pointee, ""); - ConstExprValue *other_val = pointee; - const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); - render_const_val_global(g, const_val, ""); + const_val->global_refs->llvm_value = LLVMConstBitCast(pointee->global_refs->llvm_global, const_val->type->type_ref); return const_val->global_refs->llvm_value; } case ConstPtrSpecialBaseArray: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; assert(array_const_val->type->id == ZigTypeIdArray); - if (array_const_val->type->zero_bits) { + if (!type_has_bits(array_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->llvm_value; } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, - elem_index); + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); return ptr_val; } case ConstPtrSpecialBaseStruct: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; assert(struct_const_val->type->id == ZigTypeIdStruct); - if (struct_const_val->type->zero_bits) { + if (!type_has_bits(struct_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->llvm_value; } size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; - size_t gen_field_index = - struct_const_val->type->data.structure.fields[src_field_index].gen_index; + size_t gen_field_index = struct_const_val->type->data.structure.fields[src_field_index].gen_index; LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, gen_field_index); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); return ptr_val; } case ConstPtrSpecialBaseErrorUnionCode: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); - if (err_union_const_val->type->zero_bits) { + if (!type_has_bits(err_union_const_val->type)) { // make this a null pointer ZigType *usize = g->builtin_types.entry_usize; const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->llvm_value; } LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); return ptr_val; } case ConstPtrSpecialBaseErrorUnionPayload: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); if (err_union_const_val->type->zero_bits) { @@ -5844,18 +5834,16 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con ZigType *usize = g->builtin_types.entry_usize; const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->llvm_value; } LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); return ptr_val; } case ConstPtrSpecialBaseOptionalPayload: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; assert(optional_const_val->type->id == ZigTypeIdOptional); if (optional_const_val->type->zero_bits) { @@ -5863,23 +5851,20 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con ZigType *usize = g->builtin_types.entry_usize; const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), const_val->type->type_ref); - render_const_val_global(g, const_val, ""); return const_val->global_refs->llvm_value; } LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); const_val->global_refs->llvm_value = ptr_val; - render_const_val_global(g, const_val, ""); return ptr_val; } case ConstPtrSpecialHardCodedAddr: { - render_const_val_global(g, const_val, name); + assert(const_val->global_refs != nullptr); uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; ZigType *usize = g->builtin_types.entry_usize; - const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false), - const_val->type->type_ref); - render_const_val_global(g, const_val, ""); + const_val->global_refs->llvm_value = LLVMConstIntToPtr( + LLVMConstInt(usize->type_ref, addr_value, false), const_val->type->type_ref); return const_val->global_refs->llvm_value; } case ConstPtrSpecialFunction: From 661fc79fba242f2ad72ede06d4e23e635a1b1452 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 10 Feb 2019 12:02:38 -0500 Subject: [PATCH 009/134] 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 43df49cb35349e48b63356eef4a990c2233ec88f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 12:59:33 -0500 Subject: [PATCH 010/134] README: move i386-macosx to Tier 4 See #1930 --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e9756b404d..8b64d75c11 100644 --- a/README.md +++ b/README.md @@ -84,13 +84,16 @@ clarity. * LLVM may have the target as an experimental target, which means that you need to use Zig-provided binaries for the target to be available, or build LLVM from source with special configure flags. + * This target may be considered deprecated by an official party, + [such as macosx/i386](https://support.apple.com/en-us/HT208436) in which + case this target will remain forever stuck in Tier 4. #### Support Table | | freestanding | linux | macosx | windows | freebsd | UEFI | other | |--------|--------------|--------|--------|---------|---------|--------|--------| |x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 3 | -|i386 | Tier 2 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | +|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | |arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | |arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | |bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | From 4a1b910e03f931b7d8d3cb8c810b084e6e152e18 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Feb 2019 14:07:19 -0500 Subject: [PATCH 011/134] 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 012/134] 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 013/134] 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 014/134] 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 015/134] 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 016/134] 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 017/134] 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 018/134] 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 019/134] 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 020/134] 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 021/134] 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 0b3db784f1cf21dd35fc14dfee29ab6fd867c0ed Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Sun, 3 Feb 2019 01:15:51 -0800 Subject: [PATCH 022/134] Enable compileLog to display slices --- src/analyze.cpp | 53 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 970d1cc382..b3b4c36cf1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6010,16 +6010,20 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const } } -static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) { - switch (const_val->data.x_array.special) { +static void render_const_val_array(CodeGen *g, Buf *buf, Buf *type_name, ConstExprValue *const_val, uint64_t start, uint64_t len) { + ConstArrayValue *array = &const_val->data.x_array; + switch (array->special) { case ConstArraySpecialUndef: buf_append_str(buf, "undefined"); return; case ConstArraySpecialBuf: { - Buf *array_buf = const_val->data.x_array.data.s_buf; + Buf *array_buf = array->data.s_buf; + const char *base = &buf_ptr(array_buf)[start]; + assert(start + len <= buf_len(array_buf)); + buf_append_char(buf, '"'); - for (size_t i = 0; i < buf_len(array_buf); i += 1) { - uint8_t c = buf_ptr(array_buf)[i]; + for (size_t i = 0; i < len; i += 1) { + uint8_t c = base[i]; if (c == '"') { buf_append_str(buf, "\\\""); } else { @@ -6030,12 +6034,13 @@ static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_v return; } case ConstArraySpecialNone: { - buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name)); + ConstExprValue *base = &array->data.s_none.elements[start]; + assert(start + len <= const_val->type->data.array.len); + + buf_appendf(buf, "%s{", buf_ptr(type_name)); for (uint64_t i = 0; i < len; i += 1) { - if (i != 0) - buf_appendf(buf, ","); - ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i]; - render_const_value(g, buf, child_value); + if (i != 0) buf_appendf(buf, ","); + render_const_value(g, buf, &base[i]); } buf_appendf(buf, "}"); return; @@ -6124,10 +6129,16 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdPointer: return render_const_val_ptr(g, buf, const_val, type_entry); - case ZigTypeIdVector: - return render_const_val_array(g, buf, const_val, type_entry->data.vector.len); - case ZigTypeIdArray: - return render_const_val_array(g, buf, const_val, type_entry->data.array.len); + case ZigTypeIdArray: { + uint64_t len = type_entry->data.array.len; + render_const_val_array(g, buf, &type_entry->name, const_val, 0, len); + return; + } + case ZigTypeIdVector: { + uint32_t len = type_entry->data.vector.len; + render_const_val_array(g, buf, &type_entry->name, const_val, 0, len); + return; + } case ZigTypeIdNull: { buf_appendf(buf, "null"); @@ -6169,7 +6180,19 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdStruct: { - buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); + if (is_slice(type_entry)) { + ConstPtrValue *ptr = &const_val->data.x_struct.fields[slice_ptr_index].data.x_ptr; + assert(ptr->special == ConstPtrSpecialBaseArray); + ConstExprValue *array = ptr->data.base_array.array_val; + size_t start = ptr->data.base_array.elem_index; + + ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index]; + size_t len = bigint_as_unsigned(&len_val->data.x_bigint); + + render_const_val_array(g, buf, &type_entry->name, array, start, len); + } else { + buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); + } return; } case ZigTypeIdEnum: From be861a85c8a1adc1f82c523136e6d6b18992c372 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 13 Feb 2019 23:24:52 +1300 Subject: [PATCH 023/134] compiler-rt: Add __addtf3, __subtf3 and __truncdfhf2 Allows addition/subtraction of f128 and narrowing casts to f16 from larger float types. --- CMakeLists.txt | 1 + std/special/compiler_rt/addXf3.zig | 191 ++++++++++++++++++++ std/special/compiler_rt/addXf3_test.zig | 85 +++++++++ std/special/compiler_rt/index.zig | 4 + std/special/compiler_rt/truncXfYf2.zig | 4 + std/special/compiler_rt/truncXfYf2_test.zig | 68 +++++++ 6 files changed, 353 insertions(+) create mode 100644 std/special/compiler_rt/addXf3.zig create mode 100644 std/special/compiler_rt/addXf3_test.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index ed79f99901..41fa44374e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -608,6 +608,7 @@ set(ZIG_STD_FILES "special/bootstrap_lib.zig" "special/build_runner.zig" "special/builtin.zig" + "special/compiler_rt/addXf3.zig" "special/compiler_rt/aulldiv.zig" "special/compiler_rt/aullrem.zig" "special/compiler_rt/comparetf2.zig" diff --git a/std/special/compiler_rt/addXf3.zig b/std/special/compiler_rt/addXf3.zig new file mode 100644 index 0000000000..5f7f73c44f --- /dev/null +++ b/std/special/compiler_rt/addXf3.zig @@ -0,0 +1,191 @@ +// Ported from: +// +// https://github.com/llvm-mirror/compiler-rt/blob/92f7768ce940f6437b32ecc0985a1446cd040f7a/lib/builtins/fp_add_impl.inc + +const std = @import("std"); +const builtin = @import("builtin"); +const compiler_rt = @import("index.zig"); + +pub extern fn __addtf3(a: f128, b: f128) f128 { + return addXf3(f128, a, b); +} + +pub extern fn __subtf3(a: f128, b: f128) f128 { + const neg_b = @bitCast(f128, @bitCast(u128, b) ^ (u128(1) << 127)); + return addXf3(f128, a, neg_b); +} + +inline fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { + const Z = @IntType(false, T.bit_count); + const significandBits = std.math.floatMantissaBits(T); + const implicitBit = Z(1) << significandBits; + + const shift = @clz(significand.*) - @clz(implicitBit); + significand.* <<= @intCast(u7, shift); + return 1 - shift; +} + +inline fn addXf3(comptime T: type, a: T, b: T) T { + const Z = @IntType(false, T.bit_count); + + const typeWidth = T.bit_count; + const significandBits = std.math.floatMantissaBits(T); + const exponentBits = std.math.floatExponentBits(T); + + const signBit = (Z(1) << (significandBits + exponentBits)); + const maxExponent = ((1 << exponentBits) - 1); + const exponentBias = (maxExponent >> 1); + + const implicitBit = (Z(1) << significandBits); + const quietBit = implicitBit >> 1; + const significandMask = implicitBit - 1; + + const absMask = signBit - 1; + const exponentMask = absMask ^ significandMask; + const qnanRep = exponentMask | quietBit; + + var aRep = @bitCast(Z, a); + var bRep = @bitCast(Z, b); + const aAbs = aRep & absMask; + const bAbs = bRep & absMask; + + const negative = (aRep & signBit) != 0; + const exponent = @intCast(i32, aAbs >> significandBits) - exponentBias; + const significand = (aAbs & significandMask) | implicitBit; + + const infRep = @bitCast(Z, std.math.inf(T)); + + // Detect if a or b is zero, infinity, or NaN. + if (aAbs - Z(1) >= infRep - Z(1) or + bAbs - Z(1) >= infRep - Z(1)) + { + // NaN + anything = qNaN + if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit); + // anything + NaN = qNaN + if (bAbs > infRep) return @bitCast(T, @bitCast(Z, b) | quietBit); + + if (aAbs == infRep) { + // +/-infinity + -/+infinity = qNaN + if ((@bitCast(Z, a) ^ @bitCast(Z, b)) == signBit) { + return @bitCast(T, qnanRep); + } + // +/-infinity + anything remaining = +/- infinity + else { + return a; + } + } + + // anything remaining + +/-infinity = +/-infinity + if (bAbs == infRep) return b; + + // zero + anything = anything + if (aAbs == 0) { + // but we need to get the sign right for zero + zero + if (bAbs == 0) { + return @bitCast(T, @bitCast(Z, a) & @bitCast(Z, b)); + } else { + return b; + } + } + + // anything + zero = anything + if (bAbs == 0) return a; + } + + // Swap a and b if necessary so that a has the larger absolute value. + if (bAbs > aAbs) { + const temp = aRep; + aRep = bRep; + bRep = temp; + } + + // Extract the exponent and significand from the (possibly swapped) a and b. + var aExponent = @intCast(i32, (aRep >> significandBits) & maxExponent); + var bExponent = @intCast(i32, (bRep >> significandBits) & maxExponent); + var aSignificand = aRep & significandMask; + var bSignificand = bRep & significandMask; + + // Normalize any denormals, and adjust the exponent accordingly. + if (aExponent == 0) aExponent = normalize(T, &aSignificand); + if (bExponent == 0) bExponent = normalize(T, &bSignificand); + + // The sign of the result is the sign of the larger operand, a. If they + // have opposite signs, we are performing a subtraction; otherwise addition. + const resultSign = aRep & signBit; + const subtraction = (aRep ^ bRep) & signBit != 0; + + // Shift the significands to give us round, guard and sticky, and or in the + // implicit significand bit. (If we fell through from the denormal path it + // was already set by normalize( ), but setting it twice won't hurt + // anything.) + aSignificand = (aSignificand | implicitBit) << 3; + bSignificand = (bSignificand | implicitBit) << 3; + + // Shift the significand of b by the difference in exponents, with a sticky + // bottom bit to get rounding correct. + const @"align" = @intCast(Z, aExponent - bExponent); + if (@"align" != 0) { + if (@"align" < typeWidth) { + const sticky = if (bSignificand << @intCast(u7, typeWidth - @"align") != 0) Z(1) else 0; + bSignificand = (bSignificand >> @truncate(u7, @"align")) | sticky; + } else { + bSignificand = 1; // sticky; b is known to be non-zero. + } + } + if (subtraction) { + aSignificand -= bSignificand; + // If a == -b, return +zero. + if (aSignificand == 0) return @bitCast(T, Z(0)); + + // If partial cancellation occured, we need to left-shift the result + // and adjust the exponent: + if (aSignificand < implicitBit << 3) { + const shift = @intCast(i32, @clz(aSignificand)) - @intCast(i32, @clz(implicitBit << 3)); + aSignificand <<= @intCast(u7, shift); + aExponent -= shift; + } + } else { // addition + aSignificand += bSignificand; + + // If the addition carried up, we need to right-shift the result and + // adjust the exponent: + if (aSignificand & (implicitBit << 4) != 0) { + const sticky = aSignificand & 1; + aSignificand = aSignificand >> 1 | sticky; + aExponent += 1; + } + } + + // If we have overflowed the type, return +/- infinity: + if (aExponent >= maxExponent) return @bitCast(T, infRep | resultSign); + + if (aExponent <= 0) { + // Result is denormal before rounding; the exponent is zero and we + // need to shift the significand. + const shift = @intCast(Z, 1 - aExponent); + const sticky = if (aSignificand << @intCast(u7, typeWidth - shift) != 0) Z(1) else 0; + aSignificand = aSignificand >> @intCast(u7, shift | sticky); + aExponent = 0; + } + + // Low three bits are round, guard, and sticky. + const roundGuardSticky = aSignificand & 0x7; + + // Shift the significand into place, and mask off the implicit bit. + var result = (aSignificand >> 3) & significandMask; + + // Insert the exponent and sign. + result |= @intCast(Z, aExponent) << significandBits; + result |= resultSign; + + // Final rounding. The result may overflow to infinity, but that is the + // correct result in that case. + if (roundGuardSticky > 0x4) result += 1; + if (roundGuardSticky == 0x4) result += result & 1; + + return @bitCast(T, result); +} + +test "import addXf3" { + _ = @import("addXf3_test.zig"); +} diff --git a/std/special/compiler_rt/addXf3_test.zig b/std/special/compiler_rt/addXf3_test.zig new file mode 100644 index 0000000000..f374a67433 --- /dev/null +++ b/std/special/compiler_rt/addXf3_test.zig @@ -0,0 +1,85 @@ +// Ported from: +// +// https://github.com/llvm-mirror/compiler-rt/blob/92f7768ce940f6437b32ecc0985a1446cd040f7a/test/builtins/Unit/addtf3_test.c +// https://github.com/llvm-mirror/compiler-rt/blob/92f7768ce940f6437b32ecc0985a1446cd040f7a/test/builtins/Unit/subtf3_test.c + +const qnan128 = @bitCast(f128, u128(0x7fff800000000000) << 64); +const inf128 = @bitCast(f128, u128(0x7fff000000000000) << 64); + +const __addtf3 = @import("addXf3.zig").__addtf3; + +fn test__addtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { + const x = __addtf3(a, b); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expected_hi and lo == expected_lo) { + return; + } + // test other possible NaN representation (signal NaN) + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__addtf3 test failure"); +} + +test "addtf3" { + test__addtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); + + // NaN + any = NaN + test__addtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); + + // inf + inf = inf + test__addtf3(inf128, inf128, 0x7fff000000000000, 0x0); + + // inf + any = inf + test__addtf3(inf128, 0x1.2335653452436234723489432abcdefp+5, 0x7fff000000000000, 0x0); + + // any + any + test__addtf3(0x1.23456734245345543849abcdefp+5, 0x1.edcba52449872455634654321fp-1, 0x40042afc95c8b579, 0x61e58dd6c51eb77c); +} + +const __subtf3 = @import("addXf3.zig").__subtf3; + +fn test__subtf3(a: f128, b: f128, expected_hi: u64, expected_lo: u64) void { + const x = __subtf3(a, b); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expected_hi and lo == expected_lo) { + return; + } + // test other possible NaN representation (signal NaN) + else if (expected_hi == 0x7fff800000000000 and expected_lo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__subtf3 test failure"); +} + +test "subtf3" { + // qNaN - any = qNaN + test__subtf3(qnan128, 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); + + // NaN + any = NaN + test__subtf3(@bitCast(f128, (u128(0x7fff000000000000) << 64) | u128(0x800030000000)), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0x0); + + // inf - any = inf + test__subtf3(inf128, 0x1.23456789abcdefp+5, 0x7fff000000000000, 0x0); + + // any + any + test__subtf3(0x1.234567829a3bcdef5678ade36734p+5, 0x1.ee9d7c52354a6936ab8d7654321fp-1, 0x40041b8af1915166, 0xa44a7bca780a166c); +} diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 3df94db589..6715df1805 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -21,6 +21,9 @@ comptime { @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); + @export("__addtf3", @import("addXf3.zig").__addtf3, linkage); + @export("__subtf3", @import("addXf3.zig").__subtf3, linkage); + @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); @@ -37,6 +40,7 @@ comptime { @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); + @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage); @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig index 5cb2f61568..b385090a93 100644 --- a/std/special/compiler_rt/truncXfYf2.zig +++ b/std/special/compiler_rt/truncXfYf2.zig @@ -4,6 +4,10 @@ pub extern fn __truncsfhf2(a: f32) u16 { return @bitCast(u16, truncXfYf2(f16, f32, a)); } +pub extern fn __truncdfhf2(a: f64) u16 { + return @bitCast(u16, truncXfYf2(f16, f64, a)); +} + pub extern fn __trunctfsf2(a: f128) f32 { return truncXfYf2(f32, f128, a); } diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig index c4bf2db733..429372c3f1 100644 --- a/std/special/compiler_rt/truncXfYf2_test.zig +++ b/std/special/compiler_rt/truncXfYf2_test.zig @@ -63,6 +63,74 @@ test "truncsfhf2" { test__truncsfhf2(0x33000000, 0x0000); // 0x1.0p-25 -> zero } +const __truncdfhf2 = @import("truncXfYf2.zig").__truncdfhf2; + +fn test__truncdfhf2(a: f64, expected: u16) void { + const rep = @bitCast(u16, __truncdfhf2(a)); + + if (rep == expected) { + return; + } + // test other possible NaN representation(signal NaN) + else if (expected == 0x7e00) { + if ((rep & 0x7c00) == 0x7c00 and (rep & 0x3ff) > 0) { + return; + } + } + + @panic("__truncdfhf2 test failure"); +} + +fn test__truncdfhf2_raw(a: u64, expected: u16) void { + const actual = __truncdfhf2(@bitCast(f64, a)); + + if (actual == expected) { + return; + } + + @panic("__truncdfhf2 test failure"); +} + +test "truncdfhf2" { + test__truncdfhf2_raw(0x7ff8000000000000, 0x7e00); // qNaN + test__truncdfhf2_raw(0x7ff0000000008000, 0x7e00); // NaN + + test__truncdfhf2_raw(0x7ff0000000000000, 0x7c00); //inf + test__truncdfhf2_raw(0xfff0000000000000, 0xfc00); // -inf + + test__truncdfhf2(0.0, 0x0); // zero + test__truncdfhf2_raw(0x80000000 << 32, 0x8000); // -zero + + test__truncdfhf2(3.1415926535, 0x4248); + test__truncdfhf2(-3.1415926535, 0xc248); + + test__truncdfhf2(0x1.987124876876324p+1000, 0x7c00); + test__truncdfhf2(0x1.987124876876324p+12, 0x6e62); + test__truncdfhf2(0x1.0p+0, 0x3c00); + test__truncdfhf2(0x1.0p-14, 0x0400); + + // denormal + test__truncdfhf2(0x1.0p-20, 0x0010); + test__truncdfhf2(0x1.0p-24, 0x0001); + test__truncdfhf2(-0x1.0p-24, 0x8001); + test__truncdfhf2(0x1.5p-25, 0x0001); + + // and back to zero + test__truncdfhf2(0x1.0p-25, 0x0000); + test__truncdfhf2(-0x1.0p-25, 0x8000); + + // max (precise) + test__truncdfhf2(65504.0, 0x7bff); + + // max (rounded) + test__truncdfhf2(65519.0, 0x7bff); + + // max (to +inf) + test__truncdfhf2(65520.0, 0x7c00); + test__truncdfhf2(-65520.0, 0xfc00); + test__truncdfhf2(65536.0, 0x7c00); +} + const __trunctfsf2 = @import("truncXfYf2.zig").__trunctfsf2; fn test__trunctfsf2(a: f128, expected: u32) void { From cf007e37b95db9faebf34c4c8f9b73aa20eb0672 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Wed, 13 Feb 2019 23:27:23 +1300 Subject: [PATCH 024/134] Add f128 support for fabs, isinf, isnan, inf and nan functions --- std/math/fabs.zig | 19 +++++++++++++++++++ std/math/index.zig | 12 +++++++++++- std/math/inf.zig | 7 ++++--- std/math/isinf.zig | 22 ++++++++++++++++++++++ std/math/isnan.zig | 6 ++++++ std/math/nan.zig | 7 ++++--- 6 files changed, 66 insertions(+), 7 deletions(-) diff --git a/std/math/fabs.zig b/std/math/fabs.zig index 9fad5644c3..a605f4f33f 100644 --- a/std/math/fabs.zig +++ b/std/math/fabs.zig @@ -14,6 +14,7 @@ pub fn fabs(x: var) @typeOf(x) { f16 => fabs16(x), f32 => fabs32(x), f64 => fabs64(x), + f128 => fabs128(x), else => @compileError("fabs not implemented for " ++ @typeName(T)), }; } @@ -36,10 +37,17 @@ fn fabs64(x: f64) f64 { return @bitCast(f64, u); } +fn fabs128(x: f128) f128 { + var u = @bitCast(u128, x); + u &= maxInt(u128) >> 1; + return @bitCast(f128, u); +} + test "math.fabs" { expect(fabs(f16(1.0)) == fabs16(1.0)); expect(fabs(f32(1.0)) == fabs32(1.0)); expect(fabs(f64(1.0)) == fabs64(1.0)); + expect(fabs(f128(1.0)) == fabs128(1.0)); } test "math.fabs16" { @@ -57,6 +65,11 @@ test "math.fabs64" { expect(fabs64(-1.0) == 1.0); } +test "math.fabs128" { + expect(fabs128(1.0) == 1.0); + expect(fabs128(-1.0) == 1.0); +} + test "math.fabs16.special" { expect(math.isPositiveInf(fabs(math.inf(f16)))); expect(math.isPositiveInf(fabs(-math.inf(f16)))); @@ -74,3 +87,9 @@ test "math.fabs64.special" { expect(math.isPositiveInf(fabs(-math.inf(f64)))); expect(math.isNan(fabs(math.nan(f64)))); } + +test "math.fabs128.special" { + expect(math.isPositiveInf(fabs(math.inf(f128)))); + expect(math.isPositiveInf(fabs(-math.inf(f128)))); + expect(math.isNan(fabs(math.nan(f128)))); +} diff --git a/std/math/index.zig b/std/math/index.zig index ccfac03038..20648139b8 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -51,6 +51,12 @@ pub const nan_f64 = @bitCast(f64, nan_u64); pub const inf_u64 = u64(0x7FF << 52); pub const inf_f64 = @bitCast(f64, inf_u64); +pub const nan_u128 = u128(0x7fff0000000000000000000000000001); +pub const nan_f128 = @bitCast(f128, nan_u128); + +pub const inf_u128 = u128(0x7fff0000000000000000000000000000); +pub const inf_f128 = @bitCast(f128, inf_u128); + pub const nan = @import("nan.zig").nan; pub const snan = @import("nan.zig").snan; pub const inf = @import("inf.zig").inf; @@ -379,7 +385,7 @@ pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) t return u0; } const is_signed = from < 0; - const largest_positive_integer = max(if (from<0) (-from)-1 else from, to); // two's complement + const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement const base = log2(largest_positive_integer); const upper = (1 << base) - 1; var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; @@ -752,6 +758,7 @@ test "minInt and maxInt" { testing.expect(maxInt(u16) == 65535); testing.expect(maxInt(u32) == 4294967295); testing.expect(maxInt(u64) == 18446744073709551615); + testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); testing.expect(maxInt(i0) == 0); testing.expect(maxInt(i1) == 0); @@ -760,6 +767,7 @@ test "minInt and maxInt" { testing.expect(maxInt(i32) == 2147483647); testing.expect(maxInt(i63) == 4611686018427387903); testing.expect(maxInt(i64) == 9223372036854775807); + testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); testing.expect(minInt(u0) == 0); testing.expect(minInt(u1) == 0); @@ -768,6 +776,7 @@ test "minInt and maxInt" { testing.expect(minInt(u32) == 0); testing.expect(minInt(u63) == 0); testing.expect(minInt(u64) == 0); + testing.expect(minInt(u128) == 0); testing.expect(minInt(i0) == 0); testing.expect(minInt(i1) == -1); @@ -776,6 +785,7 @@ test "minInt and maxInt" { testing.expect(minInt(i32) == -2147483648); testing.expect(minInt(i63) == -4611686018427387904); testing.expect(minInt(i64) == -9223372036854775808); + testing.expect(minInt(i128) == -170141183460469231731687303715884105728); } test "max value type" { diff --git a/std/math/inf.zig b/std/math/inf.zig index 62f5ef7c0d..fb7a3489c5 100644 --- a/std/math/inf.zig +++ b/std/math/inf.zig @@ -3,9 +3,10 @@ const math = std.math; pub fn inf(comptime T: type) T { return switch (T) { - f16 => @bitCast(f16, math.inf_u16), - f32 => @bitCast(f32, math.inf_u32), - f64 => @bitCast(f64, math.inf_u64), + f16 => math.inf_f16, + f32 => math.inf_f32, + f64 => math.inf_f64, + f128 => math.inf_f128, else => @compileError("inf not implemented for " ++ @typeName(T)), }; } diff --git a/std/math/isinf.zig b/std/math/isinf.zig index e34e9c5971..b1e3f7795e 100644 --- a/std/math/isinf.zig +++ b/std/math/isinf.zig @@ -18,6 +18,10 @@ pub fn isInf(x: var) bool { const bits = @bitCast(u64, x); return bits & (maxInt(u64) >> 1) == (0x7FF << 52); }, + f128 => { + const bits = @bitCast(u128, x); + return bits & (maxInt(u128) >> 1) == (0x7FFF << 112); + }, else => { @compileError("isInf not implemented for " ++ @typeName(T)); }, @@ -36,6 +40,9 @@ pub fn isPositiveInf(x: var) bool { f64 => { return @bitCast(u64, x) == 0x7FF << 52; }, + f128 => { + return @bitCast(u128, x) == 0x7FFF << 112; + }, else => { @compileError("isPositiveInf not implemented for " ++ @typeName(T)); }, @@ -54,6 +61,9 @@ pub fn isNegativeInf(x: var) bool { f64 => { return @bitCast(u64, x) == 0xFFF << 52; }, + f128 => { + return @bitCast(u128, x) == 0xFFFF << 112; + }, else => { @compileError("isNegativeInf not implemented for " ++ @typeName(T)); }, @@ -67,12 +77,16 @@ test "math.isInf" { expect(!isInf(f32(-0.0))); expect(!isInf(f64(0.0))); expect(!isInf(f64(-0.0))); + expect(!isInf(f128(0.0))); + expect(!isInf(f128(-0.0))); expect(isInf(math.inf(f16))); expect(isInf(-math.inf(f16))); expect(isInf(math.inf(f32))); expect(isInf(-math.inf(f32))); expect(isInf(math.inf(f64))); expect(isInf(-math.inf(f64))); + expect(isInf(math.inf(f128))); + expect(isInf(-math.inf(f128))); } test "math.isPositiveInf" { @@ -82,12 +96,16 @@ test "math.isPositiveInf" { expect(!isPositiveInf(f32(-0.0))); expect(!isPositiveInf(f64(0.0))); expect(!isPositiveInf(f64(-0.0))); + expect(!isPositiveInf(f128(0.0))); + expect(!isPositiveInf(f128(-0.0))); expect(isPositiveInf(math.inf(f16))); expect(!isPositiveInf(-math.inf(f16))); expect(isPositiveInf(math.inf(f32))); expect(!isPositiveInf(-math.inf(f32))); expect(isPositiveInf(math.inf(f64))); expect(!isPositiveInf(-math.inf(f64))); + expect(isPositiveInf(math.inf(f128))); + expect(!isPositiveInf(-math.inf(f128))); } test "math.isNegativeInf" { @@ -97,10 +115,14 @@ test "math.isNegativeInf" { expect(!isNegativeInf(f32(-0.0))); expect(!isNegativeInf(f64(0.0))); expect(!isNegativeInf(f64(-0.0))); + expect(!isNegativeInf(f128(0.0))); + expect(!isNegativeInf(f128(-0.0))); expect(!isNegativeInf(math.inf(f16))); expect(isNegativeInf(-math.inf(f16))); expect(!isNegativeInf(math.inf(f32))); expect(isNegativeInf(-math.inf(f32))); expect(!isNegativeInf(math.inf(f64))); expect(isNegativeInf(-math.inf(f64))); + expect(!isNegativeInf(math.inf(f128))); + expect(isNegativeInf(-math.inf(f128))); } diff --git a/std/math/isnan.zig b/std/math/isnan.zig index 641da9e620..e8b03a1e34 100644 --- a/std/math/isnan.zig +++ b/std/math/isnan.zig @@ -18,6 +18,10 @@ pub fn isNan(x: var) bool { const bits = @bitCast(u64, x); return (bits & (maxInt(u64) >> 1)) > (u64(0x7FF) << 52); }, + f128 => { + const bits = @bitCast(u128, x); + return (bits & (maxInt(u128) >> 1)) > (u128(0x7FFF) << 112); + }, else => { @compileError("isNan not implemented for " ++ @typeName(T)); }, @@ -34,7 +38,9 @@ test "math.isNan" { expect(isNan(math.nan(f16))); expect(isNan(math.nan(f32))); expect(isNan(math.nan(f64))); + expect(isNan(math.nan(f128))); expect(!isNan(f16(1.0))); expect(!isNan(f32(1.0))); expect(!isNan(f64(1.0))); + expect(!isNan(f128(1.0))); } diff --git a/std/math/nan.zig b/std/math/nan.zig index 2cbcbee81b..d3ad43da23 100644 --- a/std/math/nan.zig +++ b/std/math/nan.zig @@ -2,9 +2,10 @@ const math = @import("index.zig"); pub fn nan(comptime T: type) T { return switch (T) { - f16 => @bitCast(f16, math.nan_u16), - f32 => @bitCast(f32, math.nan_u32), - f64 => @bitCast(f64, math.nan_u64), + f16 => math.nan_f16, + f32 => math.nan_f32, + f64 => math.nan_f64, + f128 => math.nan_f128, else => @compileError("nan not implemented for " ++ @typeName(T)), }; } From c221b29c9d3c269326b3d901ce5a8b5f50263feb Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 13 Feb 2019 11:31:13 +0100 Subject: [PATCH 025/134] We already support vector on floats, so let's test it --- test/stage1/behavior/vector.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index 7cead12b65..5e3e446109 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -15,3 +15,17 @@ test "vector wrap operators" { S.doTheTest(); comptime S.doTheTest(); } + +test "vector float operators" { + const S = struct { + fn doTheTest() void { + const v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; + const x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 }; + expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 })); + expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 })); + expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 })); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From 53297a1bd09d4a4499117c1868d8448f007b12d9 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 13 Feb 2019 11:40:32 +0100 Subject: [PATCH 026/134] We already support vector bit operators, so let's test it --- test/stage1/behavior/vector.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index 5e3e446109..b0d2871454 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -29,3 +29,17 @@ test "vector float operators" { S.doTheTest(); comptime S.doTheTest(); } + +test "vector bit operators" { + const S = struct { + fn doTheTest() void { + const v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; + const x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 }; + expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 })); + expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 })); + expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 })); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From c34ce6878e5834a123be862d45bd26fd9a843eef Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 14 Feb 2019 00:06:32 +1300 Subject: [PATCH 027/134] Add parseFloat to std.fmt This is not intended to be the long-term implementation as it doesn't provide various properties that we eventually will want (e.g. round-tripping, denormal support). It also uses f64 internally so the wider f128 will be inaccurate. --- CMakeLists.txt | 1 + std/fmt/index.zig | 8 +- std/fmt/parse_float.zig | 425 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 std/fmt/parse_float.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 41fa44374e..8e80c65dbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -482,6 +482,7 @@ set(ZIG_STD_FILES "fmt/errol/index.zig" "fmt/errol/lookup.zig" "fmt/index.zig" + "fmt/parse_float.zig" "hash/adler.zig" "hash/crc.zig" "hash/fnv.zig" diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 05b028112f..efb1c41f92 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -828,7 +828,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned return x; } -test "parseUnsigned" { +test "fmt.parseUnsigned" { testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); @@ -855,6 +855,12 @@ test "parseUnsigned" { testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); } +pub const parseFloat = @import("parse_float.zig").parseFloat; + +test "fmt.parseFloat" { + _ = @import("parse_float.zig"); +} + pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { const value = switch (c) { '0'...'9' => c - '0', diff --git a/std/fmt/parse_float.zig b/std/fmt/parse_float.zig new file mode 100644 index 0000000000..b83cdbbbb2 --- /dev/null +++ b/std/fmt/parse_float.zig @@ -0,0 +1,425 @@ +// Adapted from https://github.com/grzegorz-kraszewski/stringtofloat. + +// MIT License +// +// Copyright (c) 2016 Grzegorz Kraszewski +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Be aware that this implementation has the following limitations: +// +// - Is not round-trip accurate for all values +// - Only supports round-to-zero +// - Does not handle denormals + +const std = @import("../index.zig"); + +const max_digits = 25; + +const f64_plus_zero: u64 = 0x0000000000000000; +const f64_minus_zero: u64 = 0x8000000000000000; +const f64_plus_infinity: u64 = 0x7FF0000000000000; +const f64_minus_infinity: u64 = 0xFFF0000000000000; + +const Z96 = struct { + d0: u32, + d1: u32, + d2: u32, + + // d = s >> 1 + inline fn shiftRight1(d: *Z96, s: Z96) void { + d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31); + d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31); + d.d2 = s.d2 >> 1; + } + + // d = s << 1 + inline fn shiftLeft1(d: *Z96, s: Z96) void { + d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31); + d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31); + d.d0 = s.d0 << 1; + } + + // d += s + inline fn add(d: *Z96, s: Z96) void { + var w = u64(d.d0) + u64(s.d0); + d.d0 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d1) + u64(s.d1); + d.d1 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d2) + u64(s.d2); + d.d2 = @truncate(u32, w); + } + + // d -= s + inline fn sub(d: *Z96, s: Z96) void { + var w = u64(d.d0) -% u64(s.d0); + d.d0 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d1) -% u64(s.d1); + d.d1 = @truncate(u32, w); + + w >>= 32; + w += u64(d.d2) -% u64(s.d2); + d.d2 = @truncate(u32, w); + } + + fn dump(d: Z96) void { + std.debug.warn("{} {} {}\n", d.d0, d.d1, d.d2); + } +}; + +const FloatRepr = struct { + negative: bool, + exponent: i32, + mantissa: u64, +}; + +fn convertRepr(comptime T: type, n: FloatRepr) T { + const mask28: u32 = 0xf << 28; + + var s: Z96 = undefined; + var q: Z96 = undefined; + var r: Z96 = undefined; + + s.d0 = @truncate(u32, n.mantissa); + s.d1 = @truncate(u32, n.mantissa >> 32); + s.d2 = 0; + + var binary_exponent: u64 = 92; + var exp = n.exponent; + + while (exp > 0) : (exp -= 1) { + q.shiftLeft1(s); // q = p << 1 + r.shiftLeft1(q); // r = p << 2 + s.shiftLeft1(r); // p = p << 3 + q.add(s); // p = (p << 3) + (p << 1) + + exp -= 1; + + while (s.d2 & mask28 != 0) { + q.shiftRight1(s); + binary_exponent += 1; + s = q; + } + } + + while (exp < 0) { + while (s.d2 & (1 << 31) == 0) { + q.shiftLeft1(s); + binary_exponent -= 1; + s = q; + } + + q.d2 = s.d2 / 10; + r.d1 = s.d2 % 10; + r.d2 = (s.d1 >> 8) | (r.d1 << 24); + q.d1 = r.d2 / 10; + r.d1 = r.d2 % 10; + r.d2 = ((s.d1 & 0xff) << 16) | (s.d0 >> 16) | (r.d1 << 24); + r.d0 = r.d2 / 10; + r.d1 = r.d2 % 10; + q.d1 = (q.d1 << 8) | ((r.d0 & 0x00ff0000) >> 16); + q.d0 = r.d0 << 16; + r.d2 = (s.d0 *% 0xffff) | (r.d1 << 16); + q.d0 |= r.d2 / 10; + s = q; + + exp += 1; + } + + if (s.d0 != 0 or s.d1 != 0 or s.d2 != 0) { + while (s.d2 & mask28 == 0) { + q.shiftLeft1(s); + binary_exponent -= 1; + s = q; + } + } + + binary_exponent += 1023; + + const repr: u64 = blk: { + if (binary_exponent > 2046) { + break :blk if (n.negative) f64_minus_infinity else f64_plus_infinity; + } else if (binary_exponent < 1) { + break :blk if (n.negative) f64_minus_zero else f64_plus_zero; + } else if (s.d2 != 0) { + const binexs2 = u64(binary_exponent) << 52; + const rr = (u64(s.d2 & ~mask28) << 24) | ((u64(s.d1) + 128) >> 8) | binexs2; + break :blk if (n.negative) rr | (1 << 63) else rr; + } else { + break :blk 0; + } + }; + + const f = @bitCast(f64, repr); + return @floatCast(T, f); +} + +const State = enum { + SkipLeadingWhitespace, + MaybeSign, + LeadingMantissaZeros, + LeadingFractionalZeros, + MantissaIntegral, + MantissaFractional, + ExponentSign, + LeadingExponentZeros, + Exponent, + Stop, +}; + +const ParseResult = enum { + Ok, + PlusZero, + MinusZero, + PlusInf, + MinusInf, +}; + +inline fn isDigit(c: u8) bool { + return c >= '0' and c <= '9'; +} + +inline fn isSpace(c: u8) bool { + return (c >= 0x09 and c <= 0x13) or c == 0x20; +} + +fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { + var digit_index: usize = 0; + var negative = false; + var negative_exp = false; + var exponent: i32 = 0; + + var state = State.SkipLeadingWhitespace; + + var i: usize = 0; + loop: while (state != State.Stop and i < s.len) { + const c = s[i]; + + switch (state) { + State.SkipLeadingWhitespace => { + if (isSpace(c)) { + i += 1; + } else { + state = State.MaybeSign; + } + }, + + State.MaybeSign => { + state = State.LeadingMantissaZeros; + + if (c == '+') { + i += 1; + } else if (c == '-') { + n.negative = true; + i += 1; + } else if (isDigit(c) or c == '.') { + // continue + } else { + state = State.Stop; + } + }, + + State.LeadingMantissaZeros => { + if (c == '0') { + i += 1; + } else if (c == '.') { + i += 1; + state = State.LeadingFractionalZeros; + } else { + state = State.MantissaIntegral; + } + }, + + State.LeadingFractionalZeros => { + if (c == '0') { + i += 1; + if (n.exponent > std.math.minInt(i32)) { + n.exponent -= 1; + } + } else { + state = State.MantissaFractional; + } + }, + + State.MantissaIntegral => { + if (isDigit(c)) { + if (digit_index < max_digits) { + n.mantissa *%= 10; + n.mantissa += s[i] - '0'; + digit_index += 1; + } else if (n.exponent < std.math.maxInt(i32)) { + n.exponent += 1; + } + + i += 1; + } else if (c == '.') { + i += 1; + state = State.MantissaFractional; + } else { + state = State.MantissaFractional; + } + }, + + State.MantissaFractional => { + if (isDigit(c)) { + if (digit_index < max_digits) { + n.mantissa *%= 10; + n.mantissa += c - '0'; + n.exponent -%= 1; + digit_index += 1; + } + + i += 1; + } else if (c == 'e' or c == 'E') { + i += 1; + state = State.ExponentSign; + } else { + state = State.ExponentSign; + } + }, + + State.ExponentSign => { + if (c == '+') { + i += 1; + } else if (c == '-') { + negative_exp = true; + i += 1; + } + + state = State.LeadingExponentZeros; + }, + + State.LeadingExponentZeros => { + if (c == '0') { + i += 1; + } else { + state = State.Exponent; + } + }, + + State.Exponent => { + if (isDigit(c)) { + if (exponent < std.math.maxInt(i32)) { + exponent *= 10; + exponent += @intCast(i32, c - '0'); + } + + i += 1; + } else { + state = State.Stop; + } + }, + + State.Stop => break :loop, + } + } + + if (negative_exp) exponent = -exponent; + n.exponent += exponent; + + if (n.mantissa == 0) { + return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero; + } else if (n.exponent > 309) { + return if (n.negative) ParseResult.MinusInf else ParseResult.PlusInf; + } else if (n.exponent < -328) { + return if (n.negative) ParseResult.MinusZero else ParseResult.PlusZero; + } + + return ParseResult.Ok; +} + +inline fn isLower(c: u8) bool { + return c -% 'a' < 26; +} + +inline fn toUpper(c: u8) u8 { + return if (isLower(c)) (c & 0x5f) else c; +} + +fn caseInEql(a: []const u8, b: []const u8) bool { + if (a.len != b.len) return false; + + for (a) |_, i| { + if (toUpper(a[i]) != toUpper(b[i])) { + return false; + } + } + + return true; +} + +pub fn parseFloat(comptime T: type, s: []const u8) T { + var r = FloatRepr{ + .negative = false, + .exponent = 0, + .mantissa = 0, + }; + + if (caseInEql(s, "nan")) { + return std.math.nan(T); + } else if (caseInEql(s, "inf") or caseInEql(s, "+inf")) { + return std.math.inf(T); + } else if (caseInEql(s, "-inf")) { + return -std.math.inf(T); + } + + return switch (parseRepr(s, &r)) { + ParseResult.Ok => convertRepr(T, r), + ParseResult.PlusZero => 0.0, + ParseResult.MinusZero => -T(0.0), + ParseResult.PlusInf => std.math.inf(T), + ParseResult.MinusInf => -std.math.inf(T), + }; +} + +test "fmt.parseFloat" { + const assert = std.debug.assert; + const approxEq = std.math.approxEq; + const epsilon = 1e-7; + + inline for ([]type{ f32, f64, f128 }) |T| { + const Z = @IntType(false, T.bit_count); + + assert(parseFloat(T, "0") == 0.0); + assert(parseFloat(T, "+0") == 0.0); + assert(parseFloat(T, "-0") == 0.0); + + assert(approxEq(T, parseFloat(T, "3.141"), 3.141, epsilon)); + assert(approxEq(T, parseFloat(T, "-3.141"), -3.141, epsilon)); + + assert(parseFloat(T, "1e-700") == 0); + assert(parseFloat(T, "1e+700") == std.math.inf(T)); + + assert(@bitCast(Z, parseFloat(T, "nAn")) == @bitCast(Z, std.math.nan(T))); + assert(parseFloat(T, "inF") == std.math.inf(T)); + assert(parseFloat(T, "-INF") == -std.math.inf(T)); + + if (T != f16) { + assert(approxEq(T, parseFloat(T, "123142.1"), 123142.1, epsilon)); + assert(approxEq(T, parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); + } + } +} From de7c55145aeb6687040254dedaef66eedf5328dd Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 14 Feb 2019 00:12:00 +1300 Subject: [PATCH 028/134] Add parseFloat support to json.zig --- std/json.zig | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/std/json.zig b/std/json.zig index d8f28560e5..5a28d9ab41 100644 --- a/std/json.zig +++ b/std/json.zig @@ -1345,7 +1345,7 @@ pub const Parser = struct { return if (token.number_is_integer) Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else - @panic("TODO: fmt.parseFloat not yet implemented"); + Value{ .Float = std.fmt.parseFloat(f64, token.slice(input, i)) }; } }; @@ -1366,7 +1366,8 @@ test "json.parser.dynamic" { \\ }, \\ "Animated" : false, \\ "IDs": [116, 943, 234, 38793], - \\ "ArrayOfObject": [{"n": "m"}] + \\ "ArrayOfObject": [{"n": "m"}], + \\ "double": 1.3412 \\ } \\} ; @@ -1395,4 +1396,7 @@ test "json.parser.dynamic" { const obj0 = array_of_object.Array.at(0).Object.get("n").?.value; testing.expect(mem.eql(u8, obj0.String, "m")); + + const double = image.Object.get("double").?.value; + testing.expect(double.Float == 1.3412); } From 2911eb34ded4709429a9767e08675ae862907a5f Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 13 Feb 2019 12:19:08 +0100 Subject: [PATCH 029/134] Added error for nesting vectors --- test/compile_errors.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b47cdf2ed1..b895e2c2d1 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -5430,4 +5430,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:7:30: error: unable to evaluate constant expression", ); + + cases.addTest( + "nested vectors", + \\export fn entry() void { + \\ const V = @Vector(4, @Vector(4, u8)); + \\ var v: V = undefined; + \\} + , + ".tmp_source.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", + ); } From d4d2718bca9e23ceec029bb505c0ea1b91c875b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 00:40:39 -0500 Subject: [PATCH 030/134] 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 031/134] 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 a4e32d9fb143dbfb622a5097ea4a1e4bcad03463 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 09:46:08 -0500 Subject: [PATCH 032/134] ci: freebsd: remove '.git' from URL as per upstream suggestion https://todo.sr.ht/~sircmpwn/dispatch.sr.ht/24#comment-1465 --- .builds/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 934b8a4f4a..5e94a83a3a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -4,7 +4,7 @@ packages: - ninja - llvm70 sources: - - https://github.com/ziglang/zig.git + - https://github.com/ziglang/zig tasks: - build: | cd zig && mkdir build && cd build From c58b80203443dcbf8b737ebdaa1f17fb20c77711 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 10:51:59 -0500 Subject: [PATCH 033/134] remove the "top of the comptime stack" compile error It's still best practice to put `@setEvalBranchQuota` at the top of the comptime stack, but as Jimmi notes in #1949, when a function can be called at comptime and also can be the top of the comptime stack, this compile error is fundamentally unsound. So now it's gone. closes #1949 --- src/ir.cpp | 6 ------ test/compile_errors.zig | 14 -------------- 2 files changed, 20 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 5d4013b4b9..03dea10b10 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18172,12 +18172,6 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira, IrInstructionSetEvalBranchQuota *instruction) { - if (ira->new_irb.exec->parent_exec != nullptr && !ira->new_irb.exec->is_generic_instantiation) { - ir_add_error(ira, &instruction->base, - buf_sprintf("@setEvalBranchQuota must be called from the top of the comptime stack")); - return ira->codegen->invalid_instruction; - } - uint64_t new_quota; if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota)) return ira->codegen->invalid_instruction; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b895e2c2d1..6a2ded1844 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4554,20 +4554,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal", ); - cases.add( - "@setEvalBranchQuota in non-root comptime execution context", - \\comptime { - \\ foo(); - \\} - \\fn foo() void { - \\ @setEvalBranchQuota(1001); - \\} - , - ".tmp_source.zig:5:5: error: @setEvalBranchQuota must be called from the top of the comptime stack", - ".tmp_source.zig:2:8: note: called from here", - ".tmp_source.zig:1:10: note: called from here", - ); - cases.add( "wrong pointer implicitly casted to pointer to @OpaqueType()", \\const Derp = @OpaqueType(); From e03c770145b5dc7b428d53b3cac97c2733fb84d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 12:28:50 -0500 Subject: [PATCH 034/134] 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 035/134] 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 036/134] 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 037/134] 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 038/134] 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 039/134] 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 040/134] 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 041/134] 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 042/134] 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 18ad50970f81bd4b07892a6651487be81effc4c7 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Fri, 15 Feb 2019 17:32:13 +1300 Subject: [PATCH 043/134] Make parseFloat stricter in what it accepts as input --- std/fmt/parse_float.zig | 79 +++++++++++++++++++---------------------- std/json.zig | 2 +- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/std/fmt/parse_float.zig b/std/fmt/parse_float.zig index b83cdbbbb2..de9619efe2 100644 --- a/std/fmt/parse_float.zig +++ b/std/fmt/parse_float.zig @@ -84,10 +84,6 @@ const Z96 = struct { w += u64(d.d2) -% u64(s.d2); d.d2 = @truncate(u32, w); } - - fn dump(d: Z96) void { - std.debug.warn("{} {} {}\n", d.d0, d.d1, d.d2); - } }; const FloatRepr = struct { @@ -178,7 +174,6 @@ fn convertRepr(comptime T: type, n: FloatRepr) T { } const State = enum { - SkipLeadingWhitespace, MaybeSign, LeadingMantissaZeros, LeadingFractionalZeros, @@ -187,7 +182,6 @@ const State = enum { ExponentSign, LeadingExponentZeros, Exponent, - Stop, }; const ParseResult = enum { @@ -206,27 +200,19 @@ inline fn isSpace(c: u8) bool { return (c >= 0x09 and c <= 0x13) or c == 0x20; } -fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { +fn parseRepr(s: []const u8, n: *FloatRepr) !ParseResult { var digit_index: usize = 0; var negative = false; var negative_exp = false; var exponent: i32 = 0; - var state = State.SkipLeadingWhitespace; + var state = State.MaybeSign; var i: usize = 0; - loop: while (state != State.Stop and i < s.len) { + loop: while (i < s.len) { const c = s[i]; switch (state) { - State.SkipLeadingWhitespace => { - if (isSpace(c)) { - i += 1; - } else { - state = State.MaybeSign; - } - }, - State.MaybeSign => { state = State.LeadingMantissaZeros; @@ -238,7 +224,7 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { } else if (isDigit(c) or c == '.') { // continue } else { - state = State.Stop; + return error.InvalidCharacter; } }, @@ -329,11 +315,9 @@ fn parseRepr(s: []const u8, n: *FloatRepr) ParseResult { i += 1; } else { - state = State.Stop; + return error.InvalidCharacter; } }, - - State.Stop => break :loop, } } @@ -371,12 +355,10 @@ fn caseInEql(a: []const u8, b: []const u8) bool { return true; } -pub fn parseFloat(comptime T: type, s: []const u8) T { - var r = FloatRepr{ - .negative = false, - .exponent = 0, - .mantissa = 0, - }; +pub fn parseFloat(comptime T: type, s: []const u8) !T { + if (s.len == 0) { + return error.InvalidCharacter; + } if (caseInEql(s, "nan")) { return std.math.nan(T); @@ -386,7 +368,13 @@ pub fn parseFloat(comptime T: type, s: []const u8) T { return -std.math.inf(T); } - return switch (parseRepr(s, &r)) { + var r = FloatRepr{ + .negative = false, + .exponent = 0, + .mantissa = 0, + }; + + return switch (try parseRepr(s, &r)) { ParseResult.Ok => convertRepr(T, r), ParseResult.PlusZero => 0.0, ParseResult.MinusZero => -T(0.0), @@ -396,30 +384,37 @@ pub fn parseFloat(comptime T: type, s: []const u8) T { } test "fmt.parseFloat" { - const assert = std.debug.assert; + const testing = std.testing; + const expect = testing.expect; + const expectEqual = testing.expectEqual; const approxEq = std.math.approxEq; const epsilon = 1e-7; - inline for ([]type{ f32, f64, f128 }) |T| { + inline for ([]type{ f16, f32, f64, f128 }) |T| { const Z = @IntType(false, T.bit_count); - assert(parseFloat(T, "0") == 0.0); - assert(parseFloat(T, "+0") == 0.0); - assert(parseFloat(T, "-0") == 0.0); + testing.expectError(error.InvalidCharacter, parseFloat(T, "")); + testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); + testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); - assert(approxEq(T, parseFloat(T, "3.141"), 3.141, epsilon)); - assert(approxEq(T, parseFloat(T, "-3.141"), -3.141, epsilon)); + expectEqual(try parseFloat(T, "0"), 0.0); + expectEqual((try parseFloat(T, "0")), 0.0); + expectEqual((try parseFloat(T, "+0")), 0.0); + expectEqual((try parseFloat(T, "-0")), 0.0); - assert(parseFloat(T, "1e-700") == 0); - assert(parseFloat(T, "1e+700") == std.math.inf(T)); + expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon)); + expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); - assert(@bitCast(Z, parseFloat(T, "nAn")) == @bitCast(Z, std.math.nan(T))); - assert(parseFloat(T, "inF") == std.math.inf(T)); - assert(parseFloat(T, "-INF") == -std.math.inf(T)); + expectEqual((try parseFloat(T, "1e-700")), 0); + expectEqual((try parseFloat(T, "1e+700")), std.math.inf(T)); + + expectEqual(@bitCast(Z, try parseFloat(T, "nAn")), @bitCast(Z, std.math.nan(T))); + expectEqual((try parseFloat(T, "inF")), std.math.inf(T)); + expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T)); if (T != f16) { - assert(approxEq(T, parseFloat(T, "123142.1"), 123142.1, epsilon)); - assert(approxEq(T, parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); + expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); + expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); } } } diff --git a/std/json.zig b/std/json.zig index 5a28d9ab41..dd053e7635 100644 --- a/std/json.zig +++ b/std/json.zig @@ -1345,7 +1345,7 @@ pub const Parser = struct { return if (token.number_is_integer) Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } else - Value{ .Float = std.fmt.parseFloat(f64, token.slice(input, i)) }; + Value{ .Float = try std.fmt.parseFloat(f64, token.slice(input, i)) }; } }; From 170ec504ec3201a89cb8121ea59e5d845f5cd1d1 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Fri, 15 Feb 2019 17:37:55 +1300 Subject: [PATCH 044/134] Use official llvm mirror for compiler-rt commit ref --- std/special/compiler_rt/addXf3.zig | 2 +- std/special/compiler_rt/addXf3_test.zig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/std/special/compiler_rt/addXf3.zig b/std/special/compiler_rt/addXf3.zig index 5f7f73c44f..09413b2328 100644 --- a/std/special/compiler_rt/addXf3.zig +++ b/std/special/compiler_rt/addXf3.zig @@ -1,6 +1,6 @@ // Ported from: // -// https://github.com/llvm-mirror/compiler-rt/blob/92f7768ce940f6437b32ecc0985a1446cd040f7a/lib/builtins/fp_add_impl.inc +// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/lib/builtins/fp_add_impl.inc const std = @import("std"); const builtin = @import("builtin"); diff --git a/std/special/compiler_rt/addXf3_test.zig b/std/special/compiler_rt/addXf3_test.zig index f374a67433..099b737976 100644 --- a/std/special/compiler_rt/addXf3_test.zig +++ b/std/special/compiler_rt/addXf3_test.zig @@ -1,7 +1,7 @@ // Ported from: // -// https://github.com/llvm-mirror/compiler-rt/blob/92f7768ce940f6437b32ecc0985a1446cd040f7a/test/builtins/Unit/addtf3_test.c -// https://github.com/llvm-mirror/compiler-rt/blob/92f7768ce940f6437b32ecc0985a1446cd040f7a/test/builtins/Unit/subtf3_test.c +// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/addtf3_test.c +// https://github.com/llvm/llvm-project/blob/02d85149a05cb1f6dc49f0ba7a2ceca53718ae17/compiler-rt/test/builtins/Unit/subtf3_test.c const qnan128 = @bitCast(f128, u128(0x7fff800000000000) << 64); const inf128 = @bitCast(f128, u128(0x7fff000000000000) << 64); From 71d7100aa830652490d3995bf4e1319f31b5e647 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 14 Feb 2019 23:38:14 -0500 Subject: [PATCH 045/134] 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 046/134] 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; } From ee5e196f8832359cfe05808677f143d4f460f6bc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 15 Feb 2019 02:02:19 -0500 Subject: [PATCH 047/134] add test for truncate on comptime integers closes #703 --- test/stage1/behavior/truncate.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/stage1/behavior/truncate.zig b/test/stage1/behavior/truncate.zig index 568346369f..099b6c3359 100644 --- a/test/stage1/behavior/truncate.zig +++ b/test/stage1/behavior/truncate.zig @@ -29,3 +29,8 @@ test "truncate sign mismatch but comptime known so it works anyway" { var result = @truncate(i8, x); expect(result == 10); } + +test "truncate on comptime integer" { + var x = @truncate(u16, 9999); + expect(x == 9999); +} From 7293e012d7956b892380517e914108ffadc6941b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 15 Feb 2019 18:05:50 -0500 Subject: [PATCH 048/134] breaking: fix @sizeOf to be alloc size rather than store size * Fixes breaches of the guarantee that `@sizeOf(T) >= @alignOf(T)` * Fixes std.mem.secureZero for integers where this guarantee previously was breached * Fixes std.mem.Allocator for integers where this guarantee previously was breached Closes #1851 Closes #1864 --- doc/langref.html.in | 9 ++++-- src/analyze.cpp | 37 +++++++++++++++++++--- src/analyze.hpp | 1 + src/ir.cpp | 28 ++++++++++++----- std/io.zig | 13 +++----- std/mem.zig | 49 ++++++++++++------------------ test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/1851.zig | 27 ++++++++++++++++ 8 files changed, 113 insertions(+), 52 deletions(-) create mode 100644 test/stage1/behavior/bugs/1851.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index e5a60b0bc1..1341bf1be5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6299,10 +6299,15 @@ pub const FloatMode = enum {

{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}

This function returns the number of bytes it takes to store {#syntax#}T{#endsyntax#} in memory. -

-

The result is a target-specific compile time constant.

+

+ This size may contain padding bytes. If there were two consecutive T in memory, this would be the offset + in bytes between element at index 0 and the element at index 1. For {#link|integer|Integers#}, + consider whether you want to use {#syntax#}@sizeOf(T){#endsyntax#} or + {#syntax#}@typeInfo(T).Int.bits{#endsyntax#}. +

+ {#see_also|@typeInfo#} {#header_close#} {#header_open|@sliceToBytes#} diff --git a/src/analyze.cpp b/src/analyze.cpp index e2a96da7c3..7949493586 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -356,6 +356,28 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) { } } + return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); +} + +uint64_t type_size_store(CodeGen *g, ZigType *type_entry) { + assert(type_is_complete(type_entry)); + + if (!type_has_bits(type_entry)) + return 0; + + if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { + uint64_t size_in_bits = type_size_bits(g, type_entry); + return (size_in_bits + 7) / 8; + } else if (type_entry->id == ZigTypeIdArray) { + ZigType *child_type = type_entry->data.array.child_type; + if (child_type->id == ZigTypeIdStruct && + child_type->data.structure.layout == ContainerLayoutPacked) + { + uint64_t size_in_bits = type_size_bits(g, type_entry); + return (size_in_bits + 7) / 8; + } + } + return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); } @@ -6230,14 +6252,19 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case ZigTypeIdStruct: { if (is_slice(type_entry)) { - ConstPtrValue *ptr = &const_val->data.x_struct.fields[slice_ptr_index].data.x_ptr; - assert(ptr->special == ConstPtrSpecialBaseArray); - ConstExprValue *array = ptr->data.base_array.array_val; - size_t start = ptr->data.base_array.elem_index; - ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index]; size_t len = bigint_as_unsigned(&len_val->data.x_bigint); + ConstExprValue *ptr_val = &const_val->data.x_struct.fields[slice_ptr_index]; + if (ptr_val->special == ConstValSpecialUndef) { + assert(len == 0); + buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name)); + return; + } + assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); + ConstExprValue *array = ptr_val->data.x_ptr.data.base_array.array_val; + size_t start = ptr_val->data.x_ptr.data.base_array.elem_index; + render_const_val_array(g, buf, &type_entry->name, array, start, len); } else { buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name)); diff --git a/src/analyze.hpp b/src/analyze.hpp index 845fb62534..7ded651e95 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -19,6 +19,7 @@ ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, ZigType *type_entry); +uint64_t type_size_store(CodeGen *g, ZigType *type_entry); uint64_t type_size_bits(CodeGen *g, ZigType *type_entry); ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type); diff --git a/src/ir.cpp b/src/ir.cpp index c9262038e0..92cdd8c891 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14331,15 +14331,15 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; - size_t src_size = type_size(codegen, pointee->type); - size_t dst_size = type_size(codegen, out_val->type); - - if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { - copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); - return ErrorNone; - } + // We don't need to read the padding bytes, so we look at type_size_store bytes + size_t src_size = type_size_store(codegen, pointee->type); + size_t dst_size = type_size_store(codegen, out_val->type); if (dst_size <= src_size) { + if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { + copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); + return ErrorNone; + } Buf buf = BUF_INIT; buf_resize(&buf, src_size); buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); @@ -15798,6 +15798,8 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, IrInstructionToPtrType *to_ptr_type_instruction) { + Error err; + IrInstruction *value = to_ptr_type_instruction->value->child; ZigType *type_entry = value->value.type; if (type_is_invalid(type_entry)) @@ -15813,7 +15815,17 @@ static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.pointer.child_type->data.array.child_type, type_entry->data.pointer.is_const); } else if (is_slice(type_entry)) { - ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle); + ZigType *slice_ptr_type = type_entry->data.structure.fields[0].type_entry; + ptr_type = adjust_ptr_len(ira->codegen, slice_ptr_type, PtrLenSingle); + // If the pointer is over-aligned, we may have to reduce it based on the alignment of the element type. + if (slice_ptr_type->data.pointer.explicit_alignment != 0) { + ZigType *elem_type = slice_ptr_type->data.pointer.child_type; + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + uint32_t elem_align = get_abi_alignment(ira->codegen, elem_type); + uint32_t reduced_align = min(elem_align, slice_ptr_type->data.pointer.explicit_alignment); + ptr_type = adjust_ptr_align(ira->codegen, ptr_type, reduced_align); + } } else if (type_entry->id == ZigTypeIdArgTuple) { ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad); if (!arg_tuple_val) diff --git a/std/io.zig b/std/io.zig index d7e8507f9b..6c70834b52 100644 --- a/std/io.zig +++ b/std/io.zig @@ -935,8 +935,6 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { }; } - - pub const BufferedAtomicFile = struct { atomic_file: os.AtomicFile, file_stream: os.File.OutStream, @@ -978,7 +976,6 @@ pub const BufferedAtomicFile = struct { } }; - pub fn readLine(buf: *std.Buffer) ![]u8 { var stdin = try getStdIn(); var stdin_stream = stdin.inStream(); @@ -1073,13 +1070,13 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E else => in_stream, } }; } - + pub fn alignToByte(self: *Self) void { - if(!is_packed) return; + if (!is_packed) return; self.in_stream.alignToByte(); } - //@BUG: inferred error issue. See: #1386 + //@BUG: inferred error issue. See: #1386 fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T { comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); @@ -1088,7 +1085,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E const U = @IntType(false, t_bit_count); const Log2U = math.Log2Int(U); - const int_size = @sizeOf(U); + const int_size = (U.bit_count + 7) / 8; if (is_packed) { const result = try self.in_stream.readBitsNoEof(U, t_bit_count); @@ -1301,7 +1298,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com const U = @IntType(false, t_bit_count); const Log2U = math.Log2Int(U); - const int_size = @sizeOf(U); + const int_size = (U.bit_count + 7) / 8; const u_value = @bitCast(U, value); diff --git a/std/mem.zig b/std/mem.zig index 1c7523bf13..39b9701754 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -423,8 +423,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin. /// This function cannot fail and cannot cause undefined behavior. /// Assumes the endianness of memory is native. This means the function can /// simply pointer cast memory. -pub fn readIntNative(comptime T: type, bytes: *const [@sizeOf(T)]u8) T { - comptime assert(T.bit_count % 8 == 0); +pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T { return @ptrCast(*align(1) const T, bytes).*; } @@ -432,7 +431,7 @@ pub fn readIntNative(comptime T: type, bytes: *const [@sizeOf(T)]u8) T { /// The bit count of T must be evenly divisible by 8. /// This function cannot fail and cannot cause undefined behavior. /// Assumes the endianness of memory is foreign, so it must byte-swap. -pub fn readIntForeign(comptime T: type, bytes: *const [@sizeOf(T)]u8) T { +pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T { return @bswap(T, readIntNative(T, bytes)); } @@ -446,22 +445,20 @@ pub const readIntBig = switch (builtin.endian) { builtin.Endian.Big => readIntNative, }; -/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0 +/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 /// and ignores extra bytes. -/// Note that @sizeOf(u24) is 3. /// The bit count of T must be evenly divisible by 8. /// Assumes the endianness of memory is native. This means the function can /// simply pointer cast memory. pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T { - assert(@sizeOf(u24) == 3); - assert(bytes.len >= @sizeOf(T)); + const n = @divExact(T.bit_count, 8); + assert(bytes.len >= n); // TODO https://github.com/ziglang/zig/issues/863 - return readIntNative(T, @ptrCast(*const [@sizeOf(T)]u8, bytes.ptr)); + return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr)); } -/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0 +/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 /// and ignores extra bytes. -/// Note that @sizeOf(u24) is 3. /// The bit count of T must be evenly divisible by 8. /// Assumes the endianness of memory is foreign, so it must byte-swap. pub fn readIntSliceForeign(comptime T: type, bytes: []const u8) T { @@ -481,7 +478,7 @@ pub const readIntSliceBig = switch (builtin.endian) { /// Reads an integer from memory with bit count specified by T. /// The bit count of T must be evenly divisible by 8. /// This function cannot fail and cannot cause undefined behavior. -pub fn readInt(comptime T: type, bytes: *const [@sizeOf(T)]u8, endian: builtin.Endian) T { +pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, endian: builtin.Endian) T { if (endian == builtin.endian) { return readIntNative(T, bytes); } else { @@ -489,15 +486,14 @@ pub fn readInt(comptime T: type, bytes: *const [@sizeOf(T)]u8, endian: builtin.E } } -/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0 +/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 /// and ignores extra bytes. -/// Note that @sizeOf(u24) is 3. /// The bit count of T must be evenly divisible by 8. pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T { - assert(@sizeOf(u24) == 3); - assert(bytes.len >= @sizeOf(T)); + const n = @divExact(T.bit_count, 8); + assert(bytes.len >= n); // TODO https://github.com/ziglang/zig/issues/863 - return readInt(T, @ptrCast(*const [@sizeOf(T)]u8, bytes.ptr), endian); + return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian); } test "comptime read/write int" { @@ -540,7 +536,7 @@ test "readIntBig and readIntLittle" { /// accepts any integer bit width. /// This function stores in native endian, which means it is implemented as a simple /// memory store. -pub fn writeIntNative(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void { +pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value: T) void { @ptrCast(*align(1) T, buf).* = value; } @@ -548,7 +544,7 @@ pub fn writeIntNative(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void { /// This function always succeeds, has defined behavior for all inputs, but /// the integer bit width must be divisible by 8. /// This function stores in foreign endian, which means it does a @bswap first. -pub fn writeIntForeign(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void { +pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, value: T) void { writeIntNative(T, buf, @bswap(T, value)); } @@ -565,8 +561,7 @@ pub const writeIntBig = switch (builtin.endian) { /// Writes an integer to memory, storing it in twos-complement. /// This function always succeeds, has defined behavior for all inputs, but /// the integer bit width must be divisible by 8. -pub fn writeInt(comptime T: type, buffer: *[@sizeOf(T)]u8, value: T, endian: builtin.Endian) void { - comptime assert(T.bit_count % 8 == 0); +pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: T, endian: builtin.Endian) void { if (endian == builtin.endian) { return writeIntNative(T, buffer, value); } else { @@ -575,15 +570,13 @@ pub fn writeInt(comptime T: type, buffer: *[@sizeOf(T)]u8, value: T, endian: bui } /// Writes a twos-complement little-endian integer to memory. -/// Asserts that buf.len >= @sizeOf(T). Note that @sizeOf(u24) is 3. +/// Asserts that buf.len >= T.bit_count / 8. /// The bit count of T must be divisible by 8. /// Any extra bytes in buffer after writing the integer are set to zero. To /// avoid the branch to check for extra buffer bytes, use writeIntLittle /// instead. pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { - comptime assert(@sizeOf(u24) == 3); - comptime assert(T.bit_count % 8 == 0); - assert(buffer.len >= @sizeOf(T)); + assert(buffer.len >= @divExact(T.bit_count, 8)); // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough const uint = @IntType(false, T.bit_count); @@ -595,14 +588,12 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void { } /// Writes a twos-complement big-endian integer to memory. -/// Asserts that buffer.len >= @sizeOf(T). Note that @sizeOf(u24) is 3. +/// Asserts that buffer.len >= T.bit_count / 8. /// The bit count of T must be divisible by 8. /// Any extra bytes in buffer before writing the integer are set to zero. To /// avoid the branch to check for extra buffer bytes, use writeIntBig instead. pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void { - comptime assert(@sizeOf(u24) == 3); - comptime assert(T.bit_count % 8 == 0); - assert(buffer.len >= @sizeOf(T)); + assert(buffer.len >= @divExact(T.bit_count, 8)); // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough const uint = @IntType(false, T.bit_count); @@ -626,7 +617,7 @@ pub const writeIntSliceForeign = switch (builtin.endian) { }; /// Writes a twos-complement integer to memory, with the specified endianness. -/// Asserts that buf.len >= @sizeOf(T). Note that @sizeOf(u24) is 3. +/// Asserts that buf.len >= T.bit_count / 8. /// The bit count of T must be evenly divisible by 8. /// Any extra bytes in buffer not part of the integer are set to zero, with /// respect to endianness. To avoid the branch to check for extra buffer bytes, diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 1fa00b34fd..df311637fa 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -17,6 +17,7 @@ comptime { _ = @import("behavior/bugs/1421.zig"); _ = @import("behavior/bugs/1442.zig"); _ = @import("behavior/bugs/1486.zig"); + _ = @import("behavior/bugs/1851.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); diff --git a/test/stage1/behavior/bugs/1851.zig b/test/stage1/behavior/bugs/1851.zig new file mode 100644 index 0000000000..ff9ab419f8 --- /dev/null +++ b/test/stage1/behavior/bugs/1851.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "allocation and looping over 3-byte integer" { + expect(@sizeOf(u24) == 4); + expect(@sizeOf([1]u24) == 4); + expect(@alignOf(u24) == 4); + expect(@alignOf([1]u24) == 4); + var buffer: [100]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buffer).allocator; + + var x = a.alloc(u24, 2) catch unreachable; + expect(x.len == 2); + x[0] = 0xFFFFFF; + x[1] = 0xFFFFFF; + + const bytes = @sliceToBytes(x); + expect(@typeOf(bytes) == []align(4) u8); + expect(bytes.len == 8); + + for (bytes) |*b| { + b.* = 0x00; + } + + expect(x[0] == 0x00); + expect(x[1] == 0x00); +} From a05e224150a5a4bcad5ab1b399b43db8a0e28104 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 15 Feb 2019 19:19:28 -0500 Subject: [PATCH 049/134] typecheck the panic function this adds the prototype of panic to @import("builtin") and then uses it to do an implicit cast of the panic function to this prototype, rather than redoing all the implicit cast logic. closes #1894 closes #1895 --- src/all_types.hpp | 1 + src/analyze.cpp | 61 +++++++++++++++-------------------------- src/analyze.hpp | 1 + src/codegen.cpp | 4 +++ src/ir.cpp | 15 ++++++---- src/ir.hpp | 2 +- test/compile_errors.zig | 14 ++++++++-- 7 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index bafe316c3d..6fbd987b9e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1758,6 +1758,7 @@ struct CodeGen { ZigFn *cur_fn; ZigFn *main_fn; ZigFn *panic_fn; + TldFn *panic_tld_fn; AstNode *root_export_decl; CacheHash cache_hash; diff --git a/src/analyze.cpp b/src/analyze.cpp index 7949493586..12e245bd72 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1351,7 +1351,7 @@ static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *no size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, - nullptr, nullptr, node, type_name, nullptr); + nullptr, nullptr, node, type_name, nullptr, nullptr); } ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { @@ -3247,36 +3247,19 @@ static bool scope_is_root_decls(Scope *scope) { zig_unreachable(); } -static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, ZigType *fn_type) { - add_node_error(g, proto_node, - buf_sprintf("expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found '%s'", - buf_ptr(&fn_type->name))); -} +void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) { + ConstExprValue *panic_fn_type_val = get_builtin_value(g, "PanicFn"); + assert(panic_fn_type_val != nullptr); + assert(panic_fn_type_val->type->id == ZigTypeIdMetaType); + ZigType *panic_fn_type = panic_fn_type_val->data.x_type; -static void typecheck_panic_fn(CodeGen *g, ZigFn *panic_fn) { - AstNode *proto_node = panic_fn->proto_node; - assert(proto_node->type == NodeTypeFnProto); - ZigType *fn_type = panic_fn->type_entry; - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - if (fn_type_id->param_count != 2) { - return wrong_panic_prototype(g, proto_node, fn_type); - } - ZigType *const_u8_ptr = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, 0, 0, 0); - ZigType *const_u8_slice = get_slice_type(g, const_u8_ptr); - if (fn_type_id->param_info[0].type != const_u8_slice) { - return wrong_panic_prototype(g, proto_node, fn_type); - } + AstNode *fake_decl = allocate(1); + *fake_decl = *panic_fn->proto_node; + fake_decl->type = NodeTypeSymbol; + fake_decl->data.symbol_expr.symbol = &panic_fn->symbol_name; - ZigType *optional_ptr_to_stack_trace_type = get_optional_type(g, get_ptr_to_stack_trace_type(g)); - if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) { - return wrong_panic_prototype(g, proto_node, fn_type); - } - - ZigType *actual_return_type = fn_type_id->return_type; - if (actual_return_type != g->builtin_types.entry_unreachable) { - return wrong_panic_prototype(g, proto_node, fn_type); - } + // call this for the side effects of casting to panic_fn_type + analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr); } ZigType *get_test_fn_type(CodeGen *g) { @@ -3371,18 +3354,18 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { if (!fn_table_entry->type_entry->data.fn.is_generic) { if (fn_def_node) g->fn_defs.append(fn_table_entry); + } - if (scope_is_root_decls(tld_fn->base.parent_scope) && - (import == g->root_import || import->package == g->panic_package)) + if (scope_is_root_decls(tld_fn->base.parent_scope) && + (import == g->root_import || import->package == g->panic_package)) + { + if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { + g->main_fn = fn_table_entry; + } else if ((import->package == g->panic_package || g->have_pub_panic) && + buf_eql_str(&fn_table_entry->symbol_name, "panic")) { - if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { - g->main_fn = fn_table_entry; - } else if ((import->package == g->panic_package || g->have_pub_panic) && - buf_eql_str(&fn_table_entry->symbol_name, "panic")) - { - g->panic_fn = fn_table_entry; - typecheck_panic_fn(g, fn_table_entry); - } + g->panic_fn = fn_table_entry; + g->panic_tld_fn = tld_fn; } } } else if (source_node->type == NodeTypeTestDecl) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 7ded651e95..956ef47309 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -239,4 +239,5 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry); Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ConstExprValue *const_val, ZigType *wanted_type); +void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index d2662b10d2..d2b2836b0c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7144,6 +7144,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " instruction_addresses: []usize,\n" "};\n\n"); + buf_append_str(contents, "pub const PanicFn = fn([]const u8, ?*StackTrace) noreturn;\n\n"); + const char *cur_os = nullptr; { buf_appendf(contents, "pub const Os = enum {\n"); @@ -7913,6 +7915,8 @@ static void gen_root_source(CodeGen *g) { } } + typecheck_panic_fn(g, g->panic_tld_fn, g->panic_fn); + report_errors_and_maybe_exit(g); } diff --git a/src/ir.cpp b/src/ir.cpp index 92cdd8c891..0fcbb60fe8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9949,7 +9949,7 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, - IrExecutable *parent_exec) + IrExecutable *parent_exec, AstNode *expected_type_source_node) { if (expected_type != nullptr && type_is_invalid(expected_type)) return &codegen->invalid_instruction->value; @@ -9985,7 +9985,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod analyzed_executable->backward_branch_count = backward_branch_count; analyzed_executable->backward_branch_quota = backward_branch_quota; analyzed_executable->begin_scope = scope; - ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); + ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node); if (type_is_invalid(result_type)) return &codegen->invalid_instruction->value; @@ -10863,10 +10863,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa } break; } + case ConstCastResultIdFnIsGeneric: + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("only one of the functions is generic")); + break; case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnCC: // TODO case ConstCastResultIdFnVarArgs: // TODO - case ConstCastResultIdFnIsGeneric: // TODO case ConstCastResultIdFnReturnType: // TODO case ConstCastResultIdFnArgCount: // TODO case ConstCastResultIdFnGenericArgCount: // TODO @@ -13856,7 +13859,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call AstNode *body_node = fn_entry->body_node; result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, - nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec); + nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node); if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; @@ -14052,7 +14055,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, - nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); + nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr); IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); const_instruction->base.value = *align_result; @@ -18464,7 +18467,7 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct ZigType *void_type = ira->codegen->builtin_types.entry_void; ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, - &cimport_scope->buf, block_node, nullptr, nullptr); + &cimport_scope->buf, block_node, nullptr, nullptr, nullptr); if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; diff --git a/src/ir.hpp b/src/ir.hpp index 0a7c614812..0b85ad2c55 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -16,7 +16,7 @@ bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, - IrExecutable *parent_exec); + IrExecutable *parent_exec, AstNode *expected_type_source_node); ZigType *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable, ZigType *expected_type, AstNode *expected_type_source_node); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e7108cb36a..9ef4af4162 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -346,13 +346,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "Panic declared with wrong type signature in tests", + "wrong panic signature, runtime function", \\test "" {} \\ \\pub fn panic() void {} \\ , - ".tmp_source.zig:3:5: error: expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'", + ".tmp_source.zig:3:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn() void'", + ); + + cases.add( + "wrong panic signature, generic function", + \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { + \\ while (true) {} + \\} + , + ".tmp_source.zig:1:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn([]const u8,var)var'", + ".tmp_source.zig:1:5: note: only one of the functions is generic", ); cases.add( From 5736a9c6a9b357ab346dd8fcbe64f5d729d6d244 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 12 Feb 2019 10:21:45 -0600 Subject: [PATCH 050/134] removed hidden union tag in release modes --- src/analyze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 12e245bd72..9941104bc4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2886,7 +2886,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum || enum_type_node != nullptr; bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); - bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); + bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease); ZigType *tag_type; bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety); bool *covered_enum_fields; From ba56f365c813440b79c1710c6a8b0fd591883e13 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 00:42:56 -0500 Subject: [PATCH 051/134] bring zig fmt to stage1 --- CMakeLists.txt | 5 + src-self-hosted/main.zig | 6 +- src/main.cpp | 36 ++++- std/special/fmt_runner.zig | 265 +++++++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 7 deletions(-) create mode 100644 std/special/fmt_runner.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e80c65dbd..db5f50908c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -652,6 +652,7 @@ set(ZIG_STD_FILES "special/compiler_rt/udivmodti4.zig" "special/compiler_rt/udivti3.zig" "special/compiler_rt/umodti3.zig" + "special/fmt_runner.zig" "special/init-exe/build.zig" "special/init-exe/src/main.zig" "special/init-lib/build.zig" @@ -905,3 +906,7 @@ foreach(file ${ZIG_STD_FILES}) get_filename_component(file_dir "${ZIG_STD_DEST}/${file}" DIRECTORY) install(FILES "${CMAKE_SOURCE_DIR}/std/${file}" DESTINATION "${file_dir}") endforeach() + +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/arg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/main.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") +install(FILES "${CMAKE_SOURCE_DIR}/src-self-hosted/errmsg.zig" DESTINATION "${ZIG_STD_DEST}/special/fmt/") diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 64aa729469..42556beaed 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -24,7 +24,7 @@ var stderr_file: os.File = undefined; var stderr: *io.OutStream(os.File.WriteError) = undefined; var stdout: *io.OutStream(os.File.WriteError) = undefined; -const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB +pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB const usage = \\usage: zig [command] [options] @@ -510,7 +510,7 @@ fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void { return buildOutputType(allocator, args, Compilation.Kind.Obj); } -const usage_fmt = +pub const usage_fmt = \\usage: zig fmt [file]... \\ \\ Formats the input files and modifies them in-place. @@ -527,7 +527,7 @@ const usage_fmt = \\ ; -const args_fmt_spec = []Flag{ +pub const args_fmt_spec = []Flag{ Flag.Bool("--help"), Flag.Bool("--check"), Flag.Option("--color", []const []const u8{ diff --git a/src/main.cpp b/src/main.cpp index 73a1d75d42..78446f9e98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,8 +21,8 @@ static int print_error_usage(const char *arg0) { return EXIT_FAILURE; } -static int print_full_usage(const char *arg0) { - fprintf(stdout, +static int print_full_usage(const char *arg0, FILE *file, int return_code) { + fprintf(file, "Usage: %s [command] [options]\n" "\n" "Commands:\n" @@ -31,6 +31,7 @@ static int print_full_usage(const char *arg0) { " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" " builtin show the source code of that @import(\"builtin\")\n" + " fmt parse files and render in canonical zig format\n" " help show this usage information\n" " id print the base64-encoded compiler id\n" " init-exe initialize a `zig build` application in the cwd\n" @@ -106,7 +107,7 @@ static int print_full_usage(const char *arg0) { " --test-cmd [arg] specify test execution command one arg at a time\n" " --test-cmd-bin appends test binary path to test cmd args\n" , arg0); - return EXIT_SUCCESS; + return return_code; } static const char *ZIG_ZEN = "\n" @@ -515,6 +516,31 @@ int main(int argc, char **argv) { fprintf(stderr, "\n"); } return (term.how == TerminationIdClean) ? term.code : -1; + } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { + init_all_targets(); + Buf *fmt_runner_path = buf_alloc(); + os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); + CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + nullptr); + g->is_single_threaded = true; + codegen_set_out_name(g, buf_create_from_str("fmt")); + g->enable_cache = true; + + codegen_build_and_link(g); + + ZigList args = {0}; + for (int i = 2; i < argc; i += 1) { + args.append(argv[i]); + } + args.append(nullptr); + const char *exec_path = buf_ptr(&g->output_file_path); + + os_execv(exec_path, args.items); + + args.pop(); + Termination term; + os_spawn_process(exec_path, args, &term); + return term.code; } for (int i = 1; i < argc; i += 1) { @@ -527,6 +553,8 @@ int main(int argc, char **argv) { build_mode = BuildModeSafeRelease; } else if (strcmp(arg, "--release-small") == 0) { build_mode = BuildModeSmallRelease; + } else if (strcmp(arg, "--help") == 0) { + return print_full_usage(arg0, stderr, EXIT_FAILURE); } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { @@ -1080,7 +1108,7 @@ int main(int argc, char **argv) { } } case CmdHelp: - return print_full_usage(arg0); + return print_full_usage(arg0, stdout, EXIT_SUCCESS); case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); return EXIT_SUCCESS; diff --git a/std/special/fmt_runner.zig b/std/special/fmt_runner.zig new file mode 100644 index 0000000000..b6b728f5cf --- /dev/null +++ b/std/special/fmt_runner.zig @@ -0,0 +1,265 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const os = std.os; +const io = std.io; +const mem = std.mem; +const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const Buffer = std.Buffer; +const ast = std.zig.ast; + +const arg = @import("fmt/arg.zig"); +const self_hosted_main = @import("fmt/main.zig"); +const Args = arg.Args; +const Flag = arg.Flag; +const errmsg = @import("fmt/errmsg.zig"); + +var stderr_file: os.File = undefined; +var stderr: *io.OutStream(os.File.WriteError) = undefined; +var stdout: *io.OutStream(os.File.WriteError) = undefined; + +// This brings `zig fmt` to stage 1. +pub fn main() !void { + // Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` is a short-lived, + // one shot program. We don't need to waste time freeing memory and finding places to squish + // bytes into. So we free everything all at once at the very end. + var direct_allocator = std.heap.DirectAllocator.init(); + var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator); + const allocator = &arena.allocator; + + var stdout_file = try std.io.getStdOut(); + var stdout_out_stream = stdout_file.outStream(); + stdout = &stdout_out_stream.stream; + + stderr_file = try std.io.getStdErr(); + var stderr_out_stream = stderr_file.outStream(); + stderr = &stderr_out_stream.stream; + const args = try std.os.argsAlloc(allocator); + + var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args); + defer flags.deinit(); + + if (flags.present("help")) { + try stdout.write(self_hosted_main.usage_fmt); + os.exit(0); + } + + const color = blk: { + if (flags.single("color")) |color_flag| { + if (mem.eql(u8, color_flag, "auto")) { + break :blk errmsg.Color.Auto; + } else if (mem.eql(u8, color_flag, "on")) { + break :blk errmsg.Color.On; + } else if (mem.eql(u8, color_flag, "off")) { + break :blk errmsg.Color.Off; + } else unreachable; + } else { + break :blk errmsg.Color.Auto; + } + }; + + if (flags.present("stdin")) { + if (flags.positionals.len != 0) { + try stderr.write("cannot use --stdin with positional arguments\n"); + os.exit(1); + } + + var stdin_file = try io.getStdIn(); + var stdin = stdin_file.inStream(); + + const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size); + defer allocator.free(source_code); + + var tree = std.zig.parse(allocator, source_code) catch |err| { + try stderr.print("error parsing stdin: {}\n", err); + os.exit(1); + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(allocator, parse_error, &tree, "", stderr_file, color); + } + if (tree.errors.len != 0) { + os.exit(1); + } + if (flags.present("check")) { + const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree); + const code = if (anything_changed) u8(1) else u8(0); + os.exit(code); + } + + _ = try std.zig.render(allocator, stdout, &tree); + return; + } + + if (flags.positionals.len == 0) { + try stderr.write("expected at least one source file argument\n"); + os.exit(1); + } + + if (flags.positionals.len == 0) { + try stderr.write("expected at least one source file argument\n"); + os.exit(1); + } + + var fmt = Fmt{ + .seen = Fmt.SeenMap.init(allocator), + .any_error = false, + .color = color, + .allocator = allocator, + }; + + const check_mode = flags.present("check"); + + for (flags.positionals.toSliceConst()) |file_path| { + try fmtPath(&fmt, file_path, check_mode); + } + if (fmt.any_error) { + os.exit(1); + } +} + +const FmtError = error{ + SystemResources, + OperationAborted, + IoPending, + BrokenPipe, + Unexpected, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + OutOfMemory, + RenameAcrossMountPoints, + ReadOnlyFileSystem, + LinkQuotaExceeded, + FileBusy, +} || os.File.OpenError; + +fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { + const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); + defer fmt.allocator.free(file_path); + + if (try fmt.seen.put(file_path, {})) |_| return; + + const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => { + // TODO make event based (and dir.next()) + var dir = try std.os.Dir.open(fmt.allocator, file_path); + defer dir.close(); + + while (try dir.next()) |entry| { + if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try os.path.join(fmt.allocator, [][]const u8{ file_path, entry.name }); + try fmtPath(fmt, full_path, check_mode); + } + } + return; + }, + else => { + // TODO lock stderr printing + try stderr.print("unable to open '{}': {}\n", file_path, err); + fmt.any_error = true; + return; + }, + }; + defer fmt.allocator.free(source_code); + + var tree = std.zig.parse(fmt.allocator, source_code) catch |err| { + try stderr.print("error parsing file '{}': {}\n", file_path, err); + fmt.any_error = true; + return; + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color); + } + if (tree.errors.len != 0) { + fmt.any_error = true; + return; + } + + if (check_mode) { + const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, &tree); + if (anything_changed) { + try stderr.print("{}\n", file_path); + fmt.any_error = true; + } + } else { + // TODO make this evented + const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); + defer baf.destroy(); + + const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree); + if (anything_changed) { + try stderr.print("{}\n", file_path); + try baf.finish(); + } + } +} + +const Fmt = struct { + seen: SeenMap, + any_error: bool, + color: errmsg.Color, + allocator: *mem.Allocator, + + const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); +}; + +fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree, + path: []const u8, file: os.File, color: errmsg.Color,) !void +{ + const color_on = switch (color) { + errmsg.Color.Auto => file.isTty(), + errmsg.Color.On => true, + errmsg.Color.Off => false, + }; + const lok_token = parse_error.loc(); + const span = errmsg.Span{ + .first = lok_token, + .last = lok_token, + }; + + const first_token = tree.tokens.at(span.first); + const last_token = tree.tokens.at(span.last); + const start_loc = tree.tokenLocationPtr(0, first_token); + const end_loc = tree.tokenLocationPtr(first_token.end, last_token); + + var text_buf = try std.Buffer.initSize(allocator, 0); + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + const text = text_buf.toOwnedSlice(); + + const stream = &file.outStream().stream; + if (!color_on) { + try stream.print( + "{}:{}:{}: error: {}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + text, + ); + return; + } + + try stream.print( + "{}:{}:{}: error: {}\n{}\n", + path, + start_loc.line + 1, + start_loc.column + 1, + text, + tree.source[start_loc.line_start..start_loc.line_end], + ); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.write("\n"); +} From a97362e67739c94d6266cc2d1352dd9bb1b713be Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 12:24:02 -0500 Subject: [PATCH 052/134] fmt_runner: remove redundant check --- std/special/fmt_runner.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/std/special/fmt_runner.zig b/std/special/fmt_runner.zig index b6b728f5cf..46ced0e136 100644 --- a/std/special/fmt_runner.zig +++ b/std/special/fmt_runner.zig @@ -99,11 +99,6 @@ pub fn main() !void { os.exit(1); } - if (flags.positionals.len == 0) { - try stderr.write("expected at least one source file argument\n"); - os.exit(1); - } - var fmt = Fmt{ .seen = Fmt.SeenMap.init(allocator), .any_error = false, From f4c5bcfea5f02dec30d7adede0550c95af424c71 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 13:34:32 -0500 Subject: [PATCH 053/134] refactor translate-c - no more using namespace clang this is in preparation for #1964 --- src/translate_c.cpp | 2276 +++++++++++++++++++++---------------------- 1 file changed, 1137 insertions(+), 1139 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 710314d1cb..7d618466b2 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -30,8 +30,6 @@ #include -using namespace clang; - struct Alias { Buf *new_name; Buf *canon_name; @@ -87,13 +85,13 @@ struct Context { HashMap decl_table; HashMap macro_table; HashMap global_table; - SourceManager *source_manager; + clang::SourceManager *source_manager; ZigList aliases; AstNode *source_node; bool warnings_on; CodeGen *codegen; - ASTContext *ctx; + clang::ASTContext *ctx; TransScopeRoot *global_scope; HashMap ptr_params; @@ -117,21 +115,21 @@ static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *paren static TransScopeBlock *trans_scope_block_find(TransScope *scope); -static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl); -static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl); -static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl); +static AstNode *resolve_record_decl(Context *c, const clang::RecordDecl *record_decl); +static AstNode *resolve_enum_decl(Context *c, const clang::EnumDecl *enum_decl); +static AstNode *resolve_typedef_decl(Context *c, const clang::TypedefNameDecl *typedef_decl); -static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, +static int trans_stmt_extra(Context *c, TransScope *scope, const clang::Stmt *stmt, ResultUsed result_used, TransLRValue lrval, AstNode **out_node, TransScope **out_child_scope, TransScope **out_node_scope); -static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node); -static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); -static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc); -static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); +static TransScope *trans_stmt(Context *c, TransScope *scope, const clang::Stmt *stmt, AstNode **out_node); +static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval); +static AstNode *trans_qual_type(Context *c, clang::QualType qt, const clang::SourceLocation &source_loc); +static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval); ATTRIBUTE_PRINTF(3, 4) -static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) { +static void emit_warning(Context *c, const clang::SourceLocation &sl, const char *format, ...) { if (!c->warnings_on) { return; } @@ -471,8 +469,8 @@ static Buf *string_ref_to_buf(StringRef string_ref) { return buf_create_from_mem((const char *)string_ref.bytes_begin(), string_ref.size()); } -static const char *decl_name(const Decl *decl) { - const NamedDecl *named_decl = static_cast(decl); +static const char *decl_name(const clang::Decl *decl) { + const clang::NamedDecl *named_decl = static_cast(decl); return (const char *)named_decl->getName().bytes_begin(); } @@ -490,20 +488,20 @@ static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) } -static const Type *qual_type_canon(QualType qt) { +static const clang::Type *qual_type_canon(clang::QualType qt) { return qt.getCanonicalType().getTypePtr(); } -static QualType get_expr_qual_type(Context *c, const Expr *expr) { +static clang::QualType get_expr_qual_type(Context *c, const clang::Expr *expr) { // String literals in C are `char *` but they should really be `const char *`. - if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *cast_expr = static_cast(expr); - if (cast_expr->getCastKind() == CK_ArrayToPointerDecay) { - const Expr *sub_expr = cast_expr->getSubExpr(); - if (sub_expr->getStmtClass() == Stmt::StringLiteralClass) { - QualType array_qt = sub_expr->getType(); - const ArrayType *array_type = static_cast(array_qt.getTypePtr()); - QualType pointee_qt = array_type->getElementType(); + if (expr->getStmtClass() == clang::Stmt::ImplicitCastExprClass) { + const clang::ImplicitCastExpr *cast_expr = static_cast(expr); + if (cast_expr->getCastKind() == clang::CK_ArrayToPointerDecay) { + const clang::Expr *sub_expr = cast_expr->getSubExpr(); + if (sub_expr->getStmtClass() == clang::Stmt::StringLiteralClass) { + clang::QualType array_qt = sub_expr->getType(); + const clang::ArrayType *array_type = static_cast(array_qt.getTypePtr()); + clang::QualType pointee_qt = array_type->getElementType(); pointee_qt.addConst(); return c->ctx->getPointerType(pointee_qt); } @@ -512,19 +510,19 @@ static QualType get_expr_qual_type(Context *c, const Expr *expr) { return expr->getType(); } -static QualType get_expr_qual_type_before_implicit_cast(Context *c, const Expr *expr) { - if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *cast_expr = static_cast(expr); +static clang::QualType get_expr_qual_type_before_implicit_cast(Context *c, const clang::Expr *expr) { + if (expr->getStmtClass() == clang::Stmt::ImplicitCastExprClass) { + const clang::ImplicitCastExpr *cast_expr = static_cast(expr); return get_expr_qual_type(c, cast_expr->getSubExpr()); } return expr->getType(); } -static AstNode *get_expr_type(Context *c, const Expr *expr) { +static AstNode *get_expr_type(Context *c, const clang::Expr *expr) { return trans_qual_type(c, get_expr_qual_type(c, expr), expr->getLocStart()); } -static bool qual_types_equal(QualType t1, QualType t2) { +static bool qual_types_equal(clang::QualType t1, clang::QualType t2) { if (t1.isConstQualified() != t2.isConstQualified()) { return false; } @@ -541,37 +539,37 @@ static bool is_c_void_type(AstNode *node) { return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void")); } -static bool expr_types_equal(Context *c, const Expr *expr1, const Expr *expr2) { - QualType t1 = get_expr_qual_type(c, expr1); - QualType t2 = get_expr_qual_type(c, expr2); +static bool expr_types_equal(Context *c, const clang::Expr *expr1, const clang::Expr *expr2) { + clang::QualType t1 = get_expr_qual_type(c, expr1); + clang::QualType t2 = get_expr_qual_type(c, expr2); return qual_types_equal(t1, t2); } -static bool qual_type_is_ptr(QualType qt) { - const Type *ty = qual_type_canon(qt); - return ty->getTypeClass() == Type::Pointer; +static bool qual_type_is_ptr(clang::QualType qt) { + const clang::Type *ty = qual_type_canon(qt); + return ty->getTypeClass() == clang::Type::Pointer; } -static const FunctionProtoType *qual_type_get_fn_proto(QualType qt, bool *is_ptr) { - const Type *ty = qual_type_canon(qt); +static const clang::FunctionProtoType *qual_type_get_fn_proto(clang::QualType qt, bool *is_ptr) { + const clang::Type *ty = qual_type_canon(qt); *is_ptr = false; - if (ty->getTypeClass() == Type::Pointer) { + if (ty->getTypeClass() == clang::Type::Pointer) { *is_ptr = true; - const PointerType *pointer_ty = static_cast(ty); - QualType child_qt = pointer_ty->getPointeeType(); + const clang::PointerType *pointer_ty = static_cast(ty); + clang::QualType child_qt = pointer_ty->getPointeeType(); ty = child_qt.getTypePtr(); } - if (ty->getTypeClass() == Type::FunctionProto) { - return static_cast(ty); + if (ty->getTypeClass() == clang::Type::FunctionProto) { + return static_cast(ty); } return nullptr; } -static bool qual_type_is_fn_ptr(QualType qt) { +static bool qual_type_is_fn_ptr(clang::QualType qt) { bool is_ptr; if (qual_type_get_fn_proto(qt, &is_ptr)) { return is_ptr; @@ -580,30 +578,30 @@ static bool qual_type_is_fn_ptr(QualType qt) { return false; } -static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { - const Type *ty = qt.getTypePtr(); +static uint32_t qual_type_int_bit_width(Context *c, const clang::QualType &qt, const clang::SourceLocation &source_loc) { + const clang::Type *ty = qt.getTypePtr(); switch (ty->getTypeClass()) { - case Type::Builtin: + case clang::Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); + const clang::BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: return 8; - case BuiltinType::UInt128: - case BuiltinType::Int128: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Int128: return 128; default: return 0; } zig_unreachable(); } - case Type::Typedef: + case clang::Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); const char *type_name = decl_name(typedef_decl); if (strcmp(type_name, "uint8_t") == 0 || strcmp(type_name, "int8_t") == 0) { return 8; @@ -624,8 +622,8 @@ static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const So } -static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt, - const SourceLocation &source_loc) +static AstNode *qual_type_to_log2_int_ref(Context *c, const clang::QualType &qt, + const clang::SourceLocation &source_loc) { uint32_t int_bit_width = qual_type_int_bit_width(c, qt, source_loc); if (int_bit_width != 0) { @@ -643,7 +641,7 @@ static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt, // FieldAccess // FnCall (.builtin = true) // Symbol "import" -// StringLiteral "std" +// clang::StringLiteral "std" // Symbol "math" // Symbol "Log2Int" // zig_type_node @@ -657,21 +655,21 @@ static AstNode *qual_type_to_log2_int_ref(Context *c, const QualType &qt, return log2int_fn_call; } -static bool qual_type_child_is_fn_proto(const QualType &qt) { - if (qt.getTypePtr()->getTypeClass() == Type::Paren) { - const ParenType *paren_type = static_cast(qt.getTypePtr()); - if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) { +static bool qual_type_child_is_fn_proto(const clang::QualType &qt) { + if (qt.getTypePtr()->getTypeClass() == clang::Type::Paren) { + const clang::ParenType *paren_type = static_cast(qt.getTypePtr()); + if (paren_type->getInnerType()->getTypeClass() == clang::Type::FunctionProto) { return true; } - } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) { - const AttributedType *attr_type = static_cast(qt.getTypePtr()); + } else if (qt.getTypePtr()->getTypeClass() == clang::Type::Attributed) { + const clang::AttributedType *attr_type = static_cast(qt.getTypePtr()); return qual_type_child_is_fn_proto(attr_type->getEquivalentType()); } return false; } -static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, QualType dest_type, - QualType src_type, AstNode *expr) +static AstNode* trans_c_cast(Context *c, const clang::SourceLocation &source_location, clang::QualType dest_type, + clang::QualType src_type, AstNode *expr) { if (qual_types_equal(dest_type, src_type)) { return expr; @@ -688,72 +686,72 @@ static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, return trans_create_node_fn_call_1(c, trans_qual_type(c, dest_type, source_location), expr); } -static bool c_is_signed_integer(Context *c, QualType qt) { - const Type *c_type = qual_type_canon(qt); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_signed_integer(Context *c, clang::QualType qt) { + const clang::Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); switch (builtin_ty->getKind()) { - case BuiltinType::SChar: - case BuiltinType::Short: - case BuiltinType::Int: - case BuiltinType::Long: - case BuiltinType::LongLong: - case BuiltinType::Int128: - case BuiltinType::WChar_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: + case clang::BuiltinType::WChar_S: return true; default: return false; } } -static bool c_is_unsigned_integer(Context *c, QualType qt) { - const Type *c_type = qual_type_canon(qt); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_unsigned_integer(Context *c, clang::QualType qt) { + const clang::Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); switch (builtin_ty->getKind()) { - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::UShort: - case BuiltinType::UInt: - case BuiltinType::ULong: - case BuiltinType::ULongLong: - case BuiltinType::UInt128: - case BuiltinType::WChar_U: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::WChar_U: return true; default: return false; } } -static bool c_is_builtin_type(Context *c, QualType qt, BuiltinType::Kind kind) { - const Type *c_type = qual_type_canon(qt); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_builtin_type(Context *c, clang::QualType qt, clang::BuiltinType::Kind kind) { + const clang::Type *c_type = qual_type_canon(qt); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); return builtin_ty->getKind() == kind; } -static bool c_is_float(Context *c, QualType qt) { - const Type *c_type = qt.getTypePtr(); - if (c_type->getTypeClass() != Type::Builtin) +static bool c_is_float(Context *c, clang::QualType qt) { + const clang::Type *c_type = qt.getTypePtr(); + if (c_type->getTypeClass() != clang::Type::Builtin) return false; - const BuiltinType *builtin_ty = static_cast(c_type); + const clang::BuiltinType *builtin_ty = static_cast(c_type); switch (builtin_ty->getKind()) { - case BuiltinType::Half: - case BuiltinType::Float: - case BuiltinType::Double: - case BuiltinType::Float128: - case BuiltinType::LongDouble: + case clang::BuiltinType::Half: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::Float128: + case clang::BuiltinType::LongDouble: return true; default: return false; } } -static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) { +static bool qual_type_has_wrapping_overflow(Context *c, clang::QualType qt) { if (c_is_signed_integer(c, qt) || c_is_float(c, qt)) { // float and signed integer overflow is undefined behavior. return false; @@ -763,23 +761,23 @@ 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) { +static bool type_is_opaque(Context *c, const clang::Type *ty, const clang::SourceLocation &source_loc) { switch (ty->getTypeClass()) { - case Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); - return builtin_ty->getKind() == BuiltinType::Void; + case clang::Type::Builtin: { + const clang::BuiltinType *builtin_ty = static_cast(ty); + return builtin_ty->getKind() == clang::BuiltinType::Void; } - case Type::Record: { - const RecordType *record_ty = static_cast(ty); + case clang::Type::Record: { + const clang::RecordType *record_ty = static_cast(ty); return record_ty->getDecl()->getDefinition() == nullptr; } - case Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); + case clang::Type::Elaborated: { + const clang::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(); + case clang::Type::Typedef: { + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc); } default: @@ -787,145 +785,145 @@ static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &sou } } -static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { +static AstNode *trans_type(Context *c, const clang::Type *ty, const clang::SourceLocation &source_loc) { switch (ty->getTypeClass()) { - case Type::Builtin: + case clang::Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); + const clang::BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { - case BuiltinType::Void: + case clang::BuiltinType::Void: return trans_create_node_symbol_str(c, "c_void"); - case BuiltinType::Bool: + case clang::BuiltinType::Bool: return trans_create_node_symbol_str(c, "bool"); - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::Char8: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::Char8: return trans_create_node_symbol_str(c, "u8"); - case BuiltinType::SChar: + case clang::BuiltinType::SChar: return trans_create_node_symbol_str(c, "i8"); - case BuiltinType::UShort: + case clang::BuiltinType::UShort: return trans_create_node_symbol_str(c, "c_ushort"); - case BuiltinType::UInt: + case clang::BuiltinType::UInt: return trans_create_node_symbol_str(c, "c_uint"); - case BuiltinType::ULong: + case clang::BuiltinType::ULong: return trans_create_node_symbol_str(c, "c_ulong"); - case BuiltinType::ULongLong: + case clang::BuiltinType::ULongLong: return trans_create_node_symbol_str(c, "c_ulonglong"); - case BuiltinType::Short: + case clang::BuiltinType::Short: return trans_create_node_symbol_str(c, "c_short"); - case BuiltinType::Int: + case clang::BuiltinType::Int: return trans_create_node_symbol_str(c, "c_int"); - case BuiltinType::Long: + case clang::BuiltinType::Long: return trans_create_node_symbol_str(c, "c_long"); - case BuiltinType::LongLong: + case clang::BuiltinType::LongLong: return trans_create_node_symbol_str(c, "c_longlong"); - case BuiltinType::UInt128: + case clang::BuiltinType::UInt128: return trans_create_node_symbol_str(c, "u128"); - case BuiltinType::Int128: + case clang::BuiltinType::Int128: return trans_create_node_symbol_str(c, "i128"); - case BuiltinType::Float: + case clang::BuiltinType::Float: return trans_create_node_symbol_str(c, "f32"); - case BuiltinType::Double: + case clang::BuiltinType::Double: return trans_create_node_symbol_str(c, "f64"); - case BuiltinType::Float128: + case clang::BuiltinType::Float128: return trans_create_node_symbol_str(c, "f128"); - case BuiltinType::Float16: + case clang::BuiltinType::Float16: return trans_create_node_symbol_str(c, "f16"); - case BuiltinType::LongDouble: + case clang::BuiltinType::LongDouble: return trans_create_node_symbol_str(c, "c_longdouble"); - case BuiltinType::WChar_U: - case BuiltinType::Char16: - case BuiltinType::Char32: - case BuiltinType::WChar_S: - case BuiltinType::Half: - case BuiltinType::NullPtr: - case BuiltinType::ObjCId: - case BuiltinType::ObjCClass: - case BuiltinType::ObjCSel: - case BuiltinType::OMPArraySection: - case BuiltinType::Dependent: - case BuiltinType::Overload: - case BuiltinType::BoundMember: - case BuiltinType::PseudoObject: - case BuiltinType::UnknownAny: - case BuiltinType::BuiltinFn: - case BuiltinType::ARCUnbridgedCast: - case BuiltinType::ShortAccum: - case BuiltinType::Accum: - case BuiltinType::LongAccum: - case BuiltinType::UShortAccum: - case BuiltinType::UAccum: - case BuiltinType::ULongAccum: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Half: + case clang::BuiltinType::NullPtr: + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + case clang::BuiltinType::ObjCSel: + case clang::BuiltinType::OMPArraySection: + case clang::BuiltinType::Dependent: + case clang::BuiltinType::Overload: + case clang::BuiltinType::BoundMember: + case clang::BuiltinType::PseudoObject: + case clang::BuiltinType::UnknownAny: + case clang::BuiltinType::BuiltinFn: + case clang::BuiltinType::ARCUnbridgedCast: + case clang::BuiltinType::ShortAccum: + case clang::BuiltinType::Accum: + case clang::BuiltinType::LongAccum: + case clang::BuiltinType::UShortAccum: + case clang::BuiltinType::UAccum: + case clang::BuiltinType::ULongAccum: - case BuiltinType::OCLImage1dRO: - case BuiltinType::OCLImage1dArrayRO: - case BuiltinType::OCLImage1dBufferRO: - case BuiltinType::OCLImage2dRO: - case BuiltinType::OCLImage2dArrayRO: - case BuiltinType::OCLImage2dDepthRO: - case BuiltinType::OCLImage2dArrayDepthRO: - case BuiltinType::OCLImage2dMSAARO: - case BuiltinType::OCLImage2dArrayMSAARO: - case BuiltinType::OCLImage2dMSAADepthRO: - case BuiltinType::OCLImage2dArrayMSAADepthRO: - case BuiltinType::OCLImage3dRO: - case BuiltinType::OCLImage1dWO: - case BuiltinType::OCLImage1dArrayWO: - case BuiltinType::OCLImage1dBufferWO: - case BuiltinType::OCLImage2dWO: - case BuiltinType::OCLImage2dArrayWO: - case BuiltinType::OCLImage2dDepthWO: - case BuiltinType::OCLImage2dArrayDepthWO: - case BuiltinType::OCLImage2dMSAAWO: - case BuiltinType::OCLImage2dArrayMSAAWO: - case BuiltinType::OCLImage2dMSAADepthWO: - case BuiltinType::OCLImage2dArrayMSAADepthWO: - case BuiltinType::OCLImage3dWO: - case BuiltinType::OCLImage1dRW: - case BuiltinType::OCLImage1dArrayRW: - case BuiltinType::OCLImage1dBufferRW: - case BuiltinType::OCLImage2dRW: - case BuiltinType::OCLImage2dArrayRW: - case BuiltinType::OCLImage2dDepthRW: - case BuiltinType::OCLImage2dArrayDepthRW: - case BuiltinType::OCLImage2dMSAARW: - case BuiltinType::OCLImage2dArrayMSAARW: - case BuiltinType::OCLImage2dMSAADepthRW: - case BuiltinType::OCLImage2dArrayMSAADepthRW: - case BuiltinType::OCLImage3dRW: - case BuiltinType::OCLSampler: - case BuiltinType::OCLEvent: - case BuiltinType::OCLClkEvent: - case BuiltinType::OCLQueue: - case BuiltinType::OCLReserveID: - case BuiltinType::ShortFract: - case BuiltinType::Fract: - case BuiltinType::LongFract: - case BuiltinType::UShortFract: - case BuiltinType::UFract: - case BuiltinType::ULongFract: - case BuiltinType::SatShortAccum: - case BuiltinType::SatAccum: - case BuiltinType::SatLongAccum: - case BuiltinType::SatUShortAccum: - case BuiltinType::SatUAccum: - case BuiltinType::SatULongAccum: - case BuiltinType::SatShortFract: - case BuiltinType::SatFract: - case BuiltinType::SatLongFract: - case BuiltinType::SatUShortFract: - case BuiltinType::SatUFract: - case BuiltinType::SatULongFract: + case clang::BuiltinType::OCLImage1dRO: + case clang::BuiltinType::OCLImage1dArrayRO: + case clang::BuiltinType::OCLImage1dBufferRO: + case clang::BuiltinType::OCLImage2dRO: + case clang::BuiltinType::OCLImage2dArrayRO: + case clang::BuiltinType::OCLImage2dDepthRO: + case clang::BuiltinType::OCLImage2dArrayDepthRO: + case clang::BuiltinType::OCLImage2dMSAARO: + case clang::BuiltinType::OCLImage2dArrayMSAARO: + case clang::BuiltinType::OCLImage2dMSAADepthRO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRO: + case clang::BuiltinType::OCLImage3dRO: + case clang::BuiltinType::OCLImage1dWO: + case clang::BuiltinType::OCLImage1dArrayWO: + case clang::BuiltinType::OCLImage1dBufferWO: + case clang::BuiltinType::OCLImage2dWO: + case clang::BuiltinType::OCLImage2dArrayWO: + case clang::BuiltinType::OCLImage2dDepthWO: + case clang::BuiltinType::OCLImage2dArrayDepthWO: + case clang::BuiltinType::OCLImage2dMSAAWO: + case clang::BuiltinType::OCLImage2dArrayMSAAWO: + case clang::BuiltinType::OCLImage2dMSAADepthWO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthWO: + case clang::BuiltinType::OCLImage3dWO: + case clang::BuiltinType::OCLImage1dRW: + case clang::BuiltinType::OCLImage1dArrayRW: + case clang::BuiltinType::OCLImage1dBufferRW: + case clang::BuiltinType::OCLImage2dRW: + case clang::BuiltinType::OCLImage2dArrayRW: + case clang::BuiltinType::OCLImage2dDepthRW: + case clang::BuiltinType::OCLImage2dArrayDepthRW: + case clang::BuiltinType::OCLImage2dMSAARW: + case clang::BuiltinType::OCLImage2dArrayMSAARW: + case clang::BuiltinType::OCLImage2dMSAADepthRW: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRW: + case clang::BuiltinType::OCLImage3dRW: + case clang::BuiltinType::OCLSampler: + case clang::BuiltinType::OCLEvent: + case clang::BuiltinType::OCLClkEvent: + case clang::BuiltinType::OCLQueue: + case clang::BuiltinType::OCLReserveID: + case clang::BuiltinType::ShortFract: + case clang::BuiltinType::Fract: + case clang::BuiltinType::LongFract: + case clang::BuiltinType::UShortFract: + case clang::BuiltinType::UFract: + case clang::BuiltinType::ULongFract: + case clang::BuiltinType::SatShortAccum: + case clang::BuiltinType::SatAccum: + case clang::BuiltinType::SatLongAccum: + case clang::BuiltinType::SatUShortAccum: + case clang::BuiltinType::SatUAccum: + case clang::BuiltinType::SatULongAccum: + case clang::BuiltinType::SatShortFract: + case clang::BuiltinType::SatFract: + case clang::BuiltinType::SatLongFract: + case clang::BuiltinType::SatUShortFract: + case clang::BuiltinType::SatUFract: + case clang::BuiltinType::SatULongFract: emit_warning(c, source_loc, "unsupported builtin type"); return nullptr; } break; } - case Type::Pointer: + case clang::Type::Pointer: { - const PointerType *pointer_ty = static_cast(ty); - QualType child_qt = pointer_ty->getPointeeType(); + const clang::PointerType *pointer_ty = static_cast(ty); + clang::QualType child_qt = pointer_ty->getPointeeType(); AstNode *child_node = trans_qual_type(c, child_qt, source_loc); if (child_node == nullptr) { emit_warning(c, source_loc, "pointer to unsupported type"); @@ -945,84 +943,84 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou child_qt.isVolatileQualified(), child_node, PtrLenC); } } - case Type::Typedef: + case clang::Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); return resolve_typedef_decl(c, typedef_decl); } - case Type::Elaborated: + case clang::Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); + const clang::ElaboratedType *elaborated_ty = static_cast(ty); switch (elaborated_ty->getKeyword()) { - case ETK_Struct: - case ETK_Enum: - case ETK_Union: + case clang::ETK_Struct: + case clang::ETK_Enum: + case clang::ETK_Union: return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc); - case ETK_Interface: - case ETK_Class: - case ETK_Typename: - case ETK_None: + case clang::ETK_Interface: + case clang::ETK_Class: + case clang::ETK_Typename: + case clang::ETK_None: emit_warning(c, source_loc, "unsupported elaborated type"); return nullptr; } } - case Type::FunctionProto: + case clang::Type::FunctionProto: { - const FunctionProtoType *fn_proto_ty = static_cast(ty); + const clang::FunctionProtoType *fn_proto_ty = static_cast(ty); AstNode *proto_node = trans_create_node(c, NodeTypeFnProto); switch (fn_proto_ty->getCallConv()) { - case CC_C: // __attribute__((cdecl)) + case clang::CC_C: // __attribute__((cdecl)) proto_node->data.fn_proto.cc = CallingConventionC; proto_node->data.fn_proto.is_extern = true; break; - case CC_X86StdCall: // __attribute__((stdcall)) + case clang::CC_X86StdCall: // __attribute__((stdcall)) proto_node->data.fn_proto.cc = CallingConventionStdcall; break; - case CC_X86FastCall: // __attribute__((fastcall)) + case clang::CC_X86FastCall: // __attribute__((fastcall)) emit_warning(c, source_loc, "unsupported calling convention: x86 fastcall"); return nullptr; - case CC_X86ThisCall: // __attribute__((thiscall)) + case clang::CC_X86ThisCall: // __attribute__((thiscall)) emit_warning(c, source_loc, "unsupported calling convention: x86 thiscall"); return nullptr; - case CC_X86VectorCall: // __attribute__((vectorcall)) + case clang::CC_X86VectorCall: // __attribute__((vectorcall)) emit_warning(c, source_loc, "unsupported calling convention: x86 vectorcall"); return nullptr; - case CC_X86Pascal: // __attribute__((pascal)) + case clang::CC_X86Pascal: // __attribute__((pascal)) emit_warning(c, source_loc, "unsupported calling convention: x86 pascal"); return nullptr; - case CC_Win64: // __attribute__((ms_abi)) + case clang::CC_Win64: // __attribute__((ms_abi)) emit_warning(c, source_loc, "unsupported calling convention: win64"); return nullptr; - case CC_X86_64SysV: // __attribute__((sysv_abi)) + case clang::CC_X86_64SysV: // __attribute__((sysv_abi)) emit_warning(c, source_loc, "unsupported calling convention: x86 64sysv"); return nullptr; - case CC_X86RegCall: + case clang::CC_X86RegCall: emit_warning(c, source_loc, "unsupported calling convention: x86 reg"); return nullptr; - case CC_AAPCS: // __attribute__((pcs("aapcs"))) + case clang::CC_AAPCS: // __attribute__((pcs("aapcs"))) emit_warning(c, source_loc, "unsupported calling convention: aapcs"); return nullptr; - case CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) + case clang::CC_AAPCS_VFP: // __attribute__((pcs("aapcs-vfp"))) emit_warning(c, source_loc, "unsupported calling convention: aapcs-vfp"); return nullptr; - case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc)) + case clang::CC_IntelOclBicc: // __attribute__((intel_ocl_bicc)) emit_warning(c, source_loc, "unsupported calling convention: intel_ocl_bicc"); return nullptr; - case CC_SpirFunction: // default for OpenCL functions on SPIR target + case clang::CC_SpirFunction: // default for OpenCL functions on SPIR target emit_warning(c, source_loc, "unsupported calling convention: SPIR function"); return nullptr; - case CC_OpenCLKernel: + case clang::CC_OpenCLKernel: emit_warning(c, source_loc, "unsupported calling convention: OpenCLKernel"); return nullptr; - case CC_Swift: + case clang::CC_Swift: emit_warning(c, source_loc, "unsupported calling convention: Swift"); return nullptr; - case CC_PreserveMost: + case clang::CC_PreserveMost: emit_warning(c, source_loc, "unsupported calling convention: PreserveMost"); return nullptr; - case CC_PreserveAll: + case clang::CC_PreserveAll: emit_warning(c, source_loc, "unsupported calling convention: PreserveAll"); return nullptr; } @@ -1040,7 +1038,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return nullptr; } // convert c_void to actual void (only for return type) - // we do want to look at the AstNode instead of QualType, because + // we do want to look at the AstNode instead of clang::QualType, because // if they do something like: // typedef Foo void; // void foo(void) -> Foo; @@ -1057,7 +1055,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou } for (size_t i = 0; i < param_count; i += 1) { - QualType qt = fn_proto_ty->getParamType(i); + clang::QualType qt = fn_proto_ty->getParamType(i); AstNode *param_type_node = trans_qual_type(c, qt, source_loc); if (param_type_node == nullptr) { @@ -1080,19 +1078,19 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou return proto_node; } - case Type::Record: + case clang::Type::Record: { - const RecordType *record_ty = static_cast(ty); + const clang::RecordType *record_ty = static_cast(ty); return resolve_record_decl(c, record_ty->getDecl()); } - case Type::Enum: + case clang::Type::Enum: { - const EnumType *enum_ty = static_cast(ty); + const clang::EnumType *enum_ty = static_cast(ty); return resolve_enum_decl(c, enum_ty->getDecl()); } - case Type::ConstantArray: + case clang::Type::ConstantArray: { - const ConstantArrayType *const_arr_ty = static_cast(ty); + const clang::ConstantArrayType *const_arr_ty = static_cast(ty); AstNode *child_type_node = trans_qual_type(c, const_arr_ty->getElementType(), source_loc); if (child_type_node == nullptr) { emit_warning(c, source_loc, "unresolved array element type"); @@ -1102,25 +1100,25 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou AstNode *size_node = trans_create_node_unsigned(c, size); return trans_create_node_array_type(c, size_node, child_type_node); } - case Type::Paren: + case clang::Type::Paren: { - const ParenType *paren_ty = static_cast(ty); + const clang::ParenType *paren_ty = static_cast(ty); return trans_qual_type(c, paren_ty->getInnerType(), source_loc); } - case Type::Decayed: + case clang::Type::Decayed: { - const DecayedType *decayed_ty = static_cast(ty); + const clang::DecayedType *decayed_ty = static_cast(ty); return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc); } - case Type::Attributed: + case clang::Type::Attributed: { - const AttributedType *attributed_ty = static_cast(ty); + const clang::AttributedType *attributed_ty = static_cast(ty); return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc); } - case Type::IncompleteArray: + case clang::Type::IncompleteArray: { - const IncompleteArrayType *incomplete_array_ty = static_cast(ty); - QualType child_qt = incomplete_array_ty->getElementType(); + const clang::IncompleteArrayType *incomplete_array_ty = static_cast(ty); + clang::QualType child_qt = incomplete_array_ty->getElementType(); AstNode *child_type_node = trans_qual_type(c, child_qt, source_loc); if (child_type_node == nullptr) { emit_warning(c, source_loc, "unresolved array element type"); @@ -1130,56 +1128,56 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou child_qt.isVolatileQualified(), child_type_node, PtrLenC); return pointer_node; } - case Type::BlockPointer: - case Type::LValueReference: - case Type::RValueReference: - case Type::MemberPointer: - case Type::VariableArray: - case Type::DependentSizedArray: - case Type::DependentSizedExtVector: - case Type::Vector: - case Type::ExtVector: - case Type::FunctionNoProto: - case Type::UnresolvedUsing: - case Type::Adjusted: - case Type::TypeOfExpr: - case Type::TypeOf: - case Type::Decltype: - case Type::UnaryTransform: - case Type::TemplateTypeParm: - case Type::SubstTemplateTypeParm: - case Type::SubstTemplateTypeParmPack: - case Type::TemplateSpecialization: - case Type::Auto: - case Type::InjectedClassName: - case Type::DependentName: - case Type::DependentTemplateSpecialization: - case Type::PackExpansion: - case Type::ObjCObject: - case Type::ObjCInterface: - case Type::Complex: - case Type::ObjCObjectPointer: - case Type::Atomic: - case Type::Pipe: - case Type::ObjCTypeParam: - case Type::DeducedTemplateSpecialization: - case Type::DependentAddressSpace: - case Type::DependentVector: + case clang::Type::BlockPointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: + case clang::Type::VariableArray: + case clang::Type::DependentSizedArray: + case clang::Type::DependentSizedExtVector: + case clang::Type::Vector: + case clang::Type::ExtVector: + case clang::Type::FunctionNoProto: + case clang::Type::UnresolvedUsing: + case clang::Type::Adjusted: + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + case clang::Type::UnaryTransform: + case clang::Type::TemplateTypeParm: + case clang::Type::SubstTemplateTypeParm: + case clang::Type::SubstTemplateTypeParmPack: + case clang::Type::TemplateSpecialization: + case clang::Type::Auto: + case clang::Type::InjectedClassName: + case clang::Type::DependentName: + case clang::Type::DependentTemplateSpecialization: + case clang::Type::PackExpansion: + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + case clang::Type::Complex: + case clang::Type::ObjCObjectPointer: + case clang::Type::Atomic: + case clang::Type::Pipe: + case clang::Type::ObjCTypeParam: + case clang::Type::DeducedTemplateSpecialization: + case clang::Type::DependentAddressSpace: + case clang::Type::DependentVector: emit_warning(c, source_loc, "unsupported type: '%s'", ty->getTypeClassName()); return nullptr; } zig_unreachable(); } -static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc) { +static AstNode *trans_qual_type(Context *c, clang::QualType qt, const clang::SourceLocation &source_loc) { return trans_type(c, qt.getTypePtr(), source_loc); } -static int trans_compound_stmt_inline(Context *c, TransScope *scope, const CompoundStmt *stmt, +static int trans_compound_stmt_inline(Context *c, TransScope *scope, const clang::CompoundStmt *stmt, AstNode *block_node, TransScope **out_node_scope) { assert(block_node->type == NodeTypeBlock); - for (CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) { + for (clang::CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) { AstNode *child_node; scope = trans_stmt(c, scope, *it, &child_node); if (scope == nullptr) @@ -1193,7 +1191,7 @@ static int trans_compound_stmt_inline(Context *c, TransScope *scope, const Compo return ErrorNone; } -static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const CompoundStmt *stmt, +static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const clang::CompoundStmt *stmt, TransScope **out_node_scope) { TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope); @@ -1202,8 +1200,8 @@ static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const Compoun return child_scope_block->node; } -static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStmt *stmt) { - const Expr *value_expr = stmt->getRetValue(); +static AstNode *trans_return_stmt(Context *c, TransScope *scope, const clang::ReturnStmt *stmt) { + const clang::Expr *value_expr = stmt->getRetValue(); if (value_expr == nullptr) { return trans_create_node(c, NodeTypeReturnExpr); } else { @@ -1215,7 +1213,7 @@ static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStm } } -static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) { +static AstNode *trans_integer_literal(Context *c, const clang::IntegerLiteral *stmt) { llvm::APSInt result; if (!stmt->EvaluateAsInt(result, *c->ctx)) { emit_warning(c, stmt->getLocStart(), "invalid integer literal"); @@ -1225,13 +1223,13 @@ static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) { } static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, TransScope *scope, - const ConditionalOperator *stmt) + const clang::ConditionalOperator *stmt) { AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr); - Expr *cond_expr = stmt->getCond(); - Expr *true_expr = stmt->getTrueExpr(); - Expr *false_expr = stmt->getFalseExpr(); + clang::Expr *cond_expr = stmt->getCond(); + clang::Expr *true_expr = stmt->getTrueExpr(); + clang::Expr *false_expr = stmt->getFalseExpr(); node->data.if_bool_expr.condition = trans_expr(c, ResultUsedYes, scope, cond_expr, TransRValue); if (node->data.if_bool_expr.condition == nullptr) @@ -1248,7 +1246,7 @@ static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, T return maybe_suppress_result(c, result_used, node); } -static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) { +static AstNode *trans_create_bin_op(Context *c, TransScope *scope, clang::Expr *lhs, BinOpType bin_op, clang::Expr *rhs) { AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); node->data.bin_op_expr.bin_op = bin_op; @@ -1263,7 +1261,7 @@ static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, Bi return node; } -static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) { +static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, clang::Expr *lhs, BinOpType bin_op, clang::Expr *rhs) { assert(bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeBoolOr); AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); node->data.bin_op_expr.bin_op = bin_op; @@ -1279,7 +1277,7 @@ static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, Expr *lh return node; } -static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) { +static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, clang::Expr *lhs, clang::Expr *rhs) { if (result_used == ResultUsedNo) { // common case AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); @@ -1330,10 +1328,10 @@ static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransSco } } -static AstNode *trans_create_shift_op(Context *c, TransScope *scope, QualType result_type, - Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) +static AstNode *trans_create_shift_op(Context *c, TransScope *scope, clang::QualType result_type, + clang::Expr *lhs_expr, BinOpType bin_op, clang::Expr *rhs_expr) { - const SourceLocation &rhs_location = rhs_expr->getLocStart(); + const clang::SourceLocation &rhs_location = rhs_expr->getLocStart(); AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location); // lhs >> u5(rh) @@ -1347,22 +1345,22 @@ static AstNode *trans_create_shift_op(Context *c, TransScope *scope, QualType re return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs); } -static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, const BinaryOperator *stmt) { +static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, const clang::BinaryOperator *stmt) { switch (stmt->getOpcode()) { - case BO_PtrMemD: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemD"); + case clang::BO_PtrMemD: + emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: clang::BO_PtrMemD"); return nullptr; - case BO_PtrMemI: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemI"); + case clang::BO_PtrMemI: + emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: clang::BO_PtrMemI"); return nullptr; - case BO_Cmp: - emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_Cmp"); + case clang::BO_Cmp: + emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: clang::BO_Cmp"); return nullptr; - case BO_Mul: + case clang::BO_Mul: return trans_create_bin_op(c, scope, stmt->getLHS(), qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeMultWrap : BinOpTypeMult, stmt->getRHS()); - case BO_Div: + case clang::BO_Div: if (qual_type_has_wrapping_overflow(c, stmt->getType())) { // unsigned/float division uses the operator return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS()); @@ -1377,7 +1375,7 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS fn_call->data.fn_call_expr.params.append(rhs); return fn_call; } - case BO_Rem: + case clang::BO_Rem: if (qual_type_has_wrapping_overflow(c, stmt->getType())) { // unsigned/float division uses the operator return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeMod, stmt->getRHS()); @@ -1392,43 +1390,43 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS fn_call->data.fn_call_expr.params.append(rhs); return fn_call; } - case BO_Add: + case clang::BO_Add: return trans_create_bin_op(c, scope, stmt->getLHS(), qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeAddWrap : BinOpTypeAdd, stmt->getRHS()); - case BO_Sub: + case clang::BO_Sub: return trans_create_bin_op(c, scope, stmt->getLHS(), qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub, stmt->getRHS()); - case BO_Shl: + case clang::BO_Shl: return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS()); - case BO_Shr: + case clang::BO_Shr: return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS()); - case BO_LT: + case clang::BO_LT: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS()); - case BO_GT: + case clang::BO_GT: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS()); - case BO_LE: + case clang::BO_LE: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS()); - case BO_GE: + case clang::BO_GE: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS()); - case BO_EQ: + case clang::BO_EQ: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS()); - case BO_NE: + case clang::BO_NE: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS()); - case BO_And: + case clang::BO_And: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS()); - case BO_Xor: + case clang::BO_Xor: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS()); - case BO_Or: + case clang::BO_Or: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS()); - case BO_LAnd: + case clang::BO_LAnd: return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS()); - case BO_LOr: + case clang::BO_LOr: return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); - case BO_Assign: + case clang::BO_Assign: return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS()); - case BO_Comma: + case clang::BO_Comma: { TransScopeBlock *scope_block = trans_scope_block_create(c, scope); Buf *label_name = buf_create_from_str("x"); @@ -1445,16 +1443,16 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS scope_block->node->data.block.statements.append(trans_create_node_break(c, label_name, maybe_suppress_result(c, result_used, rhs))); return scope_block->node; } - case BO_MulAssign: - case BO_DivAssign: - case BO_RemAssign: - case BO_AddAssign: - case BO_SubAssign: - case BO_ShlAssign: - case BO_ShrAssign: - case BO_AndAssign: - case BO_XorAssign: - case BO_OrAssign: + case clang::BO_MulAssign: + case clang::BO_DivAssign: + case clang::BO_RemAssign: + case clang::BO_AddAssign: + case clang::BO_SubAssign: + case clang::BO_ShlAssign: + case clang::BO_ShrAssign: + case clang::BO_AndAssign: + case clang::BO_XorAssign: + case clang::BO_OrAssign: zig_unreachable(); } @@ -1462,9 +1460,9 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS } static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result_used, TransScope *scope, - const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) + const clang::CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { - const SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); + const clang::SourceLocation &rhs_location = stmt->getRHS()->getLocStart(); AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location); bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr(); @@ -1544,7 +1542,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result } static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, TransScope *scope, - const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) + const clang::CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) { if (result_used == ResultUsedNo) { // simple common case, where the C and Zig are identical: @@ -1604,76 +1602,76 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, static AstNode *trans_compound_assign_operator(Context *c, ResultUsed result_used, TransScope *scope, - const CompoundAssignOperator *stmt) + const clang::CompoundAssignOperator *stmt) { switch (stmt->getOpcode()) { - case BO_MulAssign: + case clang::BO_MulAssign: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap); else return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimes, BinOpTypeMult); - case BO_DivAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign"); + case clang::BO_DivAssign: + emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: clang::BO_DivAssign"); return nullptr; - case BO_RemAssign: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign"); + case clang::BO_RemAssign: + emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: clang::BO_RemAssign"); return nullptr; - case BO_Cmp: - emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_Cmp"); + case clang::BO_Cmp: + emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: clang::BO_Cmp"); return nullptr; - case BO_AddAssign: + case clang::BO_AddAssign: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap); else return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlus, BinOpTypeAdd); - case BO_SubAssign: + case clang::BO_SubAssign: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap); else return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinus, BinOpTypeSub); - case BO_ShlAssign: + case clang::BO_ShlAssign: return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft); - case BO_ShrAssign: + case clang::BO_ShrAssign: return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight); - case BO_AndAssign: + case clang::BO_AndAssign: return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd); - case BO_XorAssign: + case clang::BO_XorAssign: return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor); - case BO_OrAssign: + case clang::BO_OrAssign: return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr); - case BO_PtrMemD: - case BO_PtrMemI: - case BO_Assign: - case BO_Mul: - case BO_Div: - case BO_Rem: - case BO_Add: - case BO_Sub: - case BO_Shl: - case BO_Shr: - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - case BO_And: - case BO_Xor: - case BO_Or: - case BO_LAnd: - case BO_LOr: - case BO_Comma: + case clang::BO_PtrMemD: + case clang::BO_PtrMemI: + case clang::BO_Assign: + case clang::BO_Mul: + case clang::BO_Div: + case clang::BO_Rem: + case clang::BO_Add: + case clang::BO_Sub: + case clang::BO_Shl: + case clang::BO_Shr: + case clang::BO_LT: + case clang::BO_GT: + case clang::BO_LE: + case clang::BO_GE: + case clang::BO_EQ: + case clang::BO_NE: + case clang::BO_And: + case clang::BO_Xor: + case clang::BO_Or: + case clang::BO_LAnd: + case clang::BO_LOr: + case clang::BO_Comma: zig_unreachable(); } zig_unreachable(); } -static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const ImplicitCastExpr *stmt) { +static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const clang::ImplicitCastExpr *stmt) { switch (stmt->getCastKind()) { - case CK_LValueToRValue: + case clang::CK_LValueToRValue: return trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); - case CK_IntegralCast: + case clang::CK_IntegralCast: { AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); if (target_node == nullptr) @@ -1681,15 +1679,15 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(), stmt->getSubExpr()->getType(), target_node); } - case CK_FunctionToPointerDecay: - case CK_ArrayToPointerDecay: + case clang::CK_FunctionToPointerDecay: + case clang::CK_ArrayToPointerDecay: { AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); if (target_node == nullptr) return nullptr; return target_node; } - case CK_BitCast: + case clang::CK_BitCast: { AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue); if (target_node == nullptr) @@ -1706,170 +1704,170 @@ static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const Im node->data.fn_call_expr.params.append(target_node); return node; } - case CK_NullToPointer: + case clang::CK_NullToPointer: return trans_create_node_unsigned(c, 0); - case CK_Dependent: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dependent"); + case clang::CK_Dependent: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_Dependent"); return nullptr; - case CK_LValueBitCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_LValueBitCast"); + case clang::CK_LValueBitCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_LValueBitCast"); return nullptr; - case CK_NoOp: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NoOp"); + case clang::CK_NoOp: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_NoOp"); return nullptr; - case CK_BaseToDerived: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerived"); + case clang::CK_BaseToDerived: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BaseToDerived"); return nullptr; - case CK_DerivedToBase: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBase"); + case clang::CK_DerivedToBase: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_DerivedToBase"); return nullptr; - case CK_UncheckedDerivedToBase: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UncheckedDerivedToBase"); + case clang::CK_UncheckedDerivedToBase: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_UncheckedDerivedToBase"); return nullptr; - case CK_Dynamic: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_Dynamic"); + case clang::CK_Dynamic: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_Dynamic"); return nullptr; - case CK_ToUnion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToUnion"); + case clang::CK_ToUnion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ToUnion"); return nullptr; - case CK_NullToMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NullToMemberPointer"); + case clang::CK_NullToMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_NullToMemberPointer"); return nullptr; - case CK_BaseToDerivedMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BaseToDerivedMemberPointer"); + case clang::CK_BaseToDerivedMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BaseToDerivedMemberPointer"); return nullptr; - case CK_DerivedToBaseMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_DerivedToBaseMemberPointer"); + case clang::CK_DerivedToBaseMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_DerivedToBaseMemberPointer"); return nullptr; - case CK_MemberPointerToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_MemberPointerToBoolean"); + case clang::CK_MemberPointerToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_MemberPointerToBoolean"); return nullptr; - case CK_ReinterpretMemberPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ReinterpretMemberPointer"); + case clang::CK_ReinterpretMemberPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ReinterpretMemberPointer"); return nullptr; - case CK_UserDefinedConversion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_UserDefinedConversion"); + case clang::CK_UserDefinedConversion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_UserDefinedConversion"); return nullptr; - case CK_ConstructorConversion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ConstructorConversion"); + case clang::CK_ConstructorConversion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ConstructorConversion"); return nullptr; - case CK_IntegralToPointer: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToPointer"); + case clang::CK_IntegralToPointer: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralToPointer"); return nullptr; - case CK_PointerToIntegral: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToIntegral"); + case clang::CK_PointerToIntegral: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_PointerToIntegral"); return nullptr; - case CK_PointerToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_PointerToBoolean"); + case clang::CK_PointerToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_PointerToBoolean"); return nullptr; - case CK_ToVoid: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ToVoid"); + case clang::CK_ToVoid: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ToVoid"); return nullptr; - case CK_VectorSplat: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_VectorSplat"); + case clang::CK_VectorSplat: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_VectorSplat"); return nullptr; - case CK_IntegralToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToBoolean"); + case clang::CK_IntegralToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralToBoolean"); return nullptr; - case CK_IntegralToFloating: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralToFloating"); + case clang::CK_IntegralToFloating: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralToFloating"); return nullptr; - case CK_FloatingToIntegral: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToIntegral"); + case clang::CK_FloatingToIntegral: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingToIntegral"); return nullptr; - case CK_FloatingToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingToBoolean"); + case clang::CK_FloatingToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingToBoolean"); return nullptr; - case CK_BooleanToSignedIntegral: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BooleanToSignedIntegral"); + case clang::CK_BooleanToSignedIntegral: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BooleanToSignedIntegral"); return nullptr; - case CK_FloatingCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingCast"); + case clang::CK_FloatingCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingCast"); return nullptr; - case CK_CPointerToObjCPointerCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CPointerToObjCPointerCast"); + case clang::CK_CPointerToObjCPointerCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_CPointerToObjCPointerCast"); return nullptr; - case CK_BlockPointerToObjCPointerCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BlockPointerToObjCPointerCast"); + case clang::CK_BlockPointerToObjCPointerCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BlockPointerToObjCPointerCast"); return nullptr; - case CK_AnyPointerToBlockPointerCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AnyPointerToBlockPointerCast"); + case clang::CK_AnyPointerToBlockPointerCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_AnyPointerToBlockPointerCast"); return nullptr; - case CK_ObjCObjectLValueCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ObjCObjectLValueCast"); + case clang::CK_ObjCObjectLValueCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ObjCObjectLValueCast"); return nullptr; - case CK_FloatingRealToComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingRealToComplex"); + case clang::CK_FloatingRealToComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingRealToComplex"); return nullptr; - case CK_FloatingComplexToReal: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToReal"); + case clang::CK_FloatingComplexToReal: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexToReal"); return nullptr; - case CK_FloatingComplexToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToBoolean"); + case clang::CK_FloatingComplexToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexToBoolean"); return nullptr; - case CK_FloatingComplexCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexCast"); + case clang::CK_FloatingComplexCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexCast"); return nullptr; - case CK_FloatingComplexToIntegralComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_FloatingComplexToIntegralComplex"); + case clang::CK_FloatingComplexToIntegralComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_FloatingComplexToIntegralComplex"); return nullptr; - case CK_IntegralRealToComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralRealToComplex"); + case clang::CK_IntegralRealToComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralRealToComplex"); return nullptr; - case CK_IntegralComplexToReal: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToReal"); + case clang::CK_IntegralComplexToReal: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexToReal"); return nullptr; - case CK_IntegralComplexToBoolean: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToBoolean"); + case clang::CK_IntegralComplexToBoolean: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexToBoolean"); return nullptr; - case CK_IntegralComplexCast: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexCast"); + case clang::CK_IntegralComplexCast: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexCast"); return nullptr; - case CK_IntegralComplexToFloatingComplex: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntegralComplexToFloatingComplex"); + case clang::CK_IntegralComplexToFloatingComplex: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntegralComplexToFloatingComplex"); return nullptr; - case CK_ARCProduceObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCProduceObject"); + case clang::CK_ARCProduceObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCProduceObject"); return nullptr; - case CK_ARCConsumeObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCConsumeObject"); + case clang::CK_ARCConsumeObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCConsumeObject"); return nullptr; - case CK_ARCReclaimReturnedObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCReclaimReturnedObject"); + case clang::CK_ARCReclaimReturnedObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCReclaimReturnedObject"); return nullptr; - case CK_ARCExtendBlockObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ARCExtendBlockObject"); + case clang::CK_ARCExtendBlockObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ARCExtendBlockObject"); return nullptr; - case CK_AtomicToNonAtomic: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AtomicToNonAtomic"); + case clang::CK_AtomicToNonAtomic: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_AtomicToNonAtomic"); return nullptr; - case CK_NonAtomicToAtomic: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_NonAtomicToAtomic"); + case clang::CK_NonAtomicToAtomic: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_NonAtomicToAtomic"); return nullptr; - case CK_CopyAndAutoreleaseBlockObject: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_CopyAndAutoreleaseBlockObject"); + case clang::CK_CopyAndAutoreleaseBlockObject: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_CopyAndAutoreleaseBlockObject"); return nullptr; - case CK_BuiltinFnToFnPtr: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_BuiltinFnToFnPtr"); + case clang::CK_BuiltinFnToFnPtr: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_BuiltinFnToFnPtr"); return nullptr; - case CK_ZeroToOCLEvent: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLEvent"); + case clang::CK_ZeroToOCLEvent: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ZeroToOCLEvent"); return nullptr; - case CK_ZeroToOCLQueue: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_ZeroToOCLQueue"); + case clang::CK_ZeroToOCLQueue: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_ZeroToOCLQueue"); return nullptr; - case CK_AddressSpaceConversion: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_AddressSpaceConversion"); + case clang::CK_AddressSpaceConversion: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_AddressSpaceConversion"); return nullptr; - case CK_IntToOCLSampler: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast CK_IntToOCLSampler"); + case clang::CK_IntToOCLSampler: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation cast clang::CK_IntToOCLSampler"); return nullptr; } zig_unreachable(); } -static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const DeclRefExpr *stmt, TransLRValue lrval) { - const ValueDecl *value_decl = stmt->getDecl(); +static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const clang::DeclRefExpr *stmt, TransLRValue lrval) { + const clang::ValueDecl *value_decl = stmt->getDecl(); Buf *c_symbol_name = buf_create_from_str(decl_name(value_decl)); Buf *zig_symbol_name = trans_lookup_zig_symbol(c, scope, c_symbol_name); if (lrval == TransLValue) { @@ -1879,9 +1877,9 @@ static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const DeclRef } static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, TransScope *scope, - const UnaryOperator *stmt, BinOpType assign_op) + const clang::UnaryOperator *stmt, BinOpType assign_op) { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); if (result_used == ResultUsedNo) { // common case @@ -1935,9 +1933,9 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr } static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, TransScope *scope, - const UnaryOperator *stmt, BinOpType assign_op) + const clang::UnaryOperator *stmt, BinOpType assign_op) { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); if (result_used == ResultUsedNo) { // common case @@ -1984,36 +1982,36 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra return child_scope->node; } -static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const UnaryOperator *stmt) { +static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const clang::UnaryOperator *stmt) { switch (stmt->getOpcode()) { - case UO_PostInc: + case clang::UO_PostInc: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap); else return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus); - case UO_PostDec: + case clang::UO_PostDec: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap); else return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus); - case UO_PreInc: + case clang::UO_PreInc: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap); else return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus); - case UO_PreDec: + case clang::UO_PreDec: if (qual_type_has_wrapping_overflow(c, stmt->getType())) return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap); else return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus); - case UO_AddrOf: + case clang::UO_AddrOf: { AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue); if (value_node == nullptr) return value_node; return trans_create_node_addr_of(c, value_node); } - case UO_Deref: + case clang::UO_Deref: { AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransRValue); if (value_node == nullptr) @@ -2024,12 +2022,12 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node); return trans_create_node_ptr_deref(c, unwrapped); } - case UO_Plus: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus"); + case clang::UO_Plus: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Plus"); return nullptr; - case UO_Minus: + case clang::UO_Minus: { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); if (!qual_type_has_wrapping_overflow(c, op_expr->getType())) { AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr); node->data.prefix_op_expr.prefix_op = PrefixOpNegation; @@ -2055,41 +2053,41 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc return nullptr; } } - case UO_Not: + case clang::UO_Not: { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); AstNode *sub_node = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue); if (sub_node == nullptr) return nullptr; return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); } - case UO_LNot: + case clang::UO_LNot: { - Expr *op_expr = stmt->getSubExpr(); + clang::Expr *op_expr = stmt->getSubExpr(); AstNode *sub_node = trans_bool_expr(c, ResultUsedYes, scope, op_expr, TransRValue); if (sub_node == nullptr) return nullptr; return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node); } - case UO_Real: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real"); + case clang::UO_Real: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Real"); return nullptr; - case UO_Imag: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Imag"); + case clang::UO_Imag: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Imag"); return nullptr; - case UO_Extension: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Extension"); + case clang::UO_Extension: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Extension"); return nullptr; - case UO_Coawait: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Coawait"); + case clang::UO_Coawait: + emit_warning(c, stmt->getLocStart(), "TODO handle C translation clang::UO_Coawait"); return nullptr; } zig_unreachable(); } -static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt *stmt, +static int trans_local_declaration(Context *c, TransScope *scope, const clang::DeclStmt *stmt, AstNode **out_node, TransScope **out_scope) { // declarations are added via the scope @@ -2099,11 +2097,11 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt assert(scope_block != nullptr); for (auto iter = stmt->decl_begin(); iter != stmt->decl_end(); iter++) { - Decl *decl = *iter; + clang::Decl *decl = *iter; switch (decl->getKind()) { - case Decl::Var: { - VarDecl *var_decl = (VarDecl *)decl; - QualType qual_type = var_decl->getTypeSourceInfo()->getType(); + case clang::Decl::Var: { + clang::VarDecl *var_decl = (clang::VarDecl *)decl; + clang::QualType qual_type = var_decl->getTypeSourceInfo()->getType(); AstNode *init_node = nullptr; if (var_decl->hasInit()) { init_node = trans_expr(c, ResultUsedYes, scope, var_decl->getInit(), TransRValue); @@ -2128,220 +2126,220 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt scope_block->node->data.block.statements.append(node); continue; } - case Decl::AccessSpec: + case clang::Decl::AccessSpec: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind AccessSpec"); return ErrorUnexpected; - case Decl::Block: + case clang::Decl::Block: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Block"); return ErrorUnexpected; - case Decl::Captured: + case clang::Decl::Captured: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Captured"); return ErrorUnexpected; - case Decl::ClassScopeFunctionSpecialization: + case clang::Decl::ClassScopeFunctionSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassScopeFunctionSpecialization"); return ErrorUnexpected; - case Decl::Empty: + case clang::Decl::Empty: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Empty"); return ErrorUnexpected; - case Decl::Export: + case clang::Decl::Export: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Export"); return ErrorUnexpected; - case Decl::ExternCContext: + case clang::Decl::ExternCContext: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ExternCContext"); return ErrorUnexpected; - case Decl::FileScopeAsm: + case clang::Decl::FileScopeAsm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FileScopeAsm"); return ErrorUnexpected; - case Decl::Friend: + case clang::Decl::Friend: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Friend"); return ErrorUnexpected; - case Decl::FriendTemplate: + case clang::Decl::FriendTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FriendTemplate"); return ErrorUnexpected; - case Decl::Import: + case clang::Decl::Import: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Import"); return ErrorUnexpected; - case Decl::LinkageSpec: + case clang::Decl::LinkageSpec: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind LinkageSpec"); return ErrorUnexpected; - case Decl::Label: + case clang::Decl::Label: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Label"); return ErrorUnexpected; - case Decl::Namespace: + case clang::Decl::Namespace: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Namespace"); return ErrorUnexpected; - case Decl::NamespaceAlias: + case clang::Decl::NamespaceAlias: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NamespaceAlias"); return ErrorUnexpected; - case Decl::ObjCCompatibleAlias: + case clang::Decl::ObjCCompatibleAlias: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCompatibleAlias"); return ErrorUnexpected; - case Decl::ObjCCategory: + case clang::Decl::ObjCCategory: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategory"); return ErrorUnexpected; - case Decl::ObjCCategoryImpl: + case clang::Decl::ObjCCategoryImpl: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategoryImpl"); return ErrorUnexpected; - case Decl::ObjCImplementation: + case clang::Decl::ObjCImplementation: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCImplementation"); return ErrorUnexpected; - case Decl::ObjCInterface: + case clang::Decl::ObjCInterface: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCInterface"); return ErrorUnexpected; - case Decl::ObjCProtocol: + case clang::Decl::ObjCProtocol: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProtocol"); return ErrorUnexpected; - case Decl::ObjCMethod: + case clang::Decl::ObjCMethod: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCMethod"); return ErrorUnexpected; - case Decl::ObjCProperty: + case clang::Decl::ObjCProperty: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProperty"); return ErrorUnexpected; - case Decl::BuiltinTemplate: + case clang::Decl::BuiltinTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind BuiltinTemplate"); return ErrorUnexpected; - case Decl::ClassTemplate: + case clang::Decl::ClassTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplate"); return ErrorUnexpected; - case Decl::FunctionTemplate: + case clang::Decl::FunctionTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FunctionTemplate"); return ErrorUnexpected; - case Decl::TypeAliasTemplate: + case clang::Decl::TypeAliasTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAliasTemplate"); return ErrorUnexpected; - case Decl::VarTemplate: + case clang::Decl::VarTemplate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplate"); return ErrorUnexpected; - case Decl::TemplateTemplateParm: + case clang::Decl::TemplateTemplateParm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTemplateParm"); return ErrorUnexpected; - case Decl::Enum: + case clang::Decl::Enum: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Enum"); return ErrorUnexpected; - case Decl::Record: + case clang::Decl::Record: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Record"); return ErrorUnexpected; - case Decl::CXXRecord: + case clang::Decl::CXXRecord: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXRecord"); return ErrorUnexpected; - case Decl::ClassTemplateSpecialization: + case clang::Decl::ClassTemplateSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplateSpecialization"); return ErrorUnexpected; - case Decl::ClassTemplatePartialSpecialization: + case clang::Decl::ClassTemplatePartialSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplatePartialSpecialization"); return ErrorUnexpected; - case Decl::TemplateTypeParm: + case clang::Decl::TemplateTypeParm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTypeParm"); return ErrorUnexpected; - case Decl::ObjCTypeParam: + case clang::Decl::ObjCTypeParam: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCTypeParam"); return ErrorUnexpected; - case Decl::TypeAlias: + case clang::Decl::TypeAlias: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAlias"); return ErrorUnexpected; - case Decl::Typedef: + case clang::Decl::Typedef: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Typedef"); return ErrorUnexpected; - case Decl::UnresolvedUsingTypename: + case clang::Decl::UnresolvedUsingTypename: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingTypename"); return ErrorUnexpected; - case Decl::Using: + case clang::Decl::Using: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Using"); return ErrorUnexpected; - case Decl::UsingDirective: + case clang::Decl::UsingDirective: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingDirective"); return ErrorUnexpected; - case Decl::UsingPack: + case clang::Decl::UsingPack: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingPack"); return ErrorUnexpected; - case Decl::UsingShadow: + case clang::Decl::UsingShadow: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingShadow"); return ErrorUnexpected; - case Decl::ConstructorUsingShadow: + case clang::Decl::ConstructorUsingShadow: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ConstructorUsingShadow"); return ErrorUnexpected; - case Decl::Binding: + case clang::Decl::Binding: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Binding"); return ErrorUnexpected; - case Decl::Field: + case clang::Decl::Field: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Field"); return ErrorUnexpected; - case Decl::ObjCAtDefsField: + case clang::Decl::ObjCAtDefsField: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCAtDefsField"); return ErrorUnexpected; - case Decl::ObjCIvar: + case clang::Decl::ObjCIvar: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCIvar"); return ErrorUnexpected; - case Decl::Function: + case clang::Decl::Function: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Function"); return ErrorUnexpected; - case Decl::CXXDeductionGuide: + case clang::Decl::CXXDeductionGuide: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDeductionGuide"); return ErrorUnexpected; - case Decl::CXXMethod: + case clang::Decl::CXXMethod: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXMethod"); return ErrorUnexpected; - case Decl::CXXConstructor: + case clang::Decl::CXXConstructor: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConstructor"); return ErrorUnexpected; - case Decl::CXXConversion: + case clang::Decl::CXXConversion: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConversion"); return ErrorUnexpected; - case Decl::CXXDestructor: + case clang::Decl::CXXDestructor: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDestructor"); return ErrorUnexpected; - case Decl::MSProperty: + case clang::Decl::MSProperty: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind MSProperty"); return ErrorUnexpected; - case Decl::NonTypeTemplateParm: + case clang::Decl::NonTypeTemplateParm: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NonTypeTemplateParm"); return ErrorUnexpected; - case Decl::Decomposition: + case clang::Decl::Decomposition: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Decomposition"); return ErrorUnexpected; - case Decl::ImplicitParam: + case clang::Decl::ImplicitParam: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ImplicitParam"); return ErrorUnexpected; - case Decl::OMPCapturedExpr: + case clang::Decl::OMPCapturedExpr: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPCapturedExpr"); return ErrorUnexpected; - case Decl::ParmVar: + case clang::Decl::ParmVar: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ParmVar"); return ErrorUnexpected; - case Decl::VarTemplateSpecialization: + case clang::Decl::VarTemplateSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplateSpecialization"); return ErrorUnexpected; - case Decl::VarTemplatePartialSpecialization: + case clang::Decl::VarTemplatePartialSpecialization: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplatePartialSpecialization"); return ErrorUnexpected; - case Decl::EnumConstant: + case clang::Decl::EnumConstant: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind EnumConstant"); return ErrorUnexpected; - case Decl::IndirectField: + case clang::Decl::IndirectField: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind IndirectField"); return ErrorUnexpected; - case Decl::OMPDeclareReduction: + case clang::Decl::OMPDeclareReduction: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPDeclareReduction"); return ErrorUnexpected; - case Decl::UnresolvedUsingValue: + case clang::Decl::UnresolvedUsingValue: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingValue"); return ErrorUnexpected; - case Decl::OMPThreadPrivate: + case clang::Decl::OMPThreadPrivate: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPThreadPrivate"); return ErrorUnexpected; - case Decl::ObjCPropertyImpl: + case clang::Decl::ObjCPropertyImpl: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCPropertyImpl"); return ErrorUnexpected; - case Decl::PragmaComment: + case clang::Decl::PragmaComment: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaComment"); return ErrorUnexpected; - case Decl::PragmaDetectMismatch: + case clang::Decl::PragmaDetectMismatch: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaDetectMismatch"); return ErrorUnexpected; - case Decl::StaticAssert: + case clang::Decl::StaticAssert: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind StaticAssert"); return ErrorUnexpected; - case Decl::TranslationUnit: + case clang::Decl::TranslationUnit: emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TranslationUnit"); return ErrorUnexpected; } @@ -2368,7 +2366,7 @@ static AstNode *to_enum_zero_cmp(Context *c, AstNode *expr, AstNode *enum_type) return trans_create_node_bin_op(c, expr, BinOpTypeCmpNotEq, bitcast); } -static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval) { +static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval) { AstNode *res = trans_expr(c, result_used, scope, expr, lrval); if (res == nullptr) return nullptr; @@ -2405,133 +2403,133 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope * } - const Type *ty = get_expr_qual_type_before_implicit_cast(c, expr).getTypePtr(); + const clang::Type *ty = get_expr_qual_type_before_implicit_cast(c, expr).getTypePtr(); auto classs = ty->getTypeClass(); switch (classs) { - case Type::Builtin: + case clang::Type::Builtin: { - const BuiltinType *builtin_ty = static_cast(ty); + const clang::BuiltinType *builtin_ty = static_cast(ty); switch (builtin_ty->getKind()) { - case BuiltinType::Bool: - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: - case BuiltinType::UShort: - case BuiltinType::UInt: - case BuiltinType::ULong: - case BuiltinType::ULongLong: - case BuiltinType::Short: - case BuiltinType::Int: - case BuiltinType::Long: - case BuiltinType::LongLong: - case BuiltinType::UInt128: - case BuiltinType::Int128: - case BuiltinType::Float: - case BuiltinType::Double: - case BuiltinType::Float128: - case BuiltinType::LongDouble: - case BuiltinType::WChar_U: - case BuiltinType::Char8: - case BuiltinType::Char16: - case BuiltinType::Char32: - case BuiltinType::WChar_S: - case BuiltinType::Float16: + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Int128: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::Float128: + case clang::BuiltinType::LongDouble: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char8: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Float16: return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); - case BuiltinType::NullPtr: + case clang::BuiltinType::NullPtr: return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned(c, 0)); - case BuiltinType::Void: - case BuiltinType::Half: - case BuiltinType::ObjCId: - case BuiltinType::ObjCClass: - case BuiltinType::ObjCSel: - case BuiltinType::OMPArraySection: - case BuiltinType::Dependent: - case BuiltinType::Overload: - case BuiltinType::BoundMember: - case BuiltinType::PseudoObject: - case BuiltinType::UnknownAny: - case BuiltinType::BuiltinFn: - case BuiltinType::ARCUnbridgedCast: - case BuiltinType::OCLImage1dRO: - case BuiltinType::OCLImage1dArrayRO: - case BuiltinType::OCLImage1dBufferRO: - case BuiltinType::OCLImage2dRO: - case BuiltinType::OCLImage2dArrayRO: - case BuiltinType::OCLImage2dDepthRO: - case BuiltinType::OCLImage2dArrayDepthRO: - case BuiltinType::OCLImage2dMSAARO: - case BuiltinType::OCLImage2dArrayMSAARO: - case BuiltinType::OCLImage2dMSAADepthRO: - case BuiltinType::OCLImage2dArrayMSAADepthRO: - case BuiltinType::OCLImage3dRO: - case BuiltinType::OCLImage1dWO: - case BuiltinType::OCLImage1dArrayWO: - case BuiltinType::OCLImage1dBufferWO: - case BuiltinType::OCLImage2dWO: - case BuiltinType::OCLImage2dArrayWO: - case BuiltinType::OCLImage2dDepthWO: - case BuiltinType::OCLImage2dArrayDepthWO: - case BuiltinType::OCLImage2dMSAAWO: - case BuiltinType::OCLImage2dArrayMSAAWO: - case BuiltinType::OCLImage2dMSAADepthWO: - case BuiltinType::OCLImage2dArrayMSAADepthWO: - case BuiltinType::OCLImage3dWO: - case BuiltinType::OCLImage1dRW: - case BuiltinType::OCLImage1dArrayRW: - case BuiltinType::OCLImage1dBufferRW: - case BuiltinType::OCLImage2dRW: - case BuiltinType::OCLImage2dArrayRW: - case BuiltinType::OCLImage2dDepthRW: - case BuiltinType::OCLImage2dArrayDepthRW: - case BuiltinType::OCLImage2dMSAARW: - case BuiltinType::OCLImage2dArrayMSAARW: - case BuiltinType::OCLImage2dMSAADepthRW: - case BuiltinType::OCLImage2dArrayMSAADepthRW: - case BuiltinType::OCLImage3dRW: - case BuiltinType::OCLSampler: - case BuiltinType::OCLEvent: - case BuiltinType::OCLClkEvent: - case BuiltinType::OCLQueue: - case BuiltinType::OCLReserveID: - case BuiltinType::ShortAccum: - case BuiltinType::Accum: - case BuiltinType::LongAccum: - case BuiltinType::UShortAccum: - case BuiltinType::UAccum: - case BuiltinType::ULongAccum: - case BuiltinType::ShortFract: - case BuiltinType::Fract: - case BuiltinType::LongFract: - case BuiltinType::UShortFract: - case BuiltinType::UFract: - case BuiltinType::ULongFract: - case BuiltinType::SatShortAccum: - case BuiltinType::SatAccum: - case BuiltinType::SatLongAccum: - case BuiltinType::SatUShortAccum: - case BuiltinType::SatUAccum: - case BuiltinType::SatULongAccum: - case BuiltinType::SatShortFract: - case BuiltinType::SatFract: - case BuiltinType::SatLongFract: - case BuiltinType::SatUShortFract: - case BuiltinType::SatUFract: - case BuiltinType::SatULongFract: + case clang::BuiltinType::Void: + case clang::BuiltinType::Half: + case clang::BuiltinType::ObjCId: + case clang::BuiltinType::ObjCClass: + case clang::BuiltinType::ObjCSel: + case clang::BuiltinType::OMPArraySection: + case clang::BuiltinType::Dependent: + case clang::BuiltinType::Overload: + case clang::BuiltinType::BoundMember: + case clang::BuiltinType::PseudoObject: + case clang::BuiltinType::UnknownAny: + case clang::BuiltinType::BuiltinFn: + case clang::BuiltinType::ARCUnbridgedCast: + case clang::BuiltinType::OCLImage1dRO: + case clang::BuiltinType::OCLImage1dArrayRO: + case clang::BuiltinType::OCLImage1dBufferRO: + case clang::BuiltinType::OCLImage2dRO: + case clang::BuiltinType::OCLImage2dArrayRO: + case clang::BuiltinType::OCLImage2dDepthRO: + case clang::BuiltinType::OCLImage2dArrayDepthRO: + case clang::BuiltinType::OCLImage2dMSAARO: + case clang::BuiltinType::OCLImage2dArrayMSAARO: + case clang::BuiltinType::OCLImage2dMSAADepthRO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRO: + case clang::BuiltinType::OCLImage3dRO: + case clang::BuiltinType::OCLImage1dWO: + case clang::BuiltinType::OCLImage1dArrayWO: + case clang::BuiltinType::OCLImage1dBufferWO: + case clang::BuiltinType::OCLImage2dWO: + case clang::BuiltinType::OCLImage2dArrayWO: + case clang::BuiltinType::OCLImage2dDepthWO: + case clang::BuiltinType::OCLImage2dArrayDepthWO: + case clang::BuiltinType::OCLImage2dMSAAWO: + case clang::BuiltinType::OCLImage2dArrayMSAAWO: + case clang::BuiltinType::OCLImage2dMSAADepthWO: + case clang::BuiltinType::OCLImage2dArrayMSAADepthWO: + case clang::BuiltinType::OCLImage3dWO: + case clang::BuiltinType::OCLImage1dRW: + case clang::BuiltinType::OCLImage1dArrayRW: + case clang::BuiltinType::OCLImage1dBufferRW: + case clang::BuiltinType::OCLImage2dRW: + case clang::BuiltinType::OCLImage2dArrayRW: + case clang::BuiltinType::OCLImage2dDepthRW: + case clang::BuiltinType::OCLImage2dArrayDepthRW: + case clang::BuiltinType::OCLImage2dMSAARW: + case clang::BuiltinType::OCLImage2dArrayMSAARW: + case clang::BuiltinType::OCLImage2dMSAADepthRW: + case clang::BuiltinType::OCLImage2dArrayMSAADepthRW: + case clang::BuiltinType::OCLImage3dRW: + case clang::BuiltinType::OCLSampler: + case clang::BuiltinType::OCLEvent: + case clang::BuiltinType::OCLClkEvent: + case clang::BuiltinType::OCLQueue: + case clang::BuiltinType::OCLReserveID: + case clang::BuiltinType::ShortAccum: + case clang::BuiltinType::Accum: + case clang::BuiltinType::LongAccum: + case clang::BuiltinType::UShortAccum: + case clang::BuiltinType::UAccum: + case clang::BuiltinType::ULongAccum: + case clang::BuiltinType::ShortFract: + case clang::BuiltinType::Fract: + case clang::BuiltinType::LongFract: + case clang::BuiltinType::UShortFract: + case clang::BuiltinType::UFract: + case clang::BuiltinType::ULongFract: + case clang::BuiltinType::SatShortAccum: + case clang::BuiltinType::SatAccum: + case clang::BuiltinType::SatLongAccum: + case clang::BuiltinType::SatUShortAccum: + case clang::BuiltinType::SatUAccum: + case clang::BuiltinType::SatULongAccum: + case clang::BuiltinType::SatShortFract: + case clang::BuiltinType::SatFract: + case clang::BuiltinType::SatLongFract: + case clang::BuiltinType::SatUShortFract: + case clang::BuiltinType::SatUFract: + case clang::BuiltinType::SatULongFract: return res; } break; } - case Type::Pointer: + case clang::Type::Pointer: return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned(c, 0)); - case Type::Typedef: + case clang::Type::Typedef: { - const TypedefType *typedef_ty = static_cast(ty); - const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + const clang::TypedefType *typedef_ty = static_cast(ty); + const clang::TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; @@ -2540,79 +2538,79 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope * return res; } - case Type::Enum: + case clang::Type::Enum: { - const EnumType *enum_ty = static_cast(ty); + const clang::EnumType *enum_ty = static_cast(ty); AstNode *enum_type = resolve_enum_decl(c, enum_ty->getDecl()); return to_enum_zero_cmp(c, res, enum_type); } - case Type::Elaborated: + case clang::Type::Elaborated: { - const ElaboratedType *elaborated_ty = static_cast(ty); + const clang::ElaboratedType *elaborated_ty = static_cast(ty); switch (elaborated_ty->getKeyword()) { - case ETK_Enum: { + case clang::ETK_Enum: { AstNode *enum_type = trans_qual_type(c, elaborated_ty->getNamedType(), expr->getLocStart()); return to_enum_zero_cmp(c, res, enum_type); } - case ETK_Struct: - case ETK_Union: - case ETK_Interface: - case ETK_Class: - case ETK_Typename: - case ETK_None: + case clang::ETK_Struct: + case clang::ETK_Union: + case clang::ETK_Interface: + case clang::ETK_Class: + case clang::ETK_Typename: + case clang::ETK_None: return res; } } - case Type::FunctionProto: - case Type::Record: - case Type::ConstantArray: - case Type::Paren: - case Type::Decayed: - case Type::Attributed: - case Type::IncompleteArray: - case Type::BlockPointer: - case Type::LValueReference: - case Type::RValueReference: - case Type::MemberPointer: - case Type::VariableArray: - case Type::DependentSizedArray: - case Type::DependentSizedExtVector: - case Type::Vector: - case Type::ExtVector: - case Type::FunctionNoProto: - case Type::UnresolvedUsing: - case Type::Adjusted: - case Type::TypeOfExpr: - case Type::TypeOf: - case Type::Decltype: - case Type::UnaryTransform: - case Type::TemplateTypeParm: - case Type::SubstTemplateTypeParm: - case Type::SubstTemplateTypeParmPack: - case Type::TemplateSpecialization: - case Type::Auto: - case Type::InjectedClassName: - case Type::DependentName: - case Type::DependentTemplateSpecialization: - case Type::PackExpansion: - case Type::ObjCObject: - case Type::ObjCInterface: - case Type::Complex: - case Type::ObjCObjectPointer: - case Type::Atomic: - case Type::Pipe: - case Type::ObjCTypeParam: - case Type::DeducedTemplateSpecialization: - case Type::DependentAddressSpace: - case Type::DependentVector: + case clang::Type::FunctionProto: + case clang::Type::Record: + case clang::Type::ConstantArray: + case clang::Type::Paren: + case clang::Type::Decayed: + case clang::Type::Attributed: + case clang::Type::IncompleteArray: + case clang::Type::BlockPointer: + case clang::Type::LValueReference: + case clang::Type::RValueReference: + case clang::Type::MemberPointer: + case clang::Type::VariableArray: + case clang::Type::DependentSizedArray: + case clang::Type::DependentSizedExtVector: + case clang::Type::Vector: + case clang::Type::ExtVector: + case clang::Type::FunctionNoProto: + case clang::Type::UnresolvedUsing: + case clang::Type::Adjusted: + case clang::Type::TypeOfExpr: + case clang::Type::TypeOf: + case clang::Type::Decltype: + case clang::Type::UnaryTransform: + case clang::Type::TemplateTypeParm: + case clang::Type::SubstTemplateTypeParm: + case clang::Type::SubstTemplateTypeParmPack: + case clang::Type::TemplateSpecialization: + case clang::Type::Auto: + case clang::Type::InjectedClassName: + case clang::Type::DependentName: + case clang::Type::DependentTemplateSpecialization: + case clang::Type::PackExpansion: + case clang::Type::ObjCObject: + case clang::Type::ObjCInterface: + case clang::Type::Complex: + case clang::Type::ObjCObjectPointer: + case clang::Type::Atomic: + case clang::Type::Pipe: + case clang::Type::ObjCTypeParam: + case clang::Type::DeducedTemplateSpecialization: + case clang::Type::DependentAddressSpace: + case clang::Type::DependentVector: return res; } zig_unreachable(); } -static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) { +static AstNode *trans_while_loop(Context *c, TransScope *scope, const clang::WhileStmt *stmt) { TransScopeWhile *while_scope = trans_scope_while_create(c, scope); while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); @@ -2627,7 +2625,7 @@ static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt return while_scope->node; } -static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *stmt) { +static AstNode *trans_if_statement(Context *c, TransScope *scope, const clang::IfStmt *stmt) { // if (c) t // if (c) t else e AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr); @@ -2649,7 +2647,7 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt * return if_node; } -static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) { +static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::CallExpr *stmt) { AstNode *node = trans_create_node(c, NodeTypeFnCallExpr); AstNode *callee_raw_node = trans_expr(c, ResultUsedYes, scope, stmt->getCallee(), TransRValue); @@ -2657,16 +2655,16 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * return nullptr; bool is_ptr = false; - const FunctionProtoType *fn_ty = qual_type_get_fn_proto(stmt->getCallee()->getType(), &is_ptr); + const clang::FunctionProtoType *fn_ty = qual_type_get_fn_proto(stmt->getCallee()->getType(), &is_ptr); AstNode *callee_node = nullptr; if (is_ptr && fn_ty) { - if (stmt->getCallee()->getStmtClass() == Stmt::ImplicitCastExprClass) { - const ImplicitCastExpr *implicit_cast = static_cast(stmt->getCallee()); - if (implicit_cast->getCastKind() == CK_FunctionToPointerDecay) { - if (implicit_cast->getSubExpr()->getStmtClass() == Stmt::DeclRefExprClass) { - const DeclRefExpr *decl_ref = static_cast(implicit_cast->getSubExpr()); - const Decl *decl = decl_ref->getFoundDecl(); - if (decl->getKind() == Decl::Function) { + if (stmt->getCallee()->getStmtClass() == clang::Stmt::ImplicitCastExprClass) { + const clang::ImplicitCastExpr *implicit_cast = static_cast(stmt->getCallee()); + if (implicit_cast->getCastKind() == clang::CK_FunctionToPointerDecay) { + if (implicit_cast->getSubExpr()->getStmtClass() == clang::Stmt::DeclRefExprClass) { + const clang::DeclRefExpr *decl_ref = static_cast(implicit_cast->getSubExpr()); + const clang::Decl *decl = decl_ref->getFoundDecl(); + if (decl->getKind() == clang::Decl::Function) { callee_node = callee_raw_node; } } @@ -2682,7 +2680,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * node->data.fn_call_expr.fn_ref_expr = callee_node; unsigned num_args = stmt->getNumArgs(); - const Expr * const* args = stmt->getArgs(); + const clang::Expr * const* args = stmt->getArgs(); for (unsigned i = 0; i < num_args; i += 1) { AstNode *arg_node = trans_expr(c, ResultUsedYes, scope, args[i], TransRValue); if (arg_node == nullptr) @@ -2698,7 +2696,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * return node; } -static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExpr *stmt) { +static AstNode *trans_member_expr(Context *c, TransScope *scope, const clang::MemberExpr *stmt) { AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue); if (container_node == nullptr) return nullptr; @@ -2713,7 +2711,7 @@ static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExp return node; } -static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const ArraySubscriptExpr *stmt) { +static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const clang::ArraySubscriptExpr *stmt) { AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue); if (container_node == nullptr) return nullptr; @@ -2730,7 +2728,7 @@ static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const } static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, TransScope *scope, - const CStyleCastExpr *stmt, TransLRValue lrvalue) + const clang::CStyleCastExpr *stmt, TransLRValue lrvalue) { AstNode *sub_expr_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), lrvalue); if (sub_expr_node == nullptr) @@ -2740,7 +2738,7 @@ static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, Tran } static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scope, - const UnaryExprOrTypeTraitExpr *stmt) + const clang::UnaryExprOrTypeTraitExpr *stmt) { AstNode *type_node = trans_qual_type(c, stmt->getTypeOfArgument(), stmt->getLocStart()); if (type_node == nullptr) @@ -2751,14 +2749,14 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scop return node; } -static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt *stmt) { +static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const clang::DoStmt *stmt) { TransScopeWhile *while_scope = trans_scope_while_create(c, parent_scope); while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); AstNode *body_node; TransScope *child_scope; - if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) { + if (stmt->getBody()->getStmtClass() == clang::Stmt::CompoundStmtClass) { // there's already a block in C, so we'll append our condition to it. // c: do { // c: a; @@ -2811,11 +2809,11 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt return while_scope->node; } -static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) { +static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const clang::ForStmt *stmt) { AstNode *loop_block_node; TransScopeWhile *while_scope; TransScope *cond_scope; - const Stmt *init_stmt = stmt->getInit(); + const clang::Stmt *init_stmt = stmt->getInit(); if (init_stmt == nullptr) { while_scope = trans_scope_while_create(c, parent_scope); loop_block_node = while_scope->node; @@ -2836,12 +2834,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt child_scope->node->data.block.statements.append(while_scope->node); } - const Stmt *cond_stmt = stmt->getCond(); + const clang::Stmt *cond_stmt = stmt->getCond(); if (cond_stmt == nullptr) { while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); } else { - if (Expr::classof(cond_stmt)) { - const Expr *cond_expr = static_cast(cond_stmt); + if (clang::Expr::classof(cond_stmt)) { + const clang::Expr *cond_expr = static_cast(cond_stmt); while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, cond_scope, cond_expr, TransRValue); if (while_scope->node->data.while_expr.condition == nullptr) @@ -2854,7 +2852,7 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt } } - const Stmt *inc_stmt = stmt->getInc(); + const clang::Stmt *inc_stmt = stmt->getInc(); if (inc_stmt != nullptr) { AstNode *inc_node; TransScope *inc_scope = trans_stmt(c, cond_scope, inc_stmt, &inc_node); @@ -2877,12 +2875,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt return loop_block_node; } -static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) { +static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const clang::SwitchStmt *stmt) { TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope); TransScopeSwitch *switch_scope; - const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt(); + const clang::DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt(); if (var_decl_stmt == nullptr) { switch_scope = trans_scope_switch_create(c, &block_scope->base); } else { @@ -2901,7 +2899,7 @@ static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const Sw switch_scope->end_label_name = end_label_name; block_scope->node->data.block.name = end_label_name; - const Expr *cond_expr = stmt->getCond(); + const clang::Expr *cond_expr = stmt->getCond(); assert(cond_expr != nullptr); AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue); @@ -2910,9 +2908,9 @@ static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const Sw switch_scope->switch_node->data.switch_expr.expr = expr_node; AstNode *body_node; - const Stmt *body_stmt = stmt->getBody(); - if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) { - if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt, + const clang::Stmt *body_stmt = stmt->getBody(); + if (body_stmt->getStmtClass() == clang::Stmt::CompoundStmtClass) { + if (trans_compound_stmt_inline(c, &switch_scope->base, (const clang::CompoundStmt *)body_stmt, block_scope->node, nullptr)) { return nullptr; @@ -2944,7 +2942,7 @@ static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) { return nullptr; } -static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node, +static int trans_switch_case(Context *c, TransScope *parent_scope, const clang::CaseStmt *stmt, AstNode **out_node, TransScope **out_scope) { *out_node = nullptr; @@ -2989,7 +2987,7 @@ static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStm return ErrorNone; } -static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node, +static int trans_switch_default(Context *c, TransScope *parent_scope, const clang::DefaultStmt *stmt, AstNode **out_node, TransScope **out_scope) { *out_node = nullptr; @@ -3026,25 +3024,25 @@ static int trans_switch_default(Context *c, TransScope *parent_scope, const Defa return ErrorNone; } -static AstNode *trans_string_literal(Context *c, TransScope *scope, const StringLiteral *stmt) { +static AstNode *trans_string_literal(Context *c, TransScope *scope, const clang::StringLiteral *stmt) { switch (stmt->getKind()) { - case StringLiteral::Ascii: - case StringLiteral::UTF8: + case clang::StringLiteral::Ascii: + case clang::StringLiteral::UTF8: return trans_create_node_str_lit_c(c, string_ref_to_buf(stmt->getString())); - case StringLiteral::UTF16: + case clang::StringLiteral::UTF16: emit_warning(c, stmt->getLocStart(), "TODO support UTF16 string literals"); return nullptr; - case StringLiteral::UTF32: + case clang::StringLiteral::UTF32: emit_warning(c, stmt->getLocStart(), "TODO support UTF32 string literals"); return nullptr; - case StringLiteral::Wide: + case clang::StringLiteral::Wide: emit_warning(c, stmt->getLocStart(), "TODO support wide string literals"); return nullptr; } zig_unreachable(); } -static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt *stmt) { +static AstNode *trans_break_stmt(Context *c, TransScope *scope, const clang::BreakStmt *stmt) { TransScope *cur_scope = scope; while (cur_scope != nullptr) { if (cur_scope->id == TransScopeIdWhile) { @@ -3058,7 +3056,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt zig_unreachable(); } -static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ContinueStmt *stmt) { +static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const clang::ContinueStmt *stmt) { return trans_create_node(c, NodeTypeContinue); } @@ -3071,47 +3069,47 @@ static int wrap_stmt(AstNode **out_node, TransScope **out_scope, TransScope *in_ return ErrorNone; } -static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, +static int trans_stmt_extra(Context *c, TransScope *scope, const clang::Stmt *stmt, ResultUsed result_used, TransLRValue lrvalue, AstNode **out_node, TransScope **out_child_scope, TransScope **out_node_scope) { - Stmt::StmtClass sc = stmt->getStmtClass(); + clang::Stmt::StmtClass sc = stmt->getStmtClass(); switch (sc) { - case Stmt::ReturnStmtClass: + case clang::Stmt::ReturnStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_return_stmt(c, scope, (const ReturnStmt *)stmt)); - case Stmt::CompoundStmtClass: + trans_return_stmt(c, scope, (const clang::ReturnStmt *)stmt)); + case clang::Stmt::CompoundStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_compound_stmt(c, scope, (const CompoundStmt *)stmt, out_node_scope)); - case Stmt::IntegerLiteralClass: + trans_compound_stmt(c, scope, (const clang::CompoundStmt *)stmt, out_node_scope)); + case clang::Stmt::IntegerLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_integer_literal(c, (const IntegerLiteral *)stmt)); - case Stmt::ConditionalOperatorClass: + trans_integer_literal(c, (const clang::IntegerLiteral *)stmt)); + case clang::Stmt::ConditionalOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_conditional_operator(c, result_used, scope, (const ConditionalOperator *)stmt)); - case Stmt::BinaryOperatorClass: + trans_conditional_operator(c, result_used, scope, (const clang::ConditionalOperator *)stmt)); + case clang::Stmt::BinaryOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_binary_operator(c, result_used, scope, (const BinaryOperator *)stmt)); - case Stmt::CompoundAssignOperatorClass: + trans_binary_operator(c, result_used, scope, (const clang::BinaryOperator *)stmt)); + case clang::Stmt::CompoundAssignOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_compound_assign_operator(c, result_used, scope, (const CompoundAssignOperator *)stmt)); - case Stmt::ImplicitCastExprClass: + trans_compound_assign_operator(c, result_used, scope, (const clang::CompoundAssignOperator *)stmt)); + case clang::Stmt::ImplicitCastExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_implicit_cast_expr(c, scope, (const ImplicitCastExpr *)stmt)); - case Stmt::DeclRefExprClass: + trans_implicit_cast_expr(c, scope, (const clang::ImplicitCastExpr *)stmt)); + case clang::Stmt::DeclRefExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_decl_ref_expr(c, scope, (const DeclRefExpr *)stmt, lrvalue)); - case Stmt::UnaryOperatorClass: + trans_decl_ref_expr(c, scope, (const clang::DeclRefExpr *)stmt, lrvalue)); + case clang::Stmt::UnaryOperatorClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt)); - case Stmt::DeclStmtClass: - return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); - case Stmt::DoStmtClass: - case Stmt::WhileStmtClass: { - AstNode *while_node = sc == Stmt::DoStmtClass - ? trans_do_loop(c, scope, (const DoStmt *)stmt) - : trans_while_loop(c, scope, (const WhileStmt *)stmt); + trans_unary_operator(c, result_used, scope, (const clang::UnaryOperator *)stmt)); + case clang::Stmt::DeclStmtClass: + return trans_local_declaration(c, scope, (const clang::DeclStmt *)stmt, out_node, out_child_scope); + case clang::Stmt::DoStmtClass: + case clang::Stmt::WhileStmtClass: { + AstNode *while_node = sc == clang::Stmt::DoStmtClass + ? trans_do_loop(c, scope, (const clang::DoStmt *)stmt) + : trans_while_loop(c, scope, (const clang::WhileStmt *)stmt); if (while_node == nullptr) return ErrorUnexpected; @@ -3122,556 +3120,556 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, return wrap_stmt(out_node, out_child_scope, scope, while_node); } - case Stmt::IfStmtClass: + case clang::Stmt::IfStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_if_statement(c, scope, (const IfStmt *)stmt)); - case Stmt::CallExprClass: + trans_if_statement(c, scope, (const clang::IfStmt *)stmt)); + case clang::Stmt::CallExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_call_expr(c, result_used, scope, (const CallExpr *)stmt)); - case Stmt::NullStmtClass: + trans_call_expr(c, result_used, scope, (const clang::CallExpr *)stmt)); + case clang::Stmt::NullStmtClass: *out_node = nullptr; *out_child_scope = scope; return ErrorNone; - case Stmt::MemberExprClass: + case clang::Stmt::MemberExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_member_expr(c, scope, (const MemberExpr *)stmt)); - case Stmt::ArraySubscriptExprClass: + trans_member_expr(c, scope, (const clang::MemberExpr *)stmt)); + case clang::Stmt::ArraySubscriptExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_array_subscript_expr(c, scope, (const ArraySubscriptExpr *)stmt)); - case Stmt::CStyleCastExprClass: + trans_array_subscript_expr(c, scope, (const clang::ArraySubscriptExpr *)stmt)); + case clang::Stmt::CStyleCastExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_c_style_cast_expr(c, result_used, scope, (const CStyleCastExpr *)stmt, lrvalue)); - case Stmt::UnaryExprOrTypeTraitExprClass: + trans_c_style_cast_expr(c, result_used, scope, (const clang::CStyleCastExpr *)stmt, lrvalue)); + case clang::Stmt::UnaryExprOrTypeTraitExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); - case Stmt::ForStmtClass: { - AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt); + trans_unary_expr_or_type_trait_expr(c, scope, (const clang::UnaryExprOrTypeTraitExpr *)stmt)); + case clang::Stmt::ForStmtClass: { + AstNode *node = trans_for_loop(c, scope, (const clang::ForStmt *)stmt); return wrap_stmt(out_node, out_child_scope, scope, node); } - case Stmt::StringLiteralClass: + case clang::Stmt::StringLiteralClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_string_literal(c, scope, (const StringLiteral *)stmt)); - case Stmt::BreakStmtClass: + trans_string_literal(c, scope, (const clang::StringLiteral *)stmt)); + case clang::Stmt::BreakStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_break_stmt(c, scope, (const BreakStmt *)stmt)); - case Stmt::ContinueStmtClass: + trans_break_stmt(c, scope, (const clang::BreakStmt *)stmt)); + case clang::Stmt::ContinueStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_continue_stmt(c, scope, (const ContinueStmt *)stmt)); - case Stmt::ParenExprClass: + trans_continue_stmt(c, scope, (const clang::ContinueStmt *)stmt)); + case clang::Stmt::ParenExprClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue)); - case Stmt::SwitchStmtClass: + trans_expr(c, result_used, scope, ((const clang::ParenExpr*)stmt)->getSubExpr(), lrvalue)); + case clang::Stmt::SwitchStmtClass: return wrap_stmt(out_node, out_child_scope, scope, - trans_switch_stmt(c, scope, (const SwitchStmt *)stmt)); - case Stmt::CaseStmtClass: - return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope); - case Stmt::DefaultStmtClass: - return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope); - case Stmt::NoStmtClass: + trans_switch_stmt(c, scope, (const clang::SwitchStmt *)stmt)); + case clang::Stmt::CaseStmtClass: + return trans_switch_case(c, scope, (const clang::CaseStmt *)stmt, out_node, out_child_scope); + case clang::Stmt::DefaultStmtClass: + return trans_switch_default(c, scope, (const clang::DefaultStmt *)stmt, out_node, out_child_scope); + case clang::Stmt::NoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass"); return ErrorUnexpected; - case Stmt::GCCAsmStmtClass: + case clang::Stmt::GCCAsmStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GCCAsmStmtClass"); return ErrorUnexpected; - case Stmt::MSAsmStmtClass: + case clang::Stmt::MSAsmStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSAsmStmtClass"); return ErrorUnexpected; - case Stmt::AttributedStmtClass: + case clang::Stmt::AttributedStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AttributedStmtClass"); return ErrorUnexpected; - case Stmt::CXXCatchStmtClass: + case clang::Stmt::CXXCatchStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXCatchStmtClass"); return ErrorUnexpected; - case Stmt::CXXForRangeStmtClass: + case clang::Stmt::CXXForRangeStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXForRangeStmtClass"); return ErrorUnexpected; - case Stmt::CXXTryStmtClass: + case clang::Stmt::CXXTryStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTryStmtClass"); return ErrorUnexpected; - case Stmt::CapturedStmtClass: + case clang::Stmt::CapturedStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CapturedStmtClass"); return ErrorUnexpected; - case Stmt::CoreturnStmtClass: + case clang::Stmt::CoreturnStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoreturnStmtClass"); return ErrorUnexpected; - case Stmt::CoroutineBodyStmtClass: + case clang::Stmt::CoroutineBodyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass"); return ErrorUnexpected; - case Stmt::BinaryConditionalOperatorClass: + case clang::Stmt::BinaryConditionalOperatorClass: emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass"); return ErrorUnexpected; - case Stmt::AddrLabelExprClass: + case clang::Stmt::AddrLabelExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AddrLabelExprClass"); return ErrorUnexpected; - case Stmt::ArrayInitIndexExprClass: + case clang::Stmt::ArrayInitIndexExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitIndexExprClass"); return ErrorUnexpected; - case Stmt::ArrayInitLoopExprClass: + case clang::Stmt::ArrayInitLoopExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitLoopExprClass"); return ErrorUnexpected; - case Stmt::ArrayTypeTraitExprClass: + case clang::Stmt::ArrayTypeTraitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayTypeTraitExprClass"); return ErrorUnexpected; - case Stmt::AsTypeExprClass: + case clang::Stmt::AsTypeExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AsTypeExprClass"); return ErrorUnexpected; - case Stmt::AtomicExprClass: + case clang::Stmt::AtomicExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C AtomicExprClass"); return ErrorUnexpected; - case Stmt::BlockExprClass: + case clang::Stmt::BlockExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C BlockExprClass"); return ErrorUnexpected; - case Stmt::CXXBindTemporaryExprClass: + case clang::Stmt::CXXBindTemporaryExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBindTemporaryExprClass"); return ErrorUnexpected; - case Stmt::CXXBoolLiteralExprClass: + case clang::Stmt::CXXBoolLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBoolLiteralExprClass"); return ErrorUnexpected; - case Stmt::CXXConstructExprClass: + case clang::Stmt::CXXConstructExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstructExprClass"); return ErrorUnexpected; - case Stmt::CXXTemporaryObjectExprClass: + case clang::Stmt::CXXTemporaryObjectExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTemporaryObjectExprClass"); return ErrorUnexpected; - case Stmt::CXXDefaultArgExprClass: + case clang::Stmt::CXXDefaultArgExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultArgExprClass"); return ErrorUnexpected; - case Stmt::CXXDefaultInitExprClass: + case clang::Stmt::CXXDefaultInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultInitExprClass"); return ErrorUnexpected; - case Stmt::CXXDeleteExprClass: + case clang::Stmt::CXXDeleteExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDeleteExprClass"); return ErrorUnexpected; - case Stmt::CXXDependentScopeMemberExprClass: + case clang::Stmt::CXXDependentScopeMemberExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDependentScopeMemberExprClass"); return ErrorUnexpected; - case Stmt::CXXFoldExprClass: + case clang::Stmt::CXXFoldExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFoldExprClass"); return ErrorUnexpected; - case Stmt::CXXInheritedCtorInitExprClass: + case clang::Stmt::CXXInheritedCtorInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXInheritedCtorInitExprClass"); return ErrorUnexpected; - case Stmt::CXXNewExprClass: + case clang::Stmt::CXXNewExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNewExprClass"); return ErrorUnexpected; - case Stmt::CXXNoexceptExprClass: + case clang::Stmt::CXXNoexceptExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNoexceptExprClass"); return ErrorUnexpected; - case Stmt::CXXNullPtrLiteralExprClass: + case clang::Stmt::CXXNullPtrLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNullPtrLiteralExprClass"); return ErrorUnexpected; - case Stmt::CXXPseudoDestructorExprClass: + case clang::Stmt::CXXPseudoDestructorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXPseudoDestructorExprClass"); return ErrorUnexpected; - case Stmt::CXXScalarValueInitExprClass: + case clang::Stmt::CXXScalarValueInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXScalarValueInitExprClass"); return ErrorUnexpected; - case Stmt::CXXStdInitializerListExprClass: + case clang::Stmt::CXXStdInitializerListExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStdInitializerListExprClass"); return ErrorUnexpected; - case Stmt::CXXThisExprClass: + case clang::Stmt::CXXThisExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThisExprClass"); return ErrorUnexpected; - case Stmt::CXXThrowExprClass: + case clang::Stmt::CXXThrowExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThrowExprClass"); return ErrorUnexpected; - case Stmt::CXXTypeidExprClass: + case clang::Stmt::CXXTypeidExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTypeidExprClass"); return ErrorUnexpected; - case Stmt::CXXUnresolvedConstructExprClass: + case clang::Stmt::CXXUnresolvedConstructExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUnresolvedConstructExprClass"); return ErrorUnexpected; - case Stmt::CXXUuidofExprClass: + case clang::Stmt::CXXUuidofExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUuidofExprClass"); return ErrorUnexpected; - case Stmt::CUDAKernelCallExprClass: + case clang::Stmt::CUDAKernelCallExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CUDAKernelCallExprClass"); return ErrorUnexpected; - case Stmt::CXXMemberCallExprClass: + case clang::Stmt::CXXMemberCallExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXMemberCallExprClass"); return ErrorUnexpected; - case Stmt::CXXOperatorCallExprClass: + case clang::Stmt::CXXOperatorCallExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXOperatorCallExprClass"); return ErrorUnexpected; - case Stmt::UserDefinedLiteralClass: + case clang::Stmt::UserDefinedLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C UserDefinedLiteralClass"); return ErrorUnexpected; - case Stmt::CXXFunctionalCastExprClass: + case clang::Stmt::CXXFunctionalCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFunctionalCastExprClass"); return ErrorUnexpected; - case Stmt::CXXConstCastExprClass: + case clang::Stmt::CXXConstCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstCastExprClass"); return ErrorUnexpected; - case Stmt::CXXDynamicCastExprClass: + case clang::Stmt::CXXDynamicCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDynamicCastExprClass"); return ErrorUnexpected; - case Stmt::CXXReinterpretCastExprClass: + case clang::Stmt::CXXReinterpretCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXReinterpretCastExprClass"); return ErrorUnexpected; - case Stmt::CXXStaticCastExprClass: + case clang::Stmt::CXXStaticCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStaticCastExprClass"); return ErrorUnexpected; - case Stmt::ObjCBridgedCastExprClass: + case clang::Stmt::ObjCBridgedCastExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBridgedCastExprClass"); return ErrorUnexpected; - case Stmt::CharacterLiteralClass: + case clang::Stmt::CharacterLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CharacterLiteralClass"); return ErrorUnexpected; - case Stmt::ChooseExprClass: + case clang::Stmt::ChooseExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ChooseExprClass"); return ErrorUnexpected; - case Stmt::CompoundLiteralExprClass: + case clang::Stmt::CompoundLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CompoundLiteralExprClass"); return ErrorUnexpected; - case Stmt::ConvertVectorExprClass: + case clang::Stmt::ConvertVectorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ConvertVectorExprClass"); return ErrorUnexpected; - case Stmt::CoawaitExprClass: + case clang::Stmt::CoawaitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoawaitExprClass"); return ErrorUnexpected; - case Stmt::CoyieldExprClass: + case clang::Stmt::CoyieldExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C CoyieldExprClass"); return ErrorUnexpected; - case Stmt::DependentCoawaitExprClass: + case clang::Stmt::DependentCoawaitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DependentCoawaitExprClass"); return ErrorUnexpected; - case Stmt::DependentScopeDeclRefExprClass: + case clang::Stmt::DependentScopeDeclRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DependentScopeDeclRefExprClass"); return ErrorUnexpected; - case Stmt::DesignatedInitExprClass: + case clang::Stmt::DesignatedInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitExprClass"); return ErrorUnexpected; - case Stmt::DesignatedInitUpdateExprClass: + case clang::Stmt::DesignatedInitUpdateExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitUpdateExprClass"); return ErrorUnexpected; - case Stmt::ExprWithCleanupsClass: + case clang::Stmt::ExprWithCleanupsClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ExprWithCleanupsClass"); return ErrorUnexpected; - case Stmt::ExpressionTraitExprClass: + case clang::Stmt::ExpressionTraitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ExpressionTraitExprClass"); return ErrorUnexpected; - case Stmt::ExtVectorElementExprClass: + case clang::Stmt::ExtVectorElementExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ExtVectorElementExprClass"); return ErrorUnexpected; - case Stmt::FloatingLiteralClass: + case clang::Stmt::FloatingLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C FloatingLiteralClass"); return ErrorUnexpected; - case Stmt::FunctionParmPackExprClass: + case clang::Stmt::FunctionParmPackExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C FunctionParmPackExprClass"); return ErrorUnexpected; - case Stmt::GNUNullExprClass: + case clang::Stmt::GNUNullExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GNUNullExprClass"); return ErrorUnexpected; - case Stmt::GenericSelectionExprClass: + case clang::Stmt::GenericSelectionExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GenericSelectionExprClass"); return ErrorUnexpected; - case Stmt::ImaginaryLiteralClass: + case clang::Stmt::ImaginaryLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ImaginaryLiteralClass"); return ErrorUnexpected; - case Stmt::ImplicitValueInitExprClass: + case clang::Stmt::ImplicitValueInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ImplicitValueInitExprClass"); return ErrorUnexpected; - case Stmt::InitListExprClass: + case clang::Stmt::InitListExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C InitListExprClass"); return ErrorUnexpected; - case Stmt::LambdaExprClass: + case clang::Stmt::LambdaExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C LambdaExprClass"); return ErrorUnexpected; - case Stmt::MSPropertyRefExprClass: + case clang::Stmt::MSPropertyRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertyRefExprClass"); return ErrorUnexpected; - case Stmt::MSPropertySubscriptExprClass: + case clang::Stmt::MSPropertySubscriptExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertySubscriptExprClass"); return ErrorUnexpected; - case Stmt::MaterializeTemporaryExprClass: + case clang::Stmt::MaterializeTemporaryExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MaterializeTemporaryExprClass"); return ErrorUnexpected; - case Stmt::NoInitExprClass: + case clang::Stmt::NoInitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C NoInitExprClass"); return ErrorUnexpected; - case Stmt::OMPArraySectionExprClass: + case clang::Stmt::OMPArraySectionExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPArraySectionExprClass"); return ErrorUnexpected; - case Stmt::ObjCArrayLiteralClass: + case clang::Stmt::ObjCArrayLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCArrayLiteralClass"); return ErrorUnexpected; - case Stmt::ObjCAvailabilityCheckExprClass: + case clang::Stmt::ObjCAvailabilityCheckExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAvailabilityCheckExprClass"); return ErrorUnexpected; - case Stmt::ObjCBoolLiteralExprClass: + case clang::Stmt::ObjCBoolLiteralExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoolLiteralExprClass"); return ErrorUnexpected; - case Stmt::ObjCBoxedExprClass: + case clang::Stmt::ObjCBoxedExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoxedExprClass"); return ErrorUnexpected; - case Stmt::ObjCDictionaryLiteralClass: + case clang::Stmt::ObjCDictionaryLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCDictionaryLiteralClass"); return ErrorUnexpected; - case Stmt::ObjCEncodeExprClass: + case clang::Stmt::ObjCEncodeExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCEncodeExprClass"); return ErrorUnexpected; - case Stmt::ObjCIndirectCopyRestoreExprClass: + case clang::Stmt::ObjCIndirectCopyRestoreExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIndirectCopyRestoreExprClass"); return ErrorUnexpected; - case Stmt::ObjCIsaExprClass: + case clang::Stmt::ObjCIsaExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIsaExprClass"); return ErrorUnexpected; - case Stmt::ObjCIvarRefExprClass: + case clang::Stmt::ObjCIvarRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIvarRefExprClass"); return ErrorUnexpected; - case Stmt::ObjCMessageExprClass: + case clang::Stmt::ObjCMessageExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCMessageExprClass"); return ErrorUnexpected; - case Stmt::ObjCPropertyRefExprClass: + case clang::Stmt::ObjCPropertyRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCPropertyRefExprClass"); return ErrorUnexpected; - case Stmt::ObjCProtocolExprClass: + case clang::Stmt::ObjCProtocolExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCProtocolExprClass"); return ErrorUnexpected; - case Stmt::ObjCSelectorExprClass: + case clang::Stmt::ObjCSelectorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSelectorExprClass"); return ErrorUnexpected; - case Stmt::ObjCStringLiteralClass: + case clang::Stmt::ObjCStringLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCStringLiteralClass"); return ErrorUnexpected; - case Stmt::ObjCSubscriptRefExprClass: + case clang::Stmt::ObjCSubscriptRefExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSubscriptRefExprClass"); return ErrorUnexpected; - case Stmt::OffsetOfExprClass: + case clang::Stmt::OffsetOfExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OffsetOfExprClass"); return ErrorUnexpected; - case Stmt::OpaqueValueExprClass: + case clang::Stmt::OpaqueValueExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OpaqueValueExprClass"); return ErrorUnexpected; - case Stmt::UnresolvedLookupExprClass: + case clang::Stmt::UnresolvedLookupExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedLookupExprClass"); return ErrorUnexpected; - case Stmt::UnresolvedMemberExprClass: + case clang::Stmt::UnresolvedMemberExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedMemberExprClass"); return ErrorUnexpected; - case Stmt::PackExpansionExprClass: + case clang::Stmt::PackExpansionExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass"); return ErrorUnexpected; - case Stmt::ParenListExprClass: + case clang::Stmt::ParenListExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass"); return ErrorUnexpected; - case Stmt::PredefinedExprClass: + case clang::Stmt::PredefinedExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C PredefinedExprClass"); return ErrorUnexpected; - case Stmt::PseudoObjectExprClass: + case clang::Stmt::PseudoObjectExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C PseudoObjectExprClass"); return ErrorUnexpected; - case Stmt::ShuffleVectorExprClass: + case clang::Stmt::ShuffleVectorExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ShuffleVectorExprClass"); return ErrorUnexpected; - case Stmt::SizeOfPackExprClass: + case clang::Stmt::SizeOfPackExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SizeOfPackExprClass"); return ErrorUnexpected; - case Stmt::StmtExprClass: + case clang::Stmt::StmtExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C StmtExprClass"); return ErrorUnexpected; - case Stmt::SubstNonTypeTemplateParmExprClass: + case clang::Stmt::SubstNonTypeTemplateParmExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmExprClass"); return ErrorUnexpected; - case Stmt::SubstNonTypeTemplateParmPackExprClass: + case clang::Stmt::SubstNonTypeTemplateParmPackExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmPackExprClass"); return ErrorUnexpected; - case Stmt::TypeTraitExprClass: + case clang::Stmt::TypeTraitExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C TypeTraitExprClass"); return ErrorUnexpected; - case Stmt::TypoExprClass: + case clang::Stmt::TypoExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C TypoExprClass"); return ErrorUnexpected; - case Stmt::VAArgExprClass: + case clang::Stmt::VAArgExprClass: emit_warning(c, stmt->getLocStart(), "TODO handle C VAArgExprClass"); return ErrorUnexpected; - case Stmt::GotoStmtClass: + case clang::Stmt::GotoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C GotoStmtClass"); return ErrorUnexpected; - case Stmt::IndirectGotoStmtClass: + case clang::Stmt::IndirectGotoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C IndirectGotoStmtClass"); return ErrorUnexpected; - case Stmt::LabelStmtClass: + case clang::Stmt::LabelStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C LabelStmtClass"); return ErrorUnexpected; - case Stmt::MSDependentExistsStmtClass: + case clang::Stmt::MSDependentExistsStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C MSDependentExistsStmtClass"); return ErrorUnexpected; - case Stmt::OMPAtomicDirectiveClass: + case clang::Stmt::OMPAtomicDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPAtomicDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPBarrierDirectiveClass: + case clang::Stmt::OMPBarrierDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPBarrierDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPCancelDirectiveClass: + case clang::Stmt::OMPCancelDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancelDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPCancellationPointDirectiveClass: + case clang::Stmt::OMPCancellationPointDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancellationPointDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPCriticalDirectiveClass: + case clang::Stmt::OMPCriticalDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCriticalDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPFlushDirectiveClass: + case clang::Stmt::OMPFlushDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPFlushDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeDirectiveClass: + case clang::Stmt::OMPDistributeDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeParallelForDirectiveClass: + case clang::Stmt::OMPDistributeParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeParallelForSimdDirectiveClass: + case clang::Stmt::OMPDistributeParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPDistributeSimdDirectiveClass: + case clang::Stmt::OMPDistributeSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPForDirectiveClass: + case clang::Stmt::OMPForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPForSimdDirectiveClass: + case clang::Stmt::OMPForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelForDirectiveClass: + case clang::Stmt::OMPParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelForSimdDirectiveClass: + case clang::Stmt::OMPParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSimdDirectiveClass: + case clang::Stmt::OMPSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetParallelForSimdDirectiveClass: + case clang::Stmt::OMPTargetParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetSimdDirectiveClass: + case clang::Stmt::OMPTargetSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + case clang::Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskLoopDirectiveClass: + case clang::Stmt::OMPTaskLoopDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskLoopSimdDirectiveClass: + case clang::Stmt::OMPTaskLoopSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeDirectiveClass: + case clang::Stmt::OMPTeamsDistributeDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case clang::Stmt::OMPTeamsDistributeParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case clang::Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case clang::Stmt::OMPTeamsDistributeSimdDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeSimdDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPMasterDirectiveClass: + case clang::Stmt::OMPMasterDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPMasterDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPOrderedDirectiveClass: + case clang::Stmt::OMPOrderedDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPOrderedDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelDirectiveClass: + case clang::Stmt::OMPParallelDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPParallelSectionsDirectiveClass: + case clang::Stmt::OMPParallelSectionsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelSectionsDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSectionDirectiveClass: + case clang::Stmt::OMPSectionDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSectionsDirectiveClass: + case clang::Stmt::OMPSectionsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionsDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPSingleDirectiveClass: + case clang::Stmt::OMPSingleDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSingleDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetDataDirectiveClass: + case clang::Stmt::OMPTargetDataDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDataDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetDirectiveClass: + case clang::Stmt::OMPTargetDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetEnterDataDirectiveClass: + case clang::Stmt::OMPTargetEnterDataDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetEnterDataDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetExitDataDirectiveClass: + case clang::Stmt::OMPTargetExitDataDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetExitDataDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetParallelDirectiveClass: + case clang::Stmt::OMPTargetParallelDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetParallelForDirectiveClass: + case clang::Stmt::OMPTargetParallelForDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetTeamsDirectiveClass: + case clang::Stmt::OMPTargetTeamsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTargetUpdateDirectiveClass: + case clang::Stmt::OMPTargetUpdateDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetUpdateDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskDirectiveClass: + case clang::Stmt::OMPTaskDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskgroupDirectiveClass: + case clang::Stmt::OMPTaskgroupDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskgroupDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskwaitDirectiveClass: + case clang::Stmt::OMPTaskwaitDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskwaitDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTaskyieldDirectiveClass: + case clang::Stmt::OMPTaskyieldDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskyieldDirectiveClass"); return ErrorUnexpected; - case Stmt::OMPTeamsDirectiveClass: + case clang::Stmt::OMPTeamsDirectiveClass: emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDirectiveClass"); return ErrorUnexpected; - case Stmt::ObjCAtCatchStmtClass: + case clang::Stmt::ObjCAtCatchStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtCatchStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtFinallyStmtClass: + case clang::Stmt::ObjCAtFinallyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtFinallyStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtSynchronizedStmtClass: + case clang::Stmt::ObjCAtSynchronizedStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtSynchronizedStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtThrowStmtClass: + case clang::Stmt::ObjCAtThrowStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtThrowStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAtTryStmtClass: + case clang::Stmt::ObjCAtTryStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtTryStmtClass"); return ErrorUnexpected; - case Stmt::ObjCAutoreleasePoolStmtClass: + case clang::Stmt::ObjCAutoreleasePoolStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAutoreleasePoolStmtClass"); return ErrorUnexpected; - case Stmt::ObjCForCollectionStmtClass: + case clang::Stmt::ObjCForCollectionStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCForCollectionStmtClass"); return ErrorUnexpected; - case Stmt::SEHExceptStmtClass: + case clang::Stmt::SEHExceptStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHExceptStmtClass"); return ErrorUnexpected; - case Stmt::SEHFinallyStmtClass: + case clang::Stmt::SEHFinallyStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHFinallyStmtClass"); return ErrorUnexpected; - case Stmt::SEHLeaveStmtClass: + case clang::Stmt::SEHLeaveStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHLeaveStmtClass"); return ErrorUnexpected; - case Stmt::SEHTryStmtClass: + case clang::Stmt::SEHTryStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C SEHTryStmtClass"); return ErrorUnexpected; - case Stmt::FixedPointLiteralClass: + case clang::Stmt::FixedPointLiteralClass: emit_warning(c, stmt->getLocStart(), "TODO handle C FixedPointLiteralClass"); return ErrorUnexpected; } @@ -3679,7 +3677,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, } // Returns null if there was an error -static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, +static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval) { AstNode *result_node; @@ -3692,7 +3690,7 @@ static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope // Statements have no result and no concept of L or R value. // Returns child scope, or null if there was an error -static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node) { +static TransScope *trans_stmt(Context *c, TransScope *scope, const clang::Stmt *stmt, AstNode **out_node) { TransScope *child_scope; if (trans_stmt_extra(c, scope, stmt, ResultUsedNo, TransRValue, out_node, &child_scope, nullptr)) { return nullptr; @@ -3700,7 +3698,7 @@ static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, A return child_scope; } -static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { +static void visit_fn_decl(Context *c, const clang::FunctionDecl *fn_decl) { Buf *fn_name = buf_create_from_str(decl_name(fn_decl)); if (get_global(c, fn_name)) { @@ -3717,13 +3715,13 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { proto_node->data.fn_proto.name = fn_name; proto_node->data.fn_proto.is_extern = !fn_decl->hasBody(); - StorageClass sc = fn_decl->getStorageClass(); - if (sc == SC_None) { + clang::StorageClass sc = fn_decl->getStorageClass(); + if (sc == clang::SC_None) { proto_node->data.fn_proto.visib_mod = c->visib_mod; proto_node->data.fn_proto.is_export = fn_decl->hasBody() ? c->want_export : false; - } else if (sc == SC_Extern || sc == SC_Static) { + } else if (sc == clang::SC_Extern || sc == clang::SC_Static) { proto_node->data.fn_proto.visib_mod = c->visib_mod; - } else if (sc == SC_PrivateExtern) { + } else if (sc == clang::SC_PrivateExtern) { emit_warning(c, fn_decl->getLocation(), "unsupported storage class: private extern"); return; } else { @@ -3735,7 +3733,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) { AstNode *param_node = proto_node->data.fn_proto.params.at(i); - const ParmVarDecl *param = fn_decl->getParamDecl(i); + const clang::ParmVarDecl *param = fn_decl->getParamDecl(i); const char *name = decl_name(param); Buf *proto_param_name; @@ -3762,7 +3760,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { // actual function definition with body c->ptr_params.clear(); - Stmt *body = fn_decl->getBody(); + clang::Stmt *body = fn_decl->getBody(); AstNode *actual_body_node; TransScope *result_scope = trans_stmt(c, scope, body, &actual_body_node); if (result_scope == nullptr) { @@ -3804,19 +3802,19 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { add_top_level_decl(c, fn_def_node->data.fn_def.fn_proto->data.fn_proto.name, fn_def_node); } -static AstNode *resolve_typdef_as_builtin(Context *c, const TypedefNameDecl *typedef_decl, const char *primitive_name) { +static AstNode *resolve_typdef_as_builtin(Context *c, const clang::TypedefNameDecl *typedef_decl, const char *primitive_name) { AstNode *node = trans_create_node_symbol_str(c, primitive_name); c->decl_table.put(typedef_decl, node); return node; } -static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl) { +static AstNode *resolve_typedef_decl(Context *c, const clang::TypedefNameDecl *typedef_decl) { auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; } - QualType child_qt = typedef_decl->getUnderlyingType(); + clang::QualType child_qt = typedef_decl->getUnderlyingType(); Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); if (buf_eql_str(type_name, "uint8_t")) { @@ -3865,7 +3863,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ return symbol_node; } -struct AstNode *demote_enum_to_opaque(Context *c, const EnumDecl *enum_decl, +struct AstNode *demote_enum_to_opaque(Context *c, const clang::EnumDecl *enum_decl, Buf *full_type_name, Buf *bare_name) { AstNode *opaque_node = trans_create_node_opaque(c); @@ -3880,7 +3878,7 @@ struct AstNode *demote_enum_to_opaque(Context *c, const EnumDecl *enum_decl, return symbol_node; } -static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { +static AstNode *resolve_enum_decl(Context *c, const clang::EnumDecl *enum_decl) { auto existing_entry = c->decl_table.maybe_get((void*)enum_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; @@ -3891,7 +3889,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name); Buf *full_type_name = is_anonymous ? nullptr : buf_sprintf("enum_%s", buf_ptr(bare_name)); - const EnumDecl *enum_def = enum_decl->getDefinition(); + const clang::EnumDecl *enum_def = enum_decl->getDefinition(); if (!enum_def) { return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name); } @@ -3903,7 +3901,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { it_end = enum_def->enumerator_end(); it != it_end; ++it, field_count += 1) { - const EnumConstantDecl *enum_const = *it; + const clang::EnumConstantDecl *enum_const = *it; if (enum_const->getInitExpr()) { pure_enum = false; } @@ -3917,8 +3915,8 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { // TODO only emit this tag type if the enum tag type is not the default. // I don't know what the default is, need to figure out how clang is deciding. // it appears to at least be different across gcc/msvc - if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && - !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) + if (!c_is_builtin_type(c, enum_decl->getIntegerType(), clang::BuiltinType::UInt) && + !c_is_builtin_type(c, enum_decl->getIntegerType(), clang::BuiltinType::Int)) { enum_node->data.container_decl.init_arg_expr = tag_int_type; } @@ -3928,7 +3926,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { it_end = enum_def->enumerator_end(); it != it_end; ++it, i += 1) { - const EnumConstantDecl *enum_const = *it; + const clang::EnumConstantDecl *enum_const = *it; Buf *enum_val_name = buf_create_from_str(decl_name(enum_const)); Buf *field_name; @@ -3969,7 +3967,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { } } -static AstNode *demote_struct_to_opaque(Context *c, const RecordDecl *record_decl, +static AstNode *demote_struct_to_opaque(Context *c, const clang::RecordDecl *record_decl, Buf *full_type_name, Buf *bare_name) { AstNode *opaque_node = trans_create_node_opaque(c); @@ -3984,7 +3982,7 @@ static AstNode *demote_struct_to_opaque(Context *c, const RecordDecl *record_dec return symbol_node; } -static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { +static AstNode *resolve_record_decl(Context *c, const clang::RecordDecl *record_decl) { auto existing_entry = c->decl_table.maybe_get((void*)record_decl->getCanonicalDecl()); if (existing_entry) { return existing_entry->value; @@ -4010,7 +4008,7 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { Buf *full_type_name = (bare_name == nullptr) ? nullptr : buf_sprintf("%s_%s", container_kind_name, buf_ptr(bare_name)); - RecordDecl *record_def = record_decl->getDefinition(); + clang::RecordDecl *record_def = record_decl->getDefinition(); if (record_def == nullptr) { return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name); } @@ -4021,7 +4019,7 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { it_end = record_def->field_end(); it != it_end; ++it, field_count += 1) { - const FieldDecl *field_decl = *it; + const clang::FieldDecl *field_decl = *it; if (field_decl->isBitField()) { emit_warning(c, field_decl->getLocation(), "%s %s demoted to opaque type - has bitfield", @@ -4051,7 +4049,7 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { it_end = record_def->field_end(); it != it_end; ++it, i += 1) { - const FieldDecl *field_decl = *it; + const clang::FieldDecl *field_decl = *it; AstNode *field_node = trans_create_node(c, NodeTypeStructField); field_node->data.struct_field.name = buf_create_from_str(decl_name(field_decl)); @@ -4078,13 +4076,13 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) { } } -static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const SourceLocation &source_loc) { +static AstNode *trans_ap_value(Context *c, clang::APValue *ap_value, clang::QualType qt, const clang::SourceLocation &source_loc) { switch (ap_value->getKind()) { - case APValue::Int: + case clang::APValue::Int: return trans_create_node_apint(c, ap_value->getInt()); - case APValue::Uninitialized: + case clang::APValue::Uninitialized: return trans_create_node(c, NodeTypeUndefinedLiteral); - case APValue::Array: { + case clang::APValue::Array: { emit_warning(c, source_loc, "TODO add a test case for this code"); unsigned init_count = ap_value->getArrayInitializedElts(); @@ -4098,10 +4096,10 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const init_node->data.container_init_expr.type = arr_type_node; init_node->data.container_init_expr.kind = ContainerInitKindArray; - QualType child_qt = qt.getTypePtr()->getAsArrayTypeUnsafe()->getElementType(); + clang::QualType child_qt = qt.getTypePtr()->getAsArrayTypeUnsafe()->getElementType(); for (size_t i = 0; i < init_count; i += 1) { - APValue &elem_ap_val = ap_value->getArrayInitializedElt(i); + clang::APValue &elem_ap_val = ap_value->getArrayInitializedElt(i); AstNode *elem_node = trans_ap_value(c, &elem_ap_val, child_qt, source_loc); if (elem_node == nullptr) return nullptr; @@ -4111,7 +4109,7 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const return init_node; } - APValue &filler_ap_val = ap_value->getArrayFiller(); + clang::APValue &filler_ap_val = ap_value->getArrayFiller(); AstNode *filler_node = trans_ap_value(c, &filler_ap_val, child_qt, source_loc); if (filler_node == nullptr) return nullptr; @@ -4139,60 +4137,60 @@ static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const return trans_create_node_bin_op(c, init_node, BinOpTypeArrayCat, rhs_node); } - case APValue::LValue: { - const APValue::LValueBase lval_base = ap_value->getLValueBase(); - if (const Expr *expr = lval_base.dyn_cast()) { + case clang::APValue::LValue: { + const clang::APValue::LValueBase lval_base = ap_value->getLValueBase(); + if (const clang::Expr *expr = lval_base.dyn_cast()) { return trans_expr(c, ResultUsedYes, &c->global_scope->base, expr, TransRValue); } - //const ValueDecl *value_decl = lval_base.get(); - emit_warning(c, source_loc, "TODO handle initializer LValue ValueDecl"); + //const clang::ValueDecl *value_decl = lval_base.get(); + emit_warning(c, source_loc, "TODO handle initializer LValue clang::ValueDecl"); return nullptr; } - case APValue::Float: + case clang::APValue::Float: emit_warning(c, source_loc, "unsupported initializer value kind: Float"); return nullptr; - case APValue::ComplexInt: + case clang::APValue::ComplexInt: emit_warning(c, source_loc, "unsupported initializer value kind: ComplexInt"); return nullptr; - case APValue::ComplexFloat: + case clang::APValue::ComplexFloat: emit_warning(c, source_loc, "unsupported initializer value kind: ComplexFloat"); return nullptr; - case APValue::Vector: + case clang::APValue::Vector: emit_warning(c, source_loc, "unsupported initializer value kind: Vector"); return nullptr; - case APValue::Struct: + case clang::APValue::Struct: emit_warning(c, source_loc, "unsupported initializer value kind: Struct"); return nullptr; - case APValue::Union: + case clang::APValue::Union: emit_warning(c, source_loc, "unsupported initializer value kind: Union"); return nullptr; - case APValue::MemberPointer: + case clang::APValue::MemberPointer: emit_warning(c, source_loc, "unsupported initializer value kind: MemberPointer"); return nullptr; - case APValue::AddrLabelDiff: + case clang::APValue::AddrLabelDiff: emit_warning(c, source_loc, "unsupported initializer value kind: AddrLabelDiff"); return nullptr; } zig_unreachable(); } -static void visit_var_decl(Context *c, const VarDecl *var_decl) { +static void visit_var_decl(Context *c, const clang::VarDecl *var_decl) { Buf *name = buf_create_from_str(decl_name(var_decl)); switch (var_decl->getTLSKind()) { - case VarDecl::TLS_None: + case clang::VarDecl::TLS_None: break; - case VarDecl::TLS_Static: + case clang::VarDecl::TLS_Static: emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - static thread local storage", buf_ptr(name)); return; - case VarDecl::TLS_Dynamic: + case clang::VarDecl::TLS_Dynamic: emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - dynamic thread local storage", buf_ptr(name)); return; } - QualType qt = var_decl->getType(); + clang::QualType qt = var_decl->getType(); AstNode *var_type = trans_qual_type(c, qt, var_decl->getLocation()); if (var_type == nullptr) { emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unresolved type", buf_ptr(name)); @@ -4206,7 +4204,7 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { if (is_static && !is_extern) { AstNode *init_node; if (var_decl->hasInit()) { - APValue *ap_value = var_decl->evaluateValue(); + clang::APValue *ap_value = var_decl->evaluateValue(); if (ap_value == nullptr) { emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name)); @@ -4236,24 +4234,24 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) { return; } -static bool decl_visitor(void *context, const Decl *decl) { +static bool decl_visitor(void *context, const clang::Decl *decl) { Context *c = (Context*)context; switch (decl->getKind()) { - case Decl::Function: - visit_fn_decl(c, static_cast(decl)); + case clang::Decl::Function: + visit_fn_decl(c, static_cast(decl)); break; - case Decl::Typedef: - resolve_typedef_decl(c, static_cast(decl)); + case clang::Decl::Typedef: + resolve_typedef_decl(c, static_cast(decl)); break; - case Decl::Enum: - resolve_enum_decl(c, static_cast(decl)); + case clang::Decl::Enum: + resolve_enum_decl(c, static_cast(decl)); break; - case Decl::Record: - resolve_record_decl(c, static_cast(decl)); + case clang::Decl::Record: + resolve_record_decl(c, static_cast(decl)); break; - case Decl::Var: - visit_var_decl(c, static_cast(decl)); + case clang::Decl::Var: + visit_var_decl(c, static_cast(decl)); break; default: emit_warning(c, decl->getLocation(), "ignoring %s decl", decl->getDeclKindName()); @@ -4674,24 +4672,24 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch c->macro_table.put(name, result_node); } -static void process_preprocessor_entities(Context *c, ASTUnit &unit) { +static void process_preprocessor_entities(Context *c, clang::ASTUnit &unit) { CTokenize ctok = {{0}}; // TODO if we see #undef, delete it from the table - for (PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) { + for (clang::PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) { switch (entity->getKind()) { - case PreprocessedEntity::InvalidKind: - case PreprocessedEntity::InclusionDirectiveKind: - case PreprocessedEntity::MacroExpansionKind: + case clang::PreprocessedEntity::InvalidKind: + case clang::PreprocessedEntity::InclusionDirectiveKind: + case clang::PreprocessedEntity::MacroExpansionKind: continue; - case PreprocessedEntity::MacroDefinitionKind: + case clang::PreprocessedEntity::MacroDefinitionKind: { - MacroDefinitionRecord *macro = static_cast(entity); + clang::MacroDefinitionRecord *macro = static_cast(entity); const char *raw_name = macro->getName()->getNameStart(); - SourceRange range = macro->getSourceRange(); - SourceLocation begin_loc = range.getBegin(); - SourceLocation end_loc = range.getEnd(); + clang::SourceRange range = macro->getSourceRange(); + clang::SourceLocation begin_loc = range.getBegin(); + clang::SourceLocation end_loc = range.getEnd(); if (begin_loc == end_loc) { // this means it is a macro without a value @@ -4816,9 +4814,9 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const // to make the [start...end] argument work clang_argv.append(nullptr); - IntrusiveRefCntPtr diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + clang::IntrusiveRefCntPtr diags(clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions)); - std::shared_ptr pch_container_ops = std::make_shared(); + std::shared_ptr pch_container_ops = std::make_shared(); bool only_local_decls = true; bool capture_diagnostics = true; @@ -4827,13 +4825,13 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const bool single_file_parse = false; bool for_serialization = false; const char *resources_path = buf_ptr(codegen->zig_c_headers_dir); - std::unique_ptr err_unit; - std::unique_ptr ast_unit(ASTUnit::LoadFromCommandLine( + std::unique_ptr err_unit; + std::unique_ptr ast_unit(clang::ASTUnit::LoadFromCommandLine( &clang_argv.at(0), &clang_argv.last(), pch_container_ops, diags, resources_path, - only_local_decls, capture_diagnostics, None, true, 0, TU_Complete, - false, false, allow_pch_with_compiler_errors, SkipFunctionBodiesScope::None, - single_file_parse, user_files_are_volatile, for_serialization, None, &err_unit, + only_local_decls, capture_diagnostics, clang::None, true, 0, clang::TU_Complete, + false, false, allow_pch_with_compiler_errors, clang::SkipFunctionBodiesScope::None, + single_file_parse, user_files_are_volatile, for_serialization, clang::None, &err_unit, nullptr)); // Early failures in LoadFromCommandLine may return with ErrUnit unset. @@ -4846,26 +4844,26 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const err_unit = std::move(ast_unit); } - for (ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), + for (clang::ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), it_end = err_unit->stored_diag_end(); it != it_end; ++it) { switch (it->getLevel()) { - case DiagnosticsEngine::Ignored: - case DiagnosticsEngine::Note: - case DiagnosticsEngine::Remark: - case DiagnosticsEngine::Warning: + case clang::DiagnosticsEngine::Ignored: + case clang::DiagnosticsEngine::Note: + case clang::DiagnosticsEngine::Remark: + case clang::DiagnosticsEngine::Warning: continue; - case DiagnosticsEngine::Error: - case DiagnosticsEngine::Fatal: + case clang::DiagnosticsEngine::Error: + case clang::DiagnosticsEngine::Fatal: break; } StringRef msg_str_ref = it->getMessage(); Buf *msg = string_ref_to_buf(msg_str_ref); - FullSourceLoc fsl = it->getLocation(); + clang::FullSourceLoc fsl = it->getLocation(); if (fsl.hasManager()) { - FileID file_id = fsl.getFileID(); - StringRef filename = fsl.getManager().getFilename(fsl); + clang::FileID file_id = fsl.getFileID(); + clang::StringRef filename = fsl.getManager().getFilename(fsl); unsigned line = fsl.getSpellingLineNumber() - 1; unsigned column = fsl.getSpellingColumnNumber() - 1; unsigned offset = fsl.getManager().getFileOffset(fsl); From bd52d81dc316df601877a17a9c70caf3a49f936d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 14:36:28 -0500 Subject: [PATCH 054/134] README: direct link to Download & Documentation at the top --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b64d75c11..3ac4f429f2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A programming language designed for robustness, optimality, and clarity. -[ziglang.org](https://ziglang.org) +[Download & Documentation](https://ziglang.org/download/) ## Feature Highlights From 356cfa08f49a9e92dfa4135cbcf2ddc079ddf228 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 15:14:51 -0500 Subject: [PATCH 055/134] translate-c: proof of concept for transitioning to userland See #1964 --- CMakeLists.txt | 1 + src/translate_c.cpp | 22 ++-- src/zig_clang.cpp | 178 +++++++++++++++++++++++++++++++ src/zig_clang.h | 253 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 src/zig_clang.cpp create mode 100644 src/zig_clang.h diff --git a/CMakeLists.txt b/CMakeLists.txt index db5f50908c..b633e01978 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -429,6 +429,7 @@ set(BLAKE_SOURCES ) set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp" "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 7d618466b2..7462c8e046 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -14,7 +14,6 @@ #include "translate_c.hpp" #include "parser.hpp" - #if __GNUC__ >= 8 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" @@ -28,6 +27,13 @@ #pragma GCC diagnostic pop #endif + +// Before the #include of zig_clang.h +// Temporary transitional thing: override ZigClangSourceLocation with clang::SourceLocation +#define ZigClangSourceLocation clang::SourceLocation +#define ZIG_CLANG_SOURCE_LOCATION ZigClangABISourceLocation +#include "zig_clang.h" + #include struct Alias { @@ -85,7 +91,7 @@ struct Context { HashMap decl_table; HashMap macro_table; HashMap global_table; - clang::SourceManager *source_manager; + ZigClangSourceManager *source_manager; ZigList aliases; AstNode *source_node; bool warnings_on; @@ -139,16 +145,16 @@ static void emit_warning(Context *c, const clang::SourceLocation &sl, const char Buf *msg = buf_vprintf(format, ap); va_end(ap); - StringRef filename = c->source_manager->getFilename(c->source_manager->getSpellingLoc(sl)); - const char *filename_bytes = (const char *)filename.bytes_begin(); + const char *filename_bytes = ZigClangSourceManager_getFilename(c->source_manager, + ZigClangSourceManager_getSpellingLoc(c->source_manager, sl)); Buf *path; if (filename_bytes) { path = buf_create_from_str(filename_bytes); } else { path = buf_sprintf("(no file)"); } - unsigned line = c->source_manager->getSpellingLineNumber(sl); - unsigned column = c->source_manager->getSpellingColumnNumber(sl); + unsigned line = ZigClangSourceManager_getSpellingLineNumber(c->source_manager, sl); + unsigned column = ZigClangSourceManager_getSpellingColumnNumber(c->source_manager, sl); fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg)); } @@ -4701,7 +4707,7 @@ static void process_preprocessor_entities(Context *c, clang::ASTUnit &unit) { continue; } - const char *begin_c = c->source_manager->getCharacterData(begin_loc); + const char *begin_c = ZigClangSourceManager_getCharacterData(c->source_manager, begin_loc); process_macro(c, &ctok, name, begin_c); } } @@ -4889,7 +4895,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const } c->ctx = &ast_unit->getASTContext(); - c->source_manager = &ast_unit->getSourceManager(); + c->source_manager = reinterpret_cast(&ast_unit->getSourceManager()); c->root = trans_create_node(c, NodeTypeContainerDecl); c->root->data.container_decl.is_root = true; diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp new file mode 100644 index 0000000000..62d1e34658 --- /dev/null +++ b/src/zig_clang.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + + +/* + * The point of this file is to contain all the Clang C++ API interaction so that: + * 1. The compile time of other files is kept under control. + * 2. Provide a C interface to the Clang functions we need for self-hosting purposes. + * 3. Prevent C++ from infecting the rest of the project. + */ + +#if __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + +#include +#include +#include + +#if __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + +// Before the #include of zig_clang.h +// We'll check that the types are compatible but just use +// the clang type. +#define ZigClangSourceLocation clang::SourceLocation +#define ZIG_CLANG_SOURCE_LOCATION ZigClangABISourceLocation + +#include "zig_clang.h" + +// Detect additions to the enum +void zig2clang_BO(ZigClangBO op) { + switch (op) { + case ZigClangBO_PtrMemD: + case ZigClangBO_PtrMemI: + case ZigClangBO_Cmp: + case ZigClangBO_Mul: + case ZigClangBO_Div: + case ZigClangBO_Rem: + case ZigClangBO_Add: + case ZigClangBO_Sub: + case ZigClangBO_Shl: + case ZigClangBO_Shr: + case ZigClangBO_LT: + case ZigClangBO_GT: + case ZigClangBO_LE: + case ZigClangBO_GE: + case ZigClangBO_EQ: + case ZigClangBO_NE: + case ZigClangBO_And: + case ZigClangBO_Xor: + case ZigClangBO_Or: + case ZigClangBO_LAnd: + case ZigClangBO_LOr: + case ZigClangBO_Assign: + case ZigClangBO_Comma: + case ZigClangBO_MulAssign: + case ZigClangBO_DivAssign: + case ZigClangBO_RemAssign: + case ZigClangBO_AddAssign: + case ZigClangBO_SubAssign: + case ZigClangBO_ShlAssign: + case ZigClangBO_ShrAssign: + case ZigClangBO_AndAssign: + case ZigClangBO_XorAssign: + case ZigClangBO_OrAssign: + break; + } +} + +static_assert((clang::BinaryOperatorKind)ZigClangBO_Add == clang::BO_Add, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_AddAssign == clang::BO_AddAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_And == clang::BO_And, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_AndAssign == clang::BO_AndAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Assign == clang::BO_Assign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Cmp == clang::BO_Cmp, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Comma == clang::BO_Comma, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Div == clang::BO_Div, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_DivAssign == clang::BO_DivAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_EQ == clang::BO_EQ, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_GE == clang::BO_GE, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_GT == clang::BO_GT, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LAnd == clang::BO_LAnd, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LE == clang::BO_LE, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LOr == clang::BO_LOr, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_LT == clang::BO_LT, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Mul == clang::BO_Mul, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_MulAssign == clang::BO_MulAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_NE == clang::BO_NE, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Or == clang::BO_Or, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_OrAssign == clang::BO_OrAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_PtrMemD == clang::BO_PtrMemD, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_PtrMemI == clang::BO_PtrMemI, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Rem == clang::BO_Rem, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_RemAssign == clang::BO_RemAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Shl == clang::BO_Shl, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_ShlAssign == clang::BO_ShlAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Shr == clang::BO_Shr, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_ShrAssign == clang::BO_ShrAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Sub == clang::BO_Sub, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_SubAssign == clang::BO_SubAssign, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_Xor == clang::BO_Xor, ""); +static_assert((clang::BinaryOperatorKind)ZigClangBO_XorAssign == clang::BO_XorAssign, ""); + +// This function detects additions to the enum +void zig2clang_UO(ZigClangUO op) { + switch (op) { + case ZigClangUO_AddrOf: + case ZigClangUO_Coawait: + case ZigClangUO_Deref: + case ZigClangUO_Extension: + case ZigClangUO_Imag: + case ZigClangUO_LNot: + case ZigClangUO_Minus: + case ZigClangUO_Not: + case ZigClangUO_Plus: + case ZigClangUO_PostDec: + case ZigClangUO_PostInc: + case ZigClangUO_PreDec: + case ZigClangUO_PreInc: + case ZigClangUO_Real: + break; + } +} + +static_assert((clang::UnaryOperatorKind)ZigClangUO_AddrOf == clang::UO_AddrOf, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Coawait == clang::UO_Coawait, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Deref == clang::UO_Deref, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Extension == clang::UO_Extension, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Imag == clang::UO_Imag, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_LNot == clang::UO_LNot, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Minus == clang::UO_Minus, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Not == clang::UO_Not, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Plus == clang::UO_Plus, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PostDec == clang::UO_PostDec, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PostInc == clang::UO_PostInc, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PreDec == clang::UO_PreDec, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_PreInc == clang::UO_PreInc, ""); +static_assert((clang::UnaryOperatorKind)ZigClangUO_Real == clang::UO_Real, ""); + +static_assert(sizeof(ZigClangABISourceLocation) == sizeof(clang::SourceLocation)); + +clang::SourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *self, + clang::SourceLocation Loc) +{ + return reinterpret_cast(self)->getSpellingLoc(Loc); +} + +const char *ZigClangSourceManager_getFilename(const ZigClangSourceManager *self, + clang::SourceLocation SpellingLoc) +{ + StringRef s = reinterpret_cast(self)->getFilename(SpellingLoc); + return (const char *)s.bytes_begin(); +} + +unsigned ZigClangSourceManager_getSpellingLineNumber(const ZigClangSourceManager *self, + ZigClangSourceLocation Loc) +{ + return reinterpret_cast(self)->getSpellingLineNumber(Loc); +} + +unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigClangSourceManager *self, + ZigClangSourceLocation Loc) +{ + return reinterpret_cast(self)->getSpellingColumnNumber(Loc); +} + +const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *self, + ZigClangSourceLocation SL) +{ + return reinterpret_cast(self)->getCharacterData(SL); +} diff --git a/src/zig_clang.h b/src/zig_clang.h new file mode 100644 index 0000000000..840d227bb4 --- /dev/null +++ b/src/zig_clang.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_ZIG_CLANG_H +#define ZIG_ZIG_CLANG_H + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +// ATTENTION: If you modify this file, be sure to update the corresponding +// extern function declarations in the self-hosted compiler. + +#ifndef ZIG_CLANG_SOURCE_LOCATION +#define ZIG_CLANG_SOURCE_LOCATION ZigClangSourceLocation +#endif + +struct ZIG_CLANG_SOURCE_LOCATION { + unsigned ID; +}; + +struct ZigClangAPValue; +struct ZigClangASTContext; +struct ZigClangASTUnit; +struct ZigClangArraySubscriptExpr; +struct ZigClangArrayType; +struct ZigClangAttributedType; +struct ZigClangBinaryOperator; +struct ZigClangBreakStmt; +struct ZigClangBuiltinType; +struct ZigClangCStyleCastExpr; +struct ZigClangCallExpr; +struct ZigClangCaseStmt; +struct ZigClangCompoundAssignOperator; +struct ZigClangCompoundStmt; +struct ZigClangConditionalOperator; +struct ZigClangConstantArrayType; +struct ZigClangContinueStmt; +struct ZigClangDecayedType; +struct ZigClangDecl; +struct ZigClangDeclRefExpr; +struct ZigClangDeclStmt; +struct ZigClangDefaultStmt; +struct ZigClangDiagnosticOptions; +struct ZigClangDiagnosticsEngine; +struct ZigClangDoStmt; +struct ZigClangElaboratedType; +struct ZigClangEnumConstantDecl; +struct ZigClangEnumDecl; +struct ZigClangEnumType; +struct ZigClangExpr; +struct ZigClangFieldDecl; +struct ZigClangFileID; +struct ZigClangForStmt; +struct ZigClangFullSourceLoc; +struct ZigClangFunctionDecl; +struct ZigClangFunctionProtoType; +struct ZigClangIfStmt; +struct ZigClangImplicitCastExpr; +struct ZigClangIncompleteArrayType; +struct ZigClangIntegerLiteral; +struct ZigClangMacroDefinitionRecord; +struct ZigClangMemberExpr; +struct ZigClangNamedDecl; +struct ZigClangNone; +struct ZigClangPCHContainerOperations; +struct ZigClangParenExpr; +struct ZigClangParenType; +struct ZigClangParmVarDecl; +struct ZigClangPointerType; +struct ZigClangPreprocessedEntity; +struct ZigClangQualType; +struct ZigClangRecordDecl; +struct ZigClangRecordType; +struct ZigClangReturnStmt; +struct ZigClangSkipFunctionBodiesScope; +struct ZigClangSourceManager; +struct ZigClangSourceRange; +struct ZigClangStmt; +struct ZigClangStorageClass; +struct ZigClangStringLiteral; +struct ZigClangStringRef; +struct ZigClangSwitchStmt; +struct ZigClangType; +struct ZigClangTypedefNameDecl; +struct ZigClangTypedefType; +struct ZigClangUnaryExprOrTypeTraitExpr; +struct ZigClangUnaryOperator; +struct ZigClangValueDecl; +struct ZigClangVarDecl; +struct ZigClangWhileStmt; + +enum ZigClangBO { + ZigClangBO_PtrMemD, + ZigClangBO_PtrMemI, + ZigClangBO_Mul, + ZigClangBO_Div, + ZigClangBO_Rem, + ZigClangBO_Add, + ZigClangBO_Sub, + ZigClangBO_Shl, + ZigClangBO_Shr, + ZigClangBO_Cmp, + ZigClangBO_LT, + ZigClangBO_GT, + ZigClangBO_LE, + ZigClangBO_GE, + ZigClangBO_EQ, + ZigClangBO_NE, + ZigClangBO_And, + ZigClangBO_Xor, + ZigClangBO_Or, + ZigClangBO_LAnd, + ZigClangBO_LOr, + ZigClangBO_Assign, + ZigClangBO_MulAssign, + ZigClangBO_DivAssign, + ZigClangBO_RemAssign, + ZigClangBO_AddAssign, + ZigClangBO_SubAssign, + ZigClangBO_ShlAssign, + ZigClangBO_ShrAssign, + ZigClangBO_AndAssign, + ZigClangBO_XorAssign, + ZigClangBO_OrAssign, + ZigClangBO_Comma, +}; + +enum ZigClangUO { + ZigClangUO_PostInc, + ZigClangUO_PostDec, + ZigClangUO_PreInc, + ZigClangUO_PreDec, + ZigClangUO_AddrOf, + ZigClangUO_Deref, + ZigClangUO_Plus, + ZigClangUO_Minus, + ZigClangUO_Not, + ZigClangUO_LNot, + ZigClangUO_Real, + ZigClangUO_Imag, + ZigClangUO_Extension, + ZigClangUO_Coawait, +}; + +//struct ZigClangCC_AAPCS; +//struct ZigClangCC_AAPCS_VFP; +//struct ZigClangCC_C; +//struct ZigClangCC_IntelOclBicc; +//struct ZigClangCC_OpenCLKernel; +//struct ZigClangCC_PreserveAll; +//struct ZigClangCC_PreserveMost; +//struct ZigClangCC_SpirFunction; +//struct ZigClangCC_Swift; +//struct ZigClangCC_Win64; +//struct ZigClangCC_X86FastCall; +//struct ZigClangCC_X86Pascal; +//struct ZigClangCC_X86RegCall; +//struct ZigClangCC_X86StdCall; +//struct ZigClangCC_X86ThisCall; +//struct ZigClangCC_X86VectorCall; +//struct ZigClangCC_X86_64SysV; + +//struct ZigClangCK_ARCConsumeObject; +//struct ZigClangCK_ARCExtendBlockObject; +//struct ZigClangCK_ARCProduceObject; +//struct ZigClangCK_ARCReclaimReturnedObject; +//struct ZigClangCK_AddressSpaceConversion; +//struct ZigClangCK_AnyPointerToBlockPointerCast; +//struct ZigClangCK_ArrayToPointerDecay; +//struct ZigClangCK_AtomicToNonAtomic; +//struct ZigClangCK_BaseToDerived; +//struct ZigClangCK_BaseToDerivedMemberPointer; +//struct ZigClangCK_BitCast; +//struct ZigClangCK_BlockPointerToObjCPointerCast; +//struct ZigClangCK_BooleanToSignedIntegral; +//struct ZigClangCK_BuiltinFnToFnPtr; +//struct ZigClangCK_CPointerToObjCPointerCast; +//struct ZigClangCK_ConstructorConversion; +//struct ZigClangCK_CopyAndAutoreleaseBlockObject; +//struct ZigClangCK_Dependent; +//struct ZigClangCK_DerivedToBase; +//struct ZigClangCK_DerivedToBaseMemberPointer; +//struct ZigClangCK_Dynamic; +//struct ZigClangCK_FloatingCast; +//struct ZigClangCK_FloatingComplexCast; +//struct ZigClangCK_FloatingComplexToBoolean; +//struct ZigClangCK_FloatingComplexToIntegralComplex; +//struct ZigClangCK_FloatingComplexToReal; +//struct ZigClangCK_FloatingRealToComplex; +//struct ZigClangCK_FloatingToBoolean; +//struct ZigClangCK_FloatingToIntegral; +//struct ZigClangCK_FunctionToPointerDecay; +//struct ZigClangCK_IntToOCLSampler; +//struct ZigClangCK_IntegralCast; +//struct ZigClangCK_IntegralComplexCast; +//struct ZigClangCK_IntegralComplexToBoolean; +//struct ZigClangCK_IntegralComplexToFloatingComplex; +//struct ZigClangCK_IntegralComplexToReal; +//struct ZigClangCK_IntegralRealToComplex; +//struct ZigClangCK_IntegralToBoolean; +//struct ZigClangCK_IntegralToFloating; +//struct ZigClangCK_IntegralToPointer; +//struct ZigClangCK_LValueBitCast; +//struct ZigClangCK_LValueToRValue; +//struct ZigClangCK_MemberPointerToBoolean; +//struct ZigClangCK_NoOp; +//struct ZigClangCK_NonAtomicToAtomic; +//struct ZigClangCK_NullToMemberPointer; +//struct ZigClangCK_NullToPointer; +//struct ZigClangCK_ObjCObjectLValueCast; +//struct ZigClangCK_PointerToBoolean; +//struct ZigClangCK_PointerToIntegral; +//struct ZigClangCK_ReinterpretMemberPointer; +//struct ZigClangCK_ToUnion; +//struct ZigClangCK_ToVoid; +//struct ZigClangCK_UncheckedDerivedToBase; +//struct ZigClangCK_UserDefinedConversion; +//struct ZigClangCK_VectorSplat; +//struct ZigClangCK_ZeroToOCLEvent; +//struct ZigClangCK_ZeroToOCLQueue; + +//struct ZigClangETK_Class; +//struct ZigClangETK_Enum; +//struct ZigClangETK_Interface; +//struct ZigClangETK_None; +//struct ZigClangETK_Struct; +//struct ZigClangETK_Typename; +//struct ZigClangETK_Union; + +//struct ZigClangSC_None; +//struct ZigClangSC_PrivateExtern; +//struct ZigClangSC_Static; + +//struct ZigClangTU_Complete; + +ZIG_EXTERN_C ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *, + ZigClangSourceLocation Loc); +ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const ZigClangSourceManager *, + ZigClangSourceLocation SpellingLoc); +ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingLineNumber(const ZigClangSourceManager *, + ZigClangSourceLocation Loc); +ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigClangSourceManager *, + ZigClangSourceLocation Loc); +ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *, + ZigClangSourceLocation SL); +#endif From 2dfa76a1a754d2537b9cc6faf7b98461de0b1429 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 16:17:30 -0500 Subject: [PATCH 056/134] fix regressions from previous commit when building with clang --- src/translate_c.cpp | 20 ++++++++++---------- src/zig_clang.cpp | 39 ++++++++++++++++++++++----------------- src/zig_clang.h | 6 +----- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 7462c8e046..7ebd90e3fe 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4,7 +4,6 @@ * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ - #include "all_types.hpp" #include "analyze.hpp" #include "c_tokenizer.hpp" @@ -13,6 +12,7 @@ #include "os.hpp" #include "translate_c.hpp" #include "parser.hpp" +#include "zig_clang.h" #if __GNUC__ >= 8 #pragma GCC diagnostic push @@ -27,13 +27,6 @@ #pragma GCC diagnostic pop #endif - -// Before the #include of zig_clang.h -// Temporary transitional thing: override ZigClangSourceLocation with clang::SourceLocation -#define ZigClangSourceLocation clang::SourceLocation -#define ZIG_CLANG_SOURCE_LOCATION ZigClangABISourceLocation -#include "zig_clang.h" - #include struct Alias { @@ -134,8 +127,14 @@ static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope static AstNode *trans_qual_type(Context *c, clang::QualType qt, const clang::SourceLocation &source_loc); static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const clang::Expr *expr, TransLRValue lrval); +static ZigClangSourceLocation bitcast(clang::SourceLocation src) { + ZigClangSourceLocation dest; + memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + return dest; +} + ATTRIBUTE_PRINTF(3, 4) -static void emit_warning(Context *c, const clang::SourceLocation &sl, const char *format, ...) { +static void emit_warning(Context *c, const clang::SourceLocation &clang_sl, const char *format, ...) { if (!c->warnings_on) { return; } @@ -145,6 +144,7 @@ static void emit_warning(Context *c, const clang::SourceLocation &sl, const char Buf *msg = buf_vprintf(format, ap); va_end(ap); + ZigClangSourceLocation sl = bitcast(clang_sl); const char *filename_bytes = ZigClangSourceManager_getFilename(c->source_manager, ZigClangSourceManager_getSpellingLoc(c->source_manager, sl)); Buf *path; @@ -4707,7 +4707,7 @@ static void process_preprocessor_entities(Context *c, clang::ASTUnit &unit) { continue; } - const char *begin_c = ZigClangSourceManager_getCharacterData(c->source_manager, begin_loc); + const char *begin_c = ZigClangSourceManager_getCharacterData(c->source_manager, bitcast(begin_loc)); process_macro(c, &ctok, name, begin_c); } } diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 62d1e34658..4e6e4e7a98 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -12,6 +12,7 @@ * 2. Provide a C interface to the Clang functions we need for self-hosting purposes. * 3. Prevent C++ from infecting the rest of the project. */ +#include "zig_clang.h" #if __GNUC__ >= 8 #pragma GCC diagnostic push @@ -26,14 +27,6 @@ #pragma GCC diagnostic pop #endif -// Before the #include of zig_clang.h -// We'll check that the types are compatible but just use -// the clang type. -#define ZigClangSourceLocation clang::SourceLocation -#define ZIG_CLANG_SOURCE_LOCATION ZigClangABISourceLocation - -#include "zig_clang.h" - // Detect additions to the enum void zig2clang_BO(ZigClangBO op) { switch (op) { @@ -144,35 +137,47 @@ static_assert((clang::UnaryOperatorKind)ZigClangUO_PreDec == clang::UO_PreDec, " static_assert((clang::UnaryOperatorKind)ZigClangUO_PreInc == clang::UO_PreInc, ""); static_assert((clang::UnaryOperatorKind)ZigClangUO_Real == clang::UO_Real, ""); -static_assert(sizeof(ZigClangABISourceLocation) == sizeof(clang::SourceLocation)); +static_assert(sizeof(ZigClangSourceLocation) == sizeof(clang::SourceLocation), ""); -clang::SourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *self, - clang::SourceLocation Loc) +static ZigClangSourceLocation bitcast(clang::SourceLocation src) { + ZigClangSourceLocation dest; + memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + return dest; +} + +static clang::SourceLocation bitcast(ZigClangSourceLocation src) { + clang::SourceLocation dest; + memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + return dest; +} + +ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *self, + ZigClangSourceLocation Loc) { - return reinterpret_cast(self)->getSpellingLoc(Loc); + return bitcast(reinterpret_cast(self)->getSpellingLoc(bitcast(Loc))); } const char *ZigClangSourceManager_getFilename(const ZigClangSourceManager *self, - clang::SourceLocation SpellingLoc) + ZigClangSourceLocation SpellingLoc) { - StringRef s = reinterpret_cast(self)->getFilename(SpellingLoc); + StringRef s = reinterpret_cast(self)->getFilename(bitcast(SpellingLoc)); return (const char *)s.bytes_begin(); } unsigned ZigClangSourceManager_getSpellingLineNumber(const ZigClangSourceManager *self, ZigClangSourceLocation Loc) { - return reinterpret_cast(self)->getSpellingLineNumber(Loc); + return reinterpret_cast(self)->getSpellingLineNumber(bitcast(Loc)); } unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigClangSourceManager *self, ZigClangSourceLocation Loc) { - return reinterpret_cast(self)->getSpellingColumnNumber(Loc); + return reinterpret_cast(self)->getSpellingColumnNumber(bitcast(Loc)); } const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *self, ZigClangSourceLocation SL) { - return reinterpret_cast(self)->getCharacterData(SL); + return reinterpret_cast(self)->getCharacterData(bitcast(SL)); } diff --git a/src/zig_clang.h b/src/zig_clang.h index 840d227bb4..78d5cd8cbc 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -17,11 +17,7 @@ // ATTENTION: If you modify this file, be sure to update the corresponding // extern function declarations in the self-hosted compiler. -#ifndef ZIG_CLANG_SOURCE_LOCATION -#define ZIG_CLANG_SOURCE_LOCATION ZigClangSourceLocation -#endif - -struct ZIG_CLANG_SOURCE_LOCATION { +struct ZigClangSourceLocation { unsigned ID; }; From c3c92ca8b1ada4faed14a9770ab7ed6536edaa15 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 19:48:39 -0500 Subject: [PATCH 057/134] translate-c: 4 more functions using C decls See #1964 --- src/translate_c.cpp | 36 ++++++++++++++++++++++++------------ src/zig_clang.cpp | 35 +++++++++++++++++++++++++++++++++-- src/zig_clang.h | 12 +++++++++++- 3 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 7ebd90e3fe..a51a671b06 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -90,7 +90,7 @@ struct Context { bool warnings_on; CodeGen *codegen; - clang::ASTContext *ctx; + ZigClangASTContext *ctx; TransScopeRoot *global_scope; HashMap ptr_params; @@ -132,6 +132,16 @@ static ZigClangSourceLocation bitcast(clang::SourceLocation src) { memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); return dest; } +static ZigClangQualType bitcast(clang::QualType src) { + ZigClangQualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} +static clang::QualType bitcast(ZigClangQualType src) { + clang::QualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} ATTRIBUTE_PRINTF(3, 4) static void emit_warning(Context *c, const clang::SourceLocation &clang_sl, const char *format, ...) { @@ -509,7 +519,7 @@ static clang::QualType get_expr_qual_type(Context *c, const clang::Expr *expr) { const clang::ArrayType *array_type = static_cast(array_qt.getTypePtr()); clang::QualType pointee_qt = array_type->getElementType(); pointee_qt.addConst(); - return c->ctx->getPointerType(pointee_qt); + return bitcast(ZigClangASTContext_getPointerType(c->ctx, bitcast(pointee_qt))); } } } @@ -1221,7 +1231,7 @@ static AstNode *trans_return_stmt(Context *c, TransScope *scope, const clang::Re static AstNode *trans_integer_literal(Context *c, const clang::IntegerLiteral *stmt) { llvm::APSInt result; - if (!stmt->EvaluateAsInt(result, *c->ctx)) { + if (!stmt->EvaluateAsInt(result, *reinterpret_cast(c->ctx))) { emit_warning(c, stmt->getLocStart(), "invalid integer literal"); return nullptr; } @@ -4240,7 +4250,8 @@ static void visit_var_decl(Context *c, const clang::VarDecl *var_decl) { return; } -static bool decl_visitor(void *context, const clang::Decl *decl) { +static bool decl_visitor(void *context, const ZigClangDecl *zdecl) { + const clang::Decl *decl = reinterpret_cast(zdecl); Context *c = (Context*)context; switch (decl->getKind()) { @@ -4678,12 +4689,13 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch c->macro_table.put(name, result_node); } -static void process_preprocessor_entities(Context *c, clang::ASTUnit &unit) { +static void process_preprocessor_entities(Context *c, ZigClangASTUnit *zunit) { + clang::ASTUnit *unit = reinterpret_cast(zunit); CTokenize ctok = {{0}}; // TODO if we see #undef, delete it from the table - for (clang::PreprocessedEntity *entity : unit.getLocalPreprocessingEntities()) { + for (clang::PreprocessedEntity *entity : unit->getLocalPreprocessingEntities()) { switch (entity->getKind()) { case clang::PreprocessedEntity::InvalidKind: case clang::PreprocessedEntity::InclusionDirectiveKind: @@ -4832,7 +4844,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const bool for_serialization = false; const char *resources_path = buf_ptr(codegen->zig_c_headers_dir); std::unique_ptr err_unit; - std::unique_ptr ast_unit(clang::ASTUnit::LoadFromCommandLine( + ZigClangASTUnit *ast_unit = reinterpret_cast(clang::ASTUnit::LoadFromCommandLine( &clang_argv.at(0), &clang_argv.last(), pch_container_ops, diags, resources_path, only_local_decls, capture_diagnostics, clang::None, true, 0, clang::TU_Complete, @@ -4847,7 +4859,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const if (diags->getClient()->getNumErrors() > 0) { if (ast_unit) { - err_unit = std::move(ast_unit); + err_unit = std::unique_ptr(reinterpret_cast(ast_unit)); } for (clang::ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(), @@ -4894,14 +4906,14 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const return ErrorCCompileErrors; } - c->ctx = &ast_unit->getASTContext(); - c->source_manager = reinterpret_cast(&ast_unit->getSourceManager()); + c->ctx = ZigClangASTUnit_getASTContext(ast_unit); + c->source_manager = ZigClangASTUnit_getSourceManager(ast_unit); c->root = trans_create_node(c, NodeTypeContainerDecl); c->root->data.container_decl.is_root = true; - ast_unit->visitLocalTopLevelDecls(c, decl_visitor); + ZigClangASTUnit_visitLocalTopLevelDecls(ast_unit, c, decl_visitor); - process_preprocessor_entities(c, *ast_unit); + process_preprocessor_entities(c, ast_unit); render_macros(c); render_aliases(c); diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 4e6e4e7a98..4220d778a0 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -138,19 +138,29 @@ static_assert((clang::UnaryOperatorKind)ZigClangUO_PreInc == clang::UO_PreInc, " static_assert((clang::UnaryOperatorKind)ZigClangUO_Real == clang::UO_Real, ""); static_assert(sizeof(ZigClangSourceLocation) == sizeof(clang::SourceLocation), ""); - static ZigClangSourceLocation bitcast(clang::SourceLocation src) { ZigClangSourceLocation dest; memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); return dest; } - static clang::SourceLocation bitcast(ZigClangSourceLocation src) { clang::SourceLocation dest; memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); return dest; } +static_assert(sizeof(ZigClangQualType) == sizeof(clang::QualType), ""); +static ZigClangQualType bitcast(clang::QualType src) { + ZigClangQualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} +static clang::QualType bitcast(ZigClangQualType src) { + clang::QualType dest; + memcpy(&dest, &src, sizeof(ZigClangQualType)); + return dest; +} + ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const ZigClangSourceManager *self, ZigClangSourceLocation Loc) { @@ -181,3 +191,24 @@ const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager * { return reinterpret_cast(self)->getCharacterData(bitcast(SL)); } + +ZigClangQualType ZigClangASTContext_getPointerType(const ZigClangASTContext* self, ZigClangQualType T) { + return bitcast(reinterpret_cast(self)->getPointerType(bitcast(T))); +} + +ZigClangASTContext *ZigClangASTUnit_getASTContext(ZigClangASTUnit *self) { + clang::ASTContext *result = &reinterpret_cast(self)->getASTContext(); + return reinterpret_cast(result); +} + +ZigClangSourceManager *ZigClangASTUnit_getSourceManager(ZigClangASTUnit *self) { + clang::SourceManager *result = &reinterpret_cast(self)->getSourceManager(); + return reinterpret_cast(result); +} + +bool ZigClangASTUnit_visitLocalTopLevelDecls(ZigClangASTUnit *self, void *context, + bool (*Fn)(void *context, const ZigClangDecl *decl)) +{ + return reinterpret_cast(self)->visitLocalTopLevelDecls(context, + reinterpret_cast(Fn)); +} diff --git a/src/zig_clang.h b/src/zig_clang.h index 78d5cd8cbc..c7d749cbd9 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -21,6 +21,10 @@ struct ZigClangSourceLocation { unsigned ID; }; +struct ZigClangQualType { + void *ptr; +}; + struct ZigClangAPValue; struct ZigClangASTContext; struct ZigClangASTUnit; @@ -71,7 +75,6 @@ struct ZigClangParenType; struct ZigClangParmVarDecl; struct ZigClangPointerType; struct ZigClangPreprocessedEntity; -struct ZigClangQualType; struct ZigClangRecordDecl; struct ZigClangRecordType; struct ZigClangReturnStmt; @@ -246,4 +249,11 @@ ZIG_EXTERN_C unsigned ZigClangSourceManager_getSpellingColumnNumber(const ZigCla ZigClangSourceLocation Loc); ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const ZigClangSourceManager *, ZigClangSourceLocation SL); + +ZIG_EXTERN_C ZigClangQualType ZigClangASTContext_getPointerType(const ZigClangASTContext*, ZigClangQualType T); + +ZIG_EXTERN_C ZigClangASTContext *ZigClangASTUnit_getASTContext(ZigClangASTUnit *); +ZIG_EXTERN_C ZigClangSourceManager *ZigClangASTUnit_getSourceManager(ZigClangASTUnit *); +ZIG_EXTERN_C bool ZigClangASTUnit_visitLocalTopLevelDecls(ZigClangASTUnit *, void *context, + bool (*Fn)(void *context, const ZigClangDecl *decl)); #endif From 91989e70ba68e3543acffef079d97c9416b5259c Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Tue, 12 Feb 2019 21:22:16 -0800 Subject: [PATCH 058/134] Fix lvalue dereference type checking Previously, if a dereference instruction was an lvalue, it would fail to typecheck that the value being dereferenced was indeed a pointer. Although a little clunky, this change obviates the need for redundant type checks scattered about the analysis. --- src/all_types.hpp | 11 ++++++----- src/ir.cpp | 39 ++++++++++++++++++--------------------- test/compile_errors.zig | 24 +++++++++++++++++++++--- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6fbd987b9e..1304106f08 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2091,6 +2091,11 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; +enum LVal { + LValNone, + LValPtr, +}; + // These instructions are in transition to having "pass 1" instructions // and "pass 2" instructions. The pass 1 instructions are suffixed with Src // and pass 2 are suffixed with Gen. @@ -2354,6 +2359,7 @@ struct IrInstructionUnOp { IrUnOp op_id; IrInstruction *value; + LVal lval; }; enum IrBinOp { @@ -3090,11 +3096,6 @@ struct IrInstructionTypeName { IrInstruction *type_value; }; -enum LVal { - LValNone, - LValPtr, -}; - struct IrInstructionDeclRef { IrInstruction base; diff --git a/src/ir.cpp b/src/ir.cpp index 0fcbb60fe8..5e6ff16f85 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1307,6 +1307,7 @@ static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *sour IrInstructionUnOp *br_instruction = ir_build_instruction(irb, scope, source_node); br_instruction->op_id = op_id; br_instruction->value = value; + br_instruction->lval = LValNone; ir_ref_instruction(value, irb->current_basic_block); @@ -7223,7 +7224,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (value == irb->codegen->invalid_instruction) return value; - return ir_build_un_op(irb, scope, node, IrUnOpDereference, value); + // We essentially just converted any lvalue from &(x.*) to (&x).*; + // this inhibits checking that x is a pointer later, so we directly + // record whether the pointer check is needed + IrInstructionUnOp *result = (IrInstructionUnOp*)ir_build_un_op(irb, scope, node, IrUnOpDereference, value); + result->lval = lval; + + return &result->base; } case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; @@ -11437,7 +11444,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc return load_ptr_instruction; } else { ir_add_error_node(ira, source_instruction->source_node, - buf_sprintf("attempt to dereference non pointer type '%s'", + buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } @@ -13616,12 +13623,6 @@ no_mem_slot: static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, IrInstruction *uncasted_value) { - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->invalid_instruction; - } - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { return ir_const_void(ira, source_instr); } @@ -14550,11 +14551,18 @@ static IrInstruction *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstruction buf_ptr(&ptr_type->name))); return ira->codegen->invalid_instruction; } - // this dereference is always an rvalue because in the IR gen we identify lvalue and emit - // one of the ptr instructions + IrInstruction *result = ir_get_deref(ira, &instruction->base, ptr); if (result == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; + + // If the result needs to be an lvalue, type check it + if (instruction->lval == LValPtr && result->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, &instruction->base, + buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value.type->name))); + return ira->codegen->invalid_instruction; + } + return result; } case IrUnOpOptional: @@ -15380,12 +15388,6 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(container_ptr->value.type)) return ira->codegen->invalid_instruction; - if (container_ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error_node(ira, field_ptr_instruction->base.source_node, - buf_sprintf("attempt to dereference non-pointer type '%s'", - buf_ptr(&container_ptr->value.type->name))); - return ira->codegen->invalid_instruction; - } ZigType *container_type = container_ptr->value.type->data.pointer.child_type; Buf *field_name = field_ptr_instruction->field_name_buffer; @@ -16596,11 +16598,6 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return ir_const_type(ira, &switch_target_instruction->base, ptr_type->data.pointer.child_type); } - if (target_value_ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, target_value_ptr, buf_sprintf("invalid deref on switch target")); - return ira->codegen->invalid_instruction; - } - ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type; ConstExprValue *pointee_val = nullptr; if (instr_is_comptime(target_value_ptr)) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9ef4af4162..f2f08c4323 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -137,6 +137,24 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", ); + cases.addTest( + "assign to invalid dereference", + \\export fn entry() void { + \\ 'a'.* = 1; + \\} + , + ".tmp_source.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int'", + ); + + cases.addTest( + "take slice of invalid dereference", + \\export fn entry() void { + \\ const x = 'a'.*[0..]; + \\} + , + ".tmp_source.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int'", + ); + cases.addTest( "@truncate undefined value", \\export fn entry() void { @@ -447,7 +465,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = a.*.len; \\} , - ".tmp_source.zig:3:12: error: attempt to dereference non-pointer type '[]u8'", + ".tmp_source.zig:3:10: error: attempt to dereference non-pointer type '[]u8'", ); cases.add( @@ -1158,7 +1176,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ Filled, \\}; , - ".tmp_source.zig:3:17: error: invalid deref on switch target", + ".tmp_source.zig:3:17: error: attempt to dereference non-pointer type 'Tile'", ); cases.add( @@ -4000,7 +4018,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(pass)); } , - ".tmp_source.zig:4:10: error: attempt to dereference non pointer type '[10]u8'", + ".tmp_source.zig:4:10: error: attempt to dereference non-pointer type '[10]u8'", ); cases.add( From fd61a084e49037bc53dd5150bd27e28758b7aecf Mon Sep 17 00:00:00 2001 From: sjdh02 Date: Fri, 25 Jan 2019 03:02:06 +0000 Subject: [PATCH 059/134] fix BufferedInStream not reading delayed input --- std/io.zig | 70 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/std/io.zig b/std/io.zig index 6c70834b52..2afd54cadd 100644 --- a/std/io.zig +++ b/std/io.zig @@ -343,23 +343,24 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) const amt_buffered = self.end_index - self.start_index; if (amt_buffered == 0) { assert(self.end_index <= buffer_size); - if (self.end_index == buffer_size) { - // we can read more data from the unbuffered stream - if (dest_space < buffer_size) { - self.start_index = 0; - self.end_index = try self.unbuffered_in_stream.read(self.buffer[0..]); - } else { - // asking for so much data that buffering is actually less efficient. - // forward the request directly to the unbuffered stream - const amt_read = try self.unbuffered_in_stream.read(dest[dest_index..]); - return dest_index + amt_read; - } - } else { - // reading from the unbuffered stream returned less than we asked for - // so we cannot read any more data. + // Make sure the last read actually gave us some data + if (self.end_index == 0) { + // reading from the unbuffered stream returned nothing + // so we have nothing left to read. return dest_index; } + // we can read more data from the unbuffered stream + if (dest_space < buffer_size) { + self.start_index = 0; + self.end_index = try self.unbuffered_in_stream.read(self.buffer[0..]); + } else { + // asking for so much data that buffering is actually less efficient. + // forward the request directly to the unbuffered stream + const amt_read = try self.unbuffered_in_stream.read(dest[dest_index..]); + return dest_index + amt_read; + } } + const copy_amount = math.min(dest_space, amt_buffered); const copy_end_index = self.start_index + copy_amount; mem.copy(u8, dest[dest_index..], self.buffer[self.start_index..copy_end_index]); @@ -370,6 +371,46 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) }; } +test "io.BufferedInStream" { + const OneByteReadInStream = struct { + const Error = error{NoError}; + const Stream = InStream(Error); + + stream: Stream, + str: []const u8, + curr: usize, + + fn init(str: []const u8) @This() { + return @This(){ + .stream = Stream{ .readFn = readFn }, + .str = str, + .curr = 0, + }; + } + + fn readFn(in_stream: *Stream, dest: []u8) Error!usize { + const self = @fieldParentPtr(@This(), "stream", in_stream); + if (self.str.len <= self.curr or dest.len == 0) + return 0; + + dest[0] = self.str[self.curr]; + self.curr += 1; + return 1; + } + }; + + var buf: [100]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + + const str = "This is a test"; + var one_byte_stream = OneByteReadInStream.init(str); + var buf_in_stream = BufferedInStream(OneByteReadInStream.Error).init(&one_byte_stream.stream); + const stream = &buf_in_stream.stream; + + const res = try stream.readAllAlloc(allocator, str.len + 1); + debug.assertOrPanic(mem.eql(u8, str, res)); +} + /// Creates a stream which supports 'un-reading' data, so that it can be read again. /// This makes look-ahead style parsing much easier. pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type { @@ -1411,3 +1452,4 @@ test "import io tests" { _ = @import("io_test.zig"); } } + From 4a0bb6258406aa1f4f876116d47be6b93e115e23 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 16 Feb 2019 22:47:58 -0500 Subject: [PATCH 060/134] fixups --- std/io.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/io.zig b/std/io.zig index 2afd54cadd..f6e3790af6 100644 --- a/std/io.zig +++ b/std/io.zig @@ -408,7 +408,7 @@ test "io.BufferedInStream" { const stream = &buf_in_stream.stream; const res = try stream.readAllAlloc(allocator, str.len + 1); - debug.assertOrPanic(mem.eql(u8, str, res)); + testing.expectEqualSlices(u8, str, res); } /// Creates a stream which supports 'un-reading' data, so that it can be read again. From bc10382ec1b87da16943907ba2d0fbd267af07f0 Mon Sep 17 00:00:00 2001 From: Maya Rashish Date: Sat, 16 Feb 2019 12:29:12 +0200 Subject: [PATCH 061/134] Add NetBSD support Mostly picking the same paths as FreeBSD. We need a little special handling for crt files, as netbsd uses its own (and not GCC's) for those, with slightly different names. --- CMakeLists.txt | 3 + cmake/Findllvm.cmake | 1 - src-self-hosted/libc_installation.zig | 2 +- src/analyze.cpp | 11 +- src/codegen.cpp | 3 +- src/link.cpp | 10 +- src/os.cpp | 19 +- src/os.hpp | 2 + src/target.cpp | 2 +- std/c/index.zig | 1 + std/c/netbsd.zig | 116 ++++ std/debug/index.zig | 6 +- std/event/fs.zig | 31 +- std/event/loop.zig | 21 +- std/heap.zig | 6 +- std/os/file.zig | 6 +- std/os/get_app_data_dir.zig | 2 +- std/os/get_user_id.zig | 2 +- std/os/index.zig | 48 +- std/os/netbsd/errno.zig | 134 +++++ std/os/netbsd/index.zig | 728 ++++++++++++++++++++++++++ std/os/path.zig | 4 +- std/os/time.zig | 10 +- 23 files changed, 1101 insertions(+), 67 deletions(-) create mode 100644 std/c/netbsd.zig create mode 100644 std/os/netbsd/errno.zig create mode 100644 std/os/netbsd/index.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index db5f50908c..11b53c6f59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -447,6 +447,7 @@ set(ZIG_STD_FILES "c/freebsd.zig" "c/index.zig" "c/linux.zig" + "c/netbsd.zig" "c/windows.zig" "coff.zig" "crypto/blake2.zig" @@ -587,6 +588,8 @@ set(ZIG_STD_FILES "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" + "os/netbsd/errno.zig" + "os/netbsd/index.zig" "os/path.zig" "os/time.zig" "os/uefi.zig" diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake index 2f0afa09b7..490bdae66e 100644 --- a/cmake/Findllvm.cmake +++ b/cmake/Findllvm.cmake @@ -50,7 +50,6 @@ NEED_TARGET("AMDGPU") NEED_TARGET("ARM") NEED_TARGET("BPF") NEED_TARGET("Hexagon") -NEED_TARGET("Lanai") NEED_TARGET("Mips") NEED_TARGET("MSP430") NEED_TARGET("NVPTX") diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index d3a461bcd9..b196d7bc70 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -172,7 +172,7 @@ pub const LibCInstallation = struct { try group.call(findNativeStaticLibDir, self, loop); try group.call(findNativeDynamicLinker, self, loop); }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include"); }, else => @compileError("unimplemented: find libc for this OS"), diff --git a/src/analyze.cpp b/src/analyze.cpp index 9941104bc4..37c0041314 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4732,7 +4732,8 @@ void find_libc_include_path(CodeGen *g) { } } else if (g->zig_target.os == OsLinux || g->zig_target.os == OsMacOSX || - g->zig_target.os == OsFreeBSD) + g->zig_target.os == OsFreeBSD || + g->zig_target.os == OsNetBSD) { g->libc_include_dir = get_posix_libc_include_path(); } else { @@ -4781,7 +4782,7 @@ void find_libc_lib_path(CodeGen *g) { } else if (g->zig_target.os == OsLinux) { g->libc_lib_dir = get_linux_libc_lib_path("crt1.o"); - } else if (g->zig_target.os == OsFreeBSD) { + } else if ((g->zig_target.os == OsFreeBSD) || (g->zig_target.os == OsNetBSD)) { g->libc_lib_dir = buf_create_from_str("/usr/lib"); } else { zig_panic("Unable to determine libc lib path."); @@ -4795,7 +4796,7 @@ void find_libc_lib_path(CodeGen *g) { return; } else if (g->zig_target.os == OsLinux) { g->libc_static_lib_dir = get_linux_libc_lib_path("crtbegin.o"); - } else if (g->zig_target.os == OsFreeBSD) { + } else if ((g->zig_target.os == OsFreeBSD) || (g->zig_target.os == OsNetBSD)) { g->libc_static_lib_dir = buf_create_from_str("/usr/lib"); } else { zig_panic("Unable to determine libc static lib path."); @@ -6710,7 +6711,9 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; - if (g->enable_cache && is_libc && g->zig_target.os != OsMacOSX && g->zig_target.os != OsIOS && g->zig_target.os != OsFreeBSD) { + if (g->enable_cache && is_libc && g->zig_target.os != OsMacOSX && + g->zig_target.os != OsIOS && g->zig_target.os != OsFreeBSD && + g->zig_target.os != OsNetBSD) { fprintf(stderr, "TODO linking against libc is currently incompatible with `--cache on`.\n" "Zig is not yet capable of determining whether the libc installation has changed on subsequent builds.\n"); exit(1); diff --git a/src/codegen.cpp b/src/codegen.cpp index d2b2836b0c..d54d06f3b5 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -184,7 +184,8 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out // On Darwin/MacOS/iOS, we always link libSystem which contains libc. if (g->zig_target.os == OsMacOSX || g->zig_target.os == OsIOS || - g->zig_target.os == OsFreeBSD) + g->zig_target.os == OsFreeBSD || + g->zig_target.os == OsNetBSD) { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); diff --git a/src/link.cpp b/src/link.cpp index 58221a99ea..956e5b6bfc 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -198,6 +198,9 @@ static Buf *get_dynamic_linker_path(CodeGen *g) { if (g->zig_target.os == OsFreeBSD) { return buf_create_from_str("/libexec/ld-elf.so.1"); } + if (g->zig_target.os == OsNetBSD) { + return buf_create_from_str("/libexec/ld.elf_so"); + } if (g->is_native_target && g->zig_target.arch.arch == ZigLLVM_x86_64) { static const char *ld_names[] = { "ld-linux-x86-64.so.2", @@ -263,10 +266,13 @@ static void construct_linker_job_elf(LinkJob *lj) { if (lj->link_in_crt) { const char *crt1o; const char *crtbegino; - if (g->is_static) { + if (g->zig_target.os == OsNetBSD) { + crt1o = "crt0.o"; + crtbegino = "crtbegin.o"; + } else if (g->is_static) { crt1o = "crt1.o"; crtbegino = "crtbeginT.o"; - } else { + } else { crt1o = "Scrt1.o"; crtbegino = "crtbegin.o"; } diff --git a/src/os.cpp b/src/os.cpp index 8eb6b34654..c11840b1cf 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -50,11 +50,11 @@ typedef SSIZE_T ssize_t; #endif -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) #include #endif -#if defined(ZIG_OS_FREEBSD) +#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) #include #endif @@ -78,7 +78,7 @@ static clock_serv_t cclock; #if defined(__APPLE__) && !defined(environ) #include #define environ (*_NSGetEnviron()) -#elif defined(ZIG_OS_FREEBSD) +#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) extern char **environ; #endif @@ -1458,6 +1458,15 @@ Error os_self_exe_path(Buf *out_path) { } buf_resize(out_path, cb - 1); return ErrorNone; +#elif defined(ZIG_OS_NETBSD) + buf_resize(out_path, PATH_MAX); + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; + size_t cb = PATH_MAX; + if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { + return ErrorUnexpected; + } + buf_resize(out_path, cb - 1); + return ErrorNone; #endif return ErrorFileNotFound; } @@ -1776,7 +1785,7 @@ Error os_get_app_data_dir(Buf *out_path, const char *appname) { } -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { ZigList *libs = reinterpret_cast< ZigList *>(data); if (info->dlpi_name[0] == '/') { @@ -1787,7 +1796,7 @@ static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, #endif Error os_self_exe_shared_libs(ZigList &paths) { -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) paths.resize(0); dl_iterate_phdr(self_exe_shared_libs_callback, &paths); return ErrorNone; diff --git a/src/os.hpp b/src/os.hpp index 68ac6e0171..dab7b84896 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -25,6 +25,8 @@ #define ZIG_OS_LINUX #elif defined(__FreeBSD__) #define ZIG_OS_FREEBSD +#elif defined(__NetBSD__) +#define ZIG_OS_NETBSD #else #define ZIG_OS_UNKNOWN #endif diff --git a/src/target.cpp b/src/target.cpp index b1434c6871..cea7826313 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -743,6 +743,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsMacOSX: case OsZen: case OsFreeBSD: + case OsNetBSD: case OsOpenBSD: switch (id) { case CIntTypeShort: @@ -783,7 +784,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { case OsIOS: case OsKFreeBSD: case OsLv2: - case OsNetBSD: case OsSolaris: case OsHaiku: case OsMinix: diff --git a/std/c/index.zig b/std/c/index.zig index 058d879c09..74cdd884ca 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -6,6 +6,7 @@ pub use switch (builtin.os) { Os.windows => @import("windows.zig"), Os.macosx, Os.ios => @import("darwin.zig"), Os.freebsd => @import("freebsd.zig"), + Os.netbsd => @import("netbsd.zig"), else => empty_import, }; const empty_import = @import("../empty.zig"); diff --git a/std/c/netbsd.zig b/std/c/netbsd.zig new file mode 100644 index 0000000000..796d45dafc --- /dev/null +++ b/std/c/netbsd.zig @@ -0,0 +1,116 @@ +extern "c" fn __errno() *c_int; +pub const _errno = __errno; + +pub extern "c" fn kqueue() c_int; +pub extern "c" fn kevent( + kq: c_int, + changelist: [*]const Kevent, + nchanges: c_int, + eventlist: [*]Kevent, + nevents: c_int, + timeout: ?*const timespec, +) c_int; +pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; +pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int; +pub extern "c" fn getdirentries(fd: c_int, buf_ptr: [*]u8, nbytes: usize, basep: *i64) usize; +pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; +pub extern "c" fn pipe2(arg0: *[2]c_int, arg1: u32) c_int; +pub extern "c" fn preadv(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; +pub extern "c" fn pwritev(fd: c_int, iov: *const c_void, iovcnt: c_int, offset: usize) isize; +pub extern "c" fn openat(fd: c_int, path: ?[*]const u8, flags: c_int) c_int; +pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; +pub extern "c" fn setuid(uid: c_uint) c_int; +pub extern "c" fn kill(pid: c_int, sig: c_int) c_int; +pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int; +pub extern "c" fn clock_getres(clk_id: c_int, tp: *timespec) c_int; + +/// Renamed from `kevent` to `Kevent` to avoid conflict with function name. +pub const Kevent = extern struct { + ident: usize, + filter: i32, + flags: u32, + fflags: u32, + data: i64, + udata: usize, +}; + +pub const pthread_attr_t = extern struct { + pta_magic: u32, + pta_flags: c_int, + pta_private: *c_void, +}; + +pub const msghdr = extern struct { + msg_name: *u8, + msg_namelen: socklen_t, + msg_iov: *iovec, + msg_iovlen: i32, + __pad1: i32, + msg_control: *u8, + msg_controllen: socklen_t, + __pad2: socklen_t, + msg_flags: i32, +}; + +pub const Stat = extern struct { + dev: u64, + mode: u32, + ino: u64, + nlink: usize, + + uid: u32, + gid: u32, + rdev: u64, + + atim: timespec, + mtim: timespec, + ctim: timespec, + birthtim: timespec, + + size: i64, + blocks: i64, + blksize: isize, + flags: u32, + gen: u32, + __spare: [2]u32, +}; + +pub const timespec = extern struct { + tv_sec: i64, + tv_nsec: isize, +}; + +pub const dirent = extern struct { + d_fileno: u64, + d_reclen: u16, + d_namlen: u16, + d_type: u8, + d_off: i64, + d_name: [512]u8, +}; + +pub const in_port_t = u16; +pub const sa_family_t = u8; + +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; + +pub const sockaddr_in = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + addr: u32, + zero: [8]u8, +}; + +pub const sockaddr_in6 = extern struct { + len: u8, + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; diff --git a/std/debug/index.zig b/std/debug/index.zig index 7e5be9acef..b8ea7e7f84 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -240,7 +240,7 @@ pub fn writeCurrentStackTraceWindows( pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { switch (builtin.os) { builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), - builtin.Os.linux, builtin.Os.freebsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), else => return error.UnsupportedOperatingSystem, } @@ -717,7 +717,7 @@ pub const OpenSelfDebugInfoError = error{ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { switch (builtin.os) { - builtin.Os.linux, builtin.Os.freebsd => return openSelfDebugInfoLinux(allocator), + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator), builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), builtin.Os.windows => return openSelfDebugInfoWindows(allocator), else => return error.UnsupportedOperatingSystem, @@ -1141,7 +1141,7 @@ pub const DebugInfo = switch (builtin.os) { sect_contribs: []pdb.SectionContribEntry, modules: []Module, }, - builtin.Os.linux, builtin.Os.freebsd => DwarfInfo, + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo, else => @compileError("Unsupported OS"), }; diff --git a/std/event/fs.zig b/std/event/fs.zig index 0e7482ec60..6535945573 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -85,6 +85,7 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, + builtin.Os.netbsd, => { const iovecs = try loop.allocator.alloc(os.posix.iovec_const, data.len); defer loop.allocator.free(iovecs); @@ -222,6 +223,7 @@ pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, + builtin.Os.netbsd, => { const iovecs = try loop.allocator.alloc(os.posix.iovec, data.len); defer loop.allocator.free(iovecs); @@ -402,7 +404,11 @@ pub async fn openPosix( pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd => { + builtin.Os.macosx, + builtin.Os.linux, + builtin.Os.freebsd, + builtin.Os.netbsd + => { const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC; return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); }, @@ -431,6 +437,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, + builtin.Os.netbsd, => { const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); @@ -453,7 +460,7 @@ pub async fn openReadWrite( mode: os.File.Mode, ) os.File.OpenError!os.FileHandle { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC; return await (async openPosix(loop, path, flags, mode) catch unreachable); }, @@ -481,7 +488,7 @@ pub const CloseOperation = struct { os_data: OsData, const OsData = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd => OsDataPosix, + builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => OsDataPosix, builtin.Os.windows => struct { handle: ?os.FileHandle, @@ -500,7 +507,7 @@ pub const CloseOperation = struct { self.* = CloseOperation{ .loop = loop, .os_data = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd => initOsDataPosix(self), + builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => initOsDataPosix(self), builtin.Os.windows => OsData{ .handle = null }, else => @compileError("Unsupported OS"), }, @@ -530,6 +537,7 @@ pub const CloseOperation = struct { builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, + builtin.Os.netbsd, => { if (self.os_data.have_fd) { self.loop.posixFsRequest(&self.os_data.close_req_node); @@ -552,6 +560,7 @@ pub const CloseOperation = struct { builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, + builtin.Os.netbsd, => { self.os_data.close_req_node.data.msg.Close.fd = handle; self.os_data.have_fd = true; @@ -569,6 +578,7 @@ pub const CloseOperation = struct { builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, + builtin.Os.netbsd, => { self.os_data.have_fd = false; }, @@ -584,6 +594,7 @@ pub const CloseOperation = struct { builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, + builtin.Os.netbsd, => { assert(self.os_data.have_fd); return self.os_data.close_req_node.data.msg.Close.fd; @@ -608,6 +619,7 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, + builtin.Os.netbsd, => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable), builtin.Os.windows => return await (async writeFileWindows(loop, path, contents) catch unreachable), else => @compileError("Unsupported OS"), @@ -713,7 +725,7 @@ pub fn Watch(comptime V: type) type { os_data: OsData, const OsData = switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => struct { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => struct { file_table: FileTable, table_lock: event.Lock, @@ -802,7 +814,7 @@ pub fn Watch(comptime V: type) type { return self; }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { const self = try loop.allocator.create(Self); errdefer loop.allocator.destroy(self); @@ -822,7 +834,7 @@ pub fn Watch(comptime V: type) type { /// All addFile calls and removeFile calls must have completed. pub fn destroy(self: *Self) void { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { // TODO we need to cancel the coroutines before destroying the lock self.os_data.table_lock.deinit(); var it = self.os_data.file_table.iterator(); @@ -864,7 +876,10 @@ pub fn Watch(comptime V: type) type { pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => return await (async addFileKEvent(self, file_path, value) catch unreachable), + builtin.Os.macosx, + builtin.Os.freebsd, + builtin.Os.netbsd + => return await (async addFileKEvent(self, file_path, value) catch unreachable), builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable), else => @compileError("Unsupported OS"), diff --git a/std/event/loop.zig b/std/event/loop.zig index b92bf41982..55a1538f0c 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -50,7 +50,7 @@ pub const Loop = struct { }; pub const EventFd = switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => KEventFd, + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => KEventFd, builtin.Os.linux => struct { base: ResumeNode, epoll_op: u32, @@ -69,7 +69,7 @@ pub const Loop = struct { }; pub const Basic = switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => KEventBasic, + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => KEventBasic, builtin.Os.linux => struct { base: ResumeNode, }, @@ -221,7 +221,7 @@ pub const Loop = struct { self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); } }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { self.os_data.kqfd = try os.bsdKQueue(); errdefer os.close(self.os_data.kqfd); @@ -386,7 +386,7 @@ pub const Loop = struct { os.close(self.os_data.epollfd); self.allocator.free(self.eventfd_resume_nodes); }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { os.close(self.os_data.kqfd); os.close(self.os_data.fs_kqfd); }, @@ -501,7 +501,7 @@ pub const Loop = struct { const eventfd_node = &resume_stack_node.data; eventfd_node.base.handle = next_tick_node.data; switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch { @@ -564,6 +564,7 @@ pub const Loop = struct { builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, + builtin.Os.netbsd, => self.os_data.fs_thread.wait(), else => {}, } @@ -628,7 +629,7 @@ pub const Loop = struct { os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; return; }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { self.posixFsRequest(&self.os_data.fs_end_request); const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; @@ -686,7 +687,7 @@ pub const Loop = struct { } } }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { var eventlist: [1]posix.Kevent = undefined; const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; const count = os.bsdKEvent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable; @@ -749,7 +750,7 @@ pub const Loop = struct { self.beginOneEvent(); // finished in posixFsRun after processing the msg self.os_data.fs_queue.put(request_node); switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { const fs_kevs = (*[1]posix.Kevent)(&self.os_data.fs_kevent_wake); const empty_kevs = ([*]posix.Kevent)(undefined)[0..0]; _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable; @@ -819,7 +820,7 @@ pub const Loop = struct { else => unreachable, } }, - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { const fs_kevs = (*[1]posix.Kevent)(&self.os_data.fs_kevent_wait); var out_kevs: [1]posix.Kevent = undefined; _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable; @@ -831,7 +832,7 @@ pub const Loop = struct { const OsData = switch (builtin.os) { builtin.Os.linux => LinuxOsData, - builtin.Os.macosx, builtin.Os.freebsd => KEventData, + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => KEventData, builtin.Os.windows => struct { io_port: windows.HANDLE, extra_thread_count: usize, diff --git a/std/heap.zig b/std/heap.zig index e7088150a8..e257977f9d 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -71,7 +71,7 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const p = os.posix; const alloc_size = if (alignment <= os.page_size) n else n + alignment; const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0); @@ -120,7 +120,7 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { if (new_size <= old_mem.len) { const base_addr = @ptrToInt(old_mem.ptr); const old_addr_end = base_addr + old_mem.len; @@ -164,7 +164,7 @@ pub const DirectAllocator = struct { const self = @fieldParentPtr(DirectAllocator, "allocator", allocator); switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len); }, Os.windows => { diff --git a/std/os/file.zig b/std/os/file.zig index 2ae547c694..85aa6e1215 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -237,7 +237,7 @@ pub const File = struct { pub fn seekForward(self: File, amount: isize) SeekError!void { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); const err = posix.getErrno(result); if (err > 0) { @@ -268,7 +268,7 @@ pub const File = struct { pub fn seekTo(self: File, pos: usize) SeekError!void { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const ipos = try math.cast(isize, pos); const result = posix.lseek(self.handle, ipos, posix.SEEK_SET); const err = posix.getErrno(result); @@ -309,7 +309,7 @@ pub const File = struct { pub fn getPos(self: File) GetSeekPosError!usize { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); const err = posix.getErrno(result); if (err > 0) { diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig index f5e0b78eec..a49d164f46 100644 --- a/std/os/get_app_data_dir.zig +++ b/std/os/get_app_data_dir.zig @@ -43,7 +43,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD }; return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname }); }, - builtin.Os.linux, builtin.Os.freebsd => { + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { const home_dir = os.getEnvPosix("HOME") orelse { // TODO look in /etc/passwd return error.AppDataDirUnavailable; diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig index 9a4d1ab275..6747cc4557 100644 --- a/std/os/get_user_id.zig +++ b/std/os/get_user_id.zig @@ -11,7 +11,7 @@ pub const UserInfo = struct { /// POSIX function which gets a uid from username. pub fn getUserInfo(name: []const u8) !UserInfo { return switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => posixGetUserInfo(name), + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posixGetUserInfo(name), else => @compileError("Unsupported OS"), }; } diff --git a/std/os/index.zig b/std/os/index.zig index f52c12c2b6..5d749479de 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -3,7 +3,7 @@ const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; const is_posix = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd => true, + builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, else => false, }; const os = @This(); @@ -30,6 +30,7 @@ pub const windows = @import("windows/index.zig"); pub const darwin = @import("darwin.zig"); pub const linux = @import("linux/index.zig"); pub const freebsd = @import("freebsd/index.zig"); +pub const netbsd = @import("netbsd/index.zig"); pub const zen = @import("zen.zig"); pub const uefi = @import("uefi.zig"); @@ -37,6 +38,7 @@ pub const posix = switch (builtin.os) { Os.linux => linux, Os.macosx, Os.ios => darwin, Os.freebsd => freebsd, + Os.netbsd => netbsd, Os.zen => zen, else => @compileError("Unsupported OS"), }; @@ -50,7 +52,7 @@ pub const time = @import("time.zig"); pub const page_size = 4 * 1024; pub const MAX_PATH_BYTES = switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => posix.PATH_MAX, + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posix.PATH_MAX, // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. // If it would require 4 UTF-8 bytes, then there would be a surrogate // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. @@ -125,7 +127,7 @@ pub fn getRandomBytes(buf: []u8) !void { else => return unexpectedErrorPosix(errno), } }, - Os.macosx, Os.ios, Os.freebsd => return getRandomBytesDevURandom(buf), + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), Os.windows => { // Call RtlGenRandom() instead of CryptGetRandom() on Windows // https://github.com/rust-lang-nursery/rand/issues/111 @@ -185,7 +187,7 @@ pub fn abort() noreturn { c.abort(); } switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { _ = posix.raise(posix.SIGABRT); _ = posix.raise(posix.SIGKILL); while (true) {} @@ -211,7 +213,7 @@ pub fn exit(status: u8) noreturn { c.exit(status); } switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { posix.exit(status); }, Os.windows => { @@ -327,7 +329,7 @@ pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u6 } } }, - builtin.Os.linux, builtin.Os.freebsd => while (true) { + builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { const rc = posix.preadv(fd, iov, count, offset); const err = posix.getErrno(rc); switch (err) { @@ -434,7 +436,7 @@ pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, off } } }, - builtin.Os.linux, builtin.Os.freebsd => while (true) { + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { const rc = posix.pwritev(fd, iov, count, offset); const err = posix.getErrno(rc); switch (err) { @@ -699,7 +701,7 @@ pub fn getBaseAddress() usize { const phdr = linuxGetAuxVal(std.elf.AT_PHDR); return phdr - @sizeOf(std.elf.Ehdr); }, - builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header), + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => return @ptrToInt(&std.c._mh_execute_header), builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), else => @compileError("Unsupported OS"), } @@ -1339,7 +1341,7 @@ pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); return deleteDirW(&dir_path_w); }, - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const err = posix.getErrno(posix.rmdir(dir_path)); switch (err) { 0 => return, @@ -1382,7 +1384,7 @@ pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); return deleteDirW(&dir_path_w); }, - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const dir_path_c = try toPosixPath(dir_path); return deleteDirC(&dir_path_c); }, @@ -1501,7 +1503,7 @@ pub const Dir = struct { allocator: *Allocator, pub const Handle = switch (builtin.os) { - Os.macosx, Os.ios, Os.freebsd => struct { + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct { fd: i32, seek: i64, buf: []u8, @@ -1578,7 +1580,7 @@ pub const Dir = struct { .name_data = undefined, }; }, - Os.macosx, Os.ios, Os.freebsd => Handle{ + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{ .fd = try posixOpen( dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, @@ -1609,7 +1611,7 @@ pub const Dir = struct { Os.windows => { _ = windows.FindClose(self.handle.handle); }, - Os.macosx, Os.ios, Os.linux, Os.freebsd => { + Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { self.allocator.free(self.handle.buf); os.close(self.handle.fd); }, @@ -1625,6 +1627,7 @@ pub const Dir = struct { Os.macosx, Os.ios => return self.nextDarwin(), Os.windows => return self.nextWindows(), Os.freebsd => return self.nextFreebsd(), + Os.netbsd => return self.nextFreebsd(), else => @compileError("unimplemented"), } } @@ -2256,7 +2259,7 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { pub fn openSelfExe() !os.File { switch (builtin.os) { Os.linux => return os.File.openReadC(c"/proc/self/exe"), - Os.macosx, Os.ios, Os.freebsd => { + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { var buf: [MAX_PATH_BYTES]u8 = undefined; const self_exe_path = try selfExePath(&buf); buf[self_exe_path.len] = 0; @@ -2317,6 +2320,19 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { else => unexpectedErrorPosix(err), }; }, + Os.netbsd => { + var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME }; + var out_len: usize = out_buffer.len; + const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); + + if (err == 0) return mem.toSlice(u8, out_buffer); + + return switch (err) { + posix.EFAULT => error.BadAdress, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; + }, Os.windows => { var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; const utf16le_slice = try selfExePathW(&utf16le_buf); @@ -2355,7 +2371,7 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { // will not return null. return path.dirname(full_exe_path).?; }, - Os.windows, Os.macosx, Os.ios, Os.freebsd => { + Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const self_exe_path = try selfExePath(out_buffer); // Assume that the OS APIs return absolute paths, and therefore dirname // will not return null. @@ -3227,7 +3243,7 @@ pub const CpuCountError = error{ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd => { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { var count: c_int = undefined; var count_len: usize = @sizeOf(c_int); const rc = posix.sysctlbyname(switch (builtin.os) { diff --git a/std/os/netbsd/errno.zig b/std/os/netbsd/errno.zig new file mode 100644 index 0000000000..1772c2ab07 --- /dev/null +++ b/std/os/netbsd/errno.zig @@ -0,0 +1,134 @@ +pub const EPERM = 1; // Operation not permitted +pub const ENOENT = 2; // No such file or directory +pub const ESRCH = 3; // No such process +pub const EINTR = 4; // Interrupted system call +pub const EIO = 5; // Input/output error +pub const ENXIO = 6; // Device not configured +pub const E2BIG = 7; // Argument list too long +pub const ENOEXEC = 8; // Exec format error +pub const EBADF = 9; // Bad file descriptor +pub const ECHILD = 10; // No child processes +pub const EDEADLK = 11; // Resource deadlock avoided +// 11 was EAGAIN +pub const ENOMEM = 12; // Cannot allocate memory +pub const EACCES = 13; // Permission denied +pub const EFAULT = 14; // Bad address +pub const ENOTBLK = 15; // Block device required +pub const EBUSY = 16; // Device busy +pub const EEXIST = 17; // File exists +pub const EXDEV = 18; // Cross-device link +pub const ENODEV = 19; // Operation not supported by device +pub const ENOTDIR = 20; // Not a directory +pub const EISDIR = 21; // Is a directory +pub const EINVAL = 22; // Invalid argument +pub const ENFILE = 23; // Too many open files in system +pub const EMFILE = 24; // Too many open files +pub const ENOTTY = 25; // Inappropriate ioctl for device +pub const ETXTBSY = 26; // Text file busy +pub const EFBIG = 27; // File too large +pub const ENOSPC = 28; // No space left on device +pub const ESPIPE = 29; // Illegal seek +pub const EROFS = 30; // Read-only file system +pub const EMLINK = 31; // Too many links +pub const EPIPE = 32; // Broken pipe + +// math software +pub const EDOM = 33; // Numerical argument out of domain +pub const ERANGE = 34; // Result too large or too small + +// non-blocking and interrupt i/o +pub const EAGAIN = 35; // Resource temporarily unavailable +pub const EWOULDBLOCK = EAGAIN; // Operation would block +pub const EINPROGRESS = 36; // Operation now in progress +pub const EALREADY = 37; // Operation already in progress + +// ipc/network software -- argument errors +pub const ENOTSOCK = 38; // Socket operation on non-socket +pub const EDESTADDRREQ = 39; // Destination address required +pub const EMSGSIZE = 40; // Message too long +pub const EPROTOTYPE = 41; // Protocol wrong type for socket +pub const ENOPROTOOPT = 42; // Protocol option not available +pub const EPROTONOSUPPORT = 43; // Protocol not supported +pub const ESOCKTNOSUPPORT = 44; // Socket type not supported +pub const EOPNOTSUPP = 45; // Operation not supported +pub const EPFNOSUPPORT = 46; // Protocol family not supported +pub const EAFNOSUPPORT = 47; // Address family not supported by protocol family +pub const EADDRINUSE = 48; // Address already in use +pub const EADDRNOTAVAIL = 49; // Can't assign requested address + +// ipc/network software -- operational errors +pub const ENETDOWN = 50; // Network is down +pub const ENETUNREACH = 51; // Network is unreachable +pub const ENETRESET = 52; // Network dropped connection on reset +pub const ECONNABORTED = 53; // Software caused connection abort +pub const ECONNRESET = 54; // Connection reset by peer +pub const ENOBUFS = 55; // No buffer space available +pub const EISCONN = 56; // Socket is already connected +pub const ENOTCONN = 57; // Socket is not connected +pub const ESHUTDOWN = 58; // Can't send after socket shutdown +pub const ETOOMANYREFS = 59; // Too many references: can't splice +pub const ETIMEDOUT = 60; // Operation timed out +pub const ECONNREFUSED = 61; // Connection refused + +pub const ELOOP = 62; // Too many levels of symbolic links +pub const ENAMETOOLONG = 63; // File name too long + +// should be rearranged +pub const EHOSTDOWN = 64; // Host is down +pub const EHOSTUNREACH = 65; // No route to host +pub const ENOTEMPTY = 66; // Directory not empty + +// quotas & mush +pub const EPROCLIM = 67; // Too many processes +pub const EUSERS = 68; // Too many users +pub const EDQUOT = 69; // Disc quota exceeded + +// Network File System +pub const ESTALE = 70; // Stale NFS file handle +pub const EREMOTE = 71; // Too many levels of remote in path +pub const EBADRPC = 72; // RPC struct is bad +pub const ERPCMISMATCH = 73; // RPC version wrong +pub const EPROGUNAVAIL = 74; // RPC prog. not avail +pub const EPROGMISMATCH = 75; // Program version wrong +pub const EPROCUNAVAIL = 76; // Bad procedure for program + +pub const ENOLCK = 77; // No locks available +pub const ENOSYS = 78; // Function not implemented + +pub const EFTYPE = 79; // Inappropriate file type or format +pub const EAUTH = 80; // Authentication error +pub const ENEEDAUTH = 81; // Need authenticator + +// SystemV IPC +pub const EIDRM = 82; // Identifier removed +pub const ENOMSG = 83; // No message of desired type +pub const EOVERFLOW = 84; // Value too large to be stored in data type + +// Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 +pub const EILSEQ = 85; // Illegal byte sequence + +// From IEEE Std 1003.1-2001 +// Base, Realtime, Threads or Thread Priority Scheduling option errors +pub const ENOTSUP = 86; // Not supported + +// Realtime option errors +pub const ECANCELED = 87; // Operation canceled + +// Realtime, XSI STREAMS option errors +pub const EBADMSG = 88; // Bad or Corrupt message + +// XSI STREAMS option errors +pub const ENODATA = 89; // No message available +pub const ENOSR = 90; // No STREAM resources +pub const ENOSTR = 91; // Not a STREAM +pub const ETIME = 92; // STREAM ioctl timeout + +// File system extended attribute errors +pub const ENOATTR = 93; // Attribute not found + +// Realtime, XSI STREAMS option errors +pub const EMULTIHOP = 94; // Multihop attempted +pub const ENOLINK = 95; // Link has been severed +pub const EPROTO = 96; // Protocol error + +pub const ELAST = 96; // Must equal largest errno diff --git a/std/os/netbsd/index.zig b/std/os/netbsd/index.zig new file mode 100644 index 0000000000..53de61126c --- /dev/null +++ b/std/os/netbsd/index.zig @@ -0,0 +1,728 @@ +const builtin = @import("builtin"); + +pub use @import("errno.zig"); + +const std = @import("../../index.zig"); +const c = std.c; + +const assert = std.debug.assert; +const maxInt = std.math.maxInt; +pub const Kevent = c.Kevent; + +pub const CTL_KERN = 1; +pub const CTL_DEBUG = 5; + +pub const KERN_PROC_ARGS = 48; // struct: process argv/env +pub const KERN_PROC_PATHNAME = 5; // path to executable + +pub const PATH_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_VIRTUAL = 1; +pub const CLOCK_PROF = 2; +pub const CLOCK_MONOTONIC = 3; +pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; +pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; + +pub const MAP_FAILED = maxInt(usize); +pub const MAP_SHARED = 0x0001; +pub const MAP_PRIVATE = 0x0002; +pub const MAP_REMAPDUP = 0x0004; +pub const MAP_FIXED = 0x0010; +pub const MAP_RENAME = 0x0020; +pub const MAP_NORESERVE = 0x0040; +pub const MAP_INHERIT = 0x0080; +pub const MAP_HASSEMAPHORE = 0x0200; +pub const MAP_TRYFIXED = 0x0400; +pub const MAP_WIRED = 0x0800; + +pub const MAP_FILE = 0x0000; +pub const MAP_NOSYNC = 0x0800; +pub const MAP_ANON = 0x1000; +pub const MAP_ANONYMOUS = MAP_ANON; +pub const MAP_STACK = 0x2000; + +pub const WNOHANG = 0x00000001; +pub const WUNTRACED = 0x00000002; +pub const WSTOPPED = WUNTRACED; +pub const WCONTINUED = 0x00000010; +pub const WNOWAIT = 0x00010000; +pub const WEXITED = 0x00000020; +pub const WTRAPPED = 0x00000040; + +pub const SA_ONSTACK = 0x0001; +pub const SA_RESTART = 0x0002; +pub const SA_RESETHAND = 0x0004; +pub const SA_NOCLDSTOP = 0x0008; +pub const SA_NODEFER = 0x0010; +pub const SA_NOCLDWAIT = 0x0020; +pub const SA_SIGINFO = 0x0040; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGEMT = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGBUS = 10; +pub const SIGSEGV = 11; +pub const SIGSYS = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGURG = 16; +pub const SIGSTOP = 17; +pub const SIGTSTP = 18; +pub const SIGCONT = 19; +pub const SIGCHLD = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGIO = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGINFO = 29; +pub const SIGUSR1 = 30; +pub const SIGUSR2 = 31; +pub const SIGPWR = 32; + +pub const SIGRTMIN = 33; +pub const SIGRTMAX = 63; + +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission + + +pub const O_RDONLY = 0x0000; +pub const O_WRONLY = 0x0001; +pub const O_RDWR = 0x0002; +pub const O_ACCMODE = 0x0003; + +pub const O_CREAT = 0x0200; +pub const O_EXCL = 0x0800; +pub const O_NOCTTY = 0x8000; +pub const O_TRUNC = 0x0400; +pub const O_APPEND = 0x0008; +pub const O_NONBLOCK = 0x0004; +pub const O_DSYNC = 0x00010000; +pub const O_SYNC = 0x0080; +pub const O_RSYNC = 0x00020000; +pub const O_DIRECTORY = 0x00080000; +pub const O_NOFOLLOW = 0x00000100; +pub const O_CLOEXEC = 0x00400000; + +pub const O_ASYNC = 0x0040; +pub const O_DIRECT = 0x00080000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0; +pub const O_PATH = 0; +pub const O_TMPFILE = 0; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_GETOWN = 5; +pub const F_SETOWN = 6; + +pub const F_GETLK = 7; +pub const F_SETLK = 8; +pub const F_SETLKW = 9; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 1; +pub const SIG_UNBLOCK = 2; +pub const SIG_SETMASK = 3; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; + +pub const SOCK_CLOEXEC = 0x10000000; +pub const SOCK_NONBLOCK = 0x20000000; + +pub const PROTO_ip = 0; +pub const PROTO_icmp = 1; +pub const PROTO_igmp = 2; +pub const PROTO_ggp = 3; +pub const PROTO_ipencap = 4; +pub const PROTO_tcp = 6; +pub const PROTO_egp = 8; +pub const PROTO_pup = 12; +pub const PROTO_udp = 17; +pub const PROTO_xns_idp = 22; +pub const PROTO_iso_tp4 = 29; +pub const PROTO_ipv6 = 41; +pub const PROTO_ipv6_route = 43; +pub const PROTO_ipv6_frag = 44; +pub const PROTO_rsvp = 46; +pub const PROTO_gre = 47; +pub const PROTO_esp = 50; +pub const PROTO_ah = 51; +pub const PROTO_ipv6_icmp = 58; +pub const PROTO_ipv6_nonxt = 59; +pub const PROTO_ipv6_opts = 60; +pub const PROTO_encap = 98; +pub const PROTO_pim = 103; +pub const PROTO_raw = 255; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_APPLETALK = 16; +pub const PF_INET6 = 24; +pub const PF_DECnet = 12; +pub const PF_KEY = 29; +pub const PF_ROUTE = 34; +pub const PF_SNA = 11; +pub const PF_MPLS = 33; +pub const PF_CAN = 35; +pub const PF_BLUETOOTH = 31; +pub const PF_ISDN = 26; +pub const PF_MAX = 37; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_INET6 = PF_INET6; +pub const AF_KEY = PF_KEY; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_SNA = PF_SNA; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_ISDN = PF_ISDN; +pub const AF_MAX = PF_MAX; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = 0; +pub const EVFILT_WRITE = 1; + +/// attached to aio requests +pub const EVFILT_AIO = 2; + +/// attached to vnodes +pub const EVFILT_VNODE = 3; + +/// attached to struct proc +pub const EVFILT_PROC = 4; + +/// attached to struct proc +pub const EVFILT_SIGNAL = 5; + +/// timers +pub const EVFILT_TIMER = 6; + +/// Filesystem events +pub const EVFILT_FS = 7; + +/// XXX no EVFILT_USER, but what is it +pub const EVFILT_USER = 0; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x08000000; + +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// process exited +pub const NOTE_EXIT = 0x80000000; + +/// process forked +pub const NOTE_FORK = 0x40000000; + +/// process exec'd +pub const NOTE_EXEC = 0x20000000; + +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = 0xf0000000; + +pub const TIOCCBRK = 0x2000747a; +pub const TIOCCDTR = 0x20007478; +pub const TIOCCONS = 0x80047462; +pub const TIOCDCDTIMESTAMP = 0x40107458; +pub const TIOCDRAIN = 0x2000745e; +pub const TIOCEXCL = 0x2000740d; +pub const TIOCEXT = 0x80047460; +pub const TIOCFLAG_CDTRCTS = 0x10; +pub const TIOCFLAG_CLOCAL = 0x2; +pub const TIOCFLAG_CRTSCTS = 0x4; +pub const TIOCFLAG_MDMBUF = 0x8; +pub const TIOCFLAG_SOFTCAR = 0x1; +pub const TIOCFLUSH = 0x80047410; +pub const TIOCGETA = 0x402c7413; +pub const TIOCGETD = 0x4004741a; +pub const TIOCGFLAGS = 0x4004745d; +pub const TIOCGLINED = 0x40207442; +pub const TIOCGPGRP = 0x40047477; +pub const TIOCGQSIZE = 0x40047481; +pub const TIOCGRANTPT = 0x20007447; +pub const TIOCGSID = 0x40047463; +pub const TIOCGSIZE = 0x40087468; +pub const TIOCGWINSZ = 0x40087468; +pub const TIOCMBIC = 0x8004746b; +pub const TIOCMBIS = 0x8004746c; +pub const TIOCMGET = 0x4004746a; +pub const TIOCMSET = 0x8004746d; +pub const TIOCM_CAR = 0x40; +pub const TIOCM_CD = 0x40; +pub const TIOCM_CTS = 0x20; +pub const TIOCM_DSR = 0x100; +pub const TIOCM_DTR = 0x2; +pub const TIOCM_LE = 0x1; +pub const TIOCM_RI = 0x80; +pub const TIOCM_RNG = 0x80; +pub const TIOCM_RTS = 0x4; +pub const TIOCM_SR = 0x10; +pub const TIOCM_ST = 0x8; +pub const TIOCNOTTY = 0x20007471; +pub const TIOCNXCL = 0x2000740e; +pub const TIOCOUTQ = 0x40047473; +pub const TIOCPKT = 0x80047470; +pub const TIOCPKT_DATA = 0x0; +pub const TIOCPKT_DOSTOP = 0x20; +pub const TIOCPKT_FLUSHREAD = 0x1; +pub const TIOCPKT_FLUSHWRITE = 0x2; +pub const TIOCPKT_IOCTL = 0x40; +pub const TIOCPKT_NOSTOP = 0x10; +pub const TIOCPKT_START = 0x8; +pub const TIOCPKT_STOP = 0x4; +pub const TIOCPTMGET = 0x40287446; +pub const TIOCPTSNAME = 0x40287448; +pub const TIOCRCVFRAME = 0x80087445; +pub const TIOCREMOTE = 0x80047469; +pub const TIOCSBRK = 0x2000747b; +pub const TIOCSCTTY = 0x20007461; +pub const TIOCSDTR = 0x20007479; +pub const TIOCSETA = 0x802c7414; +pub const TIOCSETAF = 0x802c7416; +pub const TIOCSETAW = 0x802c7415; +pub const TIOCSETD = 0x8004741b; +pub const TIOCSFLAGS = 0x8004745c; +pub const TIOCSIG = 0x2000745f; +pub const TIOCSLINED = 0x80207443; +pub const TIOCSPGRP = 0x80047476; +pub const TIOCSQSIZE = 0x80047480; +pub const TIOCSSIZE = 0x80087467; +pub const TIOCSTART = 0x2000746e; +pub const TIOCSTAT = 0x80047465; +pub const TIOCSTI = 0x80017472; +pub const TIOCSTOP = 0x2000746f; +pub const TIOCSWINSZ = 0x80087467; +pub const TIOCUCNTL = 0x80047466; +pub const TIOCXMTFRAME = 0x80087444; + +pub const sockaddr = c.sockaddr; +pub const sockaddr_in = c.sockaddr_in; +pub const sockaddr_in6 = c.sockaddr_in6; + +fn unsigned(s: i32) u32 { + return @bitCast(u32, s); +} +fn signed(s: u32) i32 { + return @bitCast(i32, s); +} +pub fn WEXITSTATUS(s: i32) i32 { + return signed((unsigned(s) >> 8) & 0xff); +} +pub fn WTERMSIG(s: i32) i32 { + return signed(unsigned(s) & 0x7f); +} +pub fn WSTOPSIG(s: i32) i32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: i32) bool { + return WTERMSIG(s) == 0; +} + +pub fn WIFCONTINUED(s: i32) bool { + return ((s & 0x7f) == 0xffff); +} + +pub fn WIFSTOPPED(s: i32) bool { + return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); +} + +pub fn WIFSIGNALED(s: i32) bool { + return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +/// Get the errno from a syscall return value, or 0 for no error. +pub fn getErrno(r: usize) usize { + const signed_r = @bitCast(isize, r); + return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; +} + +pub fn dup2(old: i32, new: i32) usize { + return errnoWrap(c.dup2(old, new)); +} + +pub fn chdir(path: [*]const u8) usize { + return errnoWrap(c.chdir(path)); +} + +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { + return errnoWrap(c.execve(path, argv, envp)); +} + +pub fn fork() usize { + return errnoWrap(c.fork()); +} + +pub fn access(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.access(path, mode)); +} + +pub fn getcwd(buf: [*]u8, size: usize) usize { + return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; +} + +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { + return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); +} + +pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { + return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); +} + +pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { + return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; +} + +pub fn isatty(fd: i32) bool { + return c.isatty(fd) != 0; +} + +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return errnoWrap(c.readlink(path, buf_ptr, buf_len)); +} + +pub fn mkdir(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.mkdir(path, mode)); +} + +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), + length, + @bitCast(c_int, @intCast(c_uint, prot)), + @bitCast(c_int, c_uint(flags)), + fd, + offset, + ); + const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); + return errnoWrap(isize_result); +} + +pub fn munmap(address: usize, length: usize) usize { + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); +} + +pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); +} + +pub fn rmdir(path: [*]const u8) usize { + return errnoWrap(c.rmdir(path)); +} + +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { + return errnoWrap(c.symlink(existing, new)); +} + +pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); +} + +pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { + return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); +} + +pub fn pipe(fd: *[2]i32) usize { + return pipe2(fd, 0); +} + +pub fn pipe2(fd: *[2]i32, flags: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); +} + +pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); +} + +pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); +} + +pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { + return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); +} + +pub fn rename(old: [*]const u8, new: [*]const u8) usize { + return errnoWrap(c.rename(old, new)); +} + +pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { + return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); +} + +pub fn create(path: [*]const u8, perm: usize) usize { + return arch.syscall2(SYS_creat, @ptrToInt(path), perm); +} + +pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { + return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); +} + +pub fn close(fd: i32) usize { + return errnoWrap(c.close(fd)); +} + +pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { + return errnoWrap(c.lseek(fd, offset, whence)); +} + +pub fn exit(code: i32) noreturn { + c.exit(code); +} + +pub fn kill(pid: i32, sig: i32) usize { + return errnoWrap(c.kill(pid, sig)); +} + +pub fn unlink(path: [*]const u8) usize { + return errnoWrap(c.unlink(path)); +} + +pub fn waitpid(pid: i32, status: *i32, options: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); +} + +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { + return errnoWrap(c.nanosleep(req, rem)); +} + +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_gettime(clk_id, tp)); +} + +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_getres(clk_id, tp)); +} + +pub fn setuid(uid: u32) usize { + return errnoWrap(c.setuid(uid)); +} + +pub fn setgid(gid: u32) usize { + return errnoWrap(c.setgid(gid)); +} + +pub fn setreuid(ruid: u32, euid: u32) usize { + return errnoWrap(c.setreuid(ruid, euid)); +} + +pub fn setregid(rgid: u32, egid: u32) usize { + return errnoWrap(c.setregid(rgid, egid)); +} + +const NSIG = 32; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + /// signal handler + __sigaction_u: extern union { + __sa_handler: extern fn (i32) void, + __sa_sigaction: extern fn (i32, *__siginfo, usize) void, + }, + + /// see signal options + sa_flags: u32, + + /// signal mask to apply + sa_mask: sigset_t, +}; + +pub const _SIG_WORDS = 4; +pub const _SIG_MAXSIG = 128; + +pub inline fn _SIG_IDX(sig: usize) usize { + return sig - 1; +} +pub inline fn _SIG_WORD(sig: usize) usize { + return_SIG_IDX(sig) >> 5; +} +pub inline fn _SIG_BIT(sig: usize) usize { + return 1 << (_SIG_IDX(sig) & 31); +} +pub inline fn _SIG_VALID(sig: usize) usize { + return sig <= _SIG_MAXSIG and sig > 0; +} + +pub const sigset_t = extern struct { + __bits: [_SIG_WORDS]u32, +}; + +pub fn raise(sig: i32) usize { + return errnoWrap(c.raise(sig)); +} + +pub const Stat = c.Stat; +pub const dirent = c.dirent; +pub const timespec = c.timespec; + +pub fn fstat(fd: i32, buf: *c.Stat) usize { + return errnoWrap(c.fstat(fd, buf)); +} +pub const iovec = extern struct { + iov_base: [*]u8, + iov_len: usize, +}; + +pub const iovec_const = extern struct { + iov_base: [*]const u8, + iov_len: usize, +}; + +// TODO avoid libc dependency +pub fn kqueue() usize { + return errnoWrap(c.kqueue()); +} + +// TODO avoid libc dependency +pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { + return errnoWrap(c.kevent( + kq, + changelist.ptr, + @intCast(c_int, changelist.len), + eventlist.ptr, + @intCast(c_int, eventlist.len), + timeout, + )); +} + +// TODO avoid libc dependency +pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); +} + +// TODO avoid libc dependency +pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); +} + +// TODO avoid libc dependency +pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { + return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); +} + +// TODO avoid libc dependency + +/// Takes the return value from a syscall and formats it back in the way +/// that the kernel represents it to libc. Errno was a mistake, let's make +/// it go away forever. +fn errnoWrap(value: isize) usize { + return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); +} diff --git a/std/os/path.zig b/std/os/path.zig index 5ba345fceb..290f8ba17f 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -1226,7 +1226,7 @@ pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealErro const pathname_w = try windows_util.cStrToPrefixedFileW(pathname); return realW(out_buffer, pathname_w); }, - Os.freebsd, Os.macosx, Os.ios => { + Os.freebsd, Os.netbsd, Os.macosx, Os.ios => { // TODO instead of calling the libc function here, port the implementation to Zig const err = posix.getErrno(posix.realpath(pathname, out_buffer)); switch (err) { @@ -1267,7 +1267,7 @@ pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError! const pathname_w = try windows_util.sliceToPrefixedFileW(pathname); return realW(out_buffer, &pathname_w); }, - Os.macosx, Os.ios, Os.linux, Os.freebsd => { + Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { const pathname_c = try os.toPosixPath(pathname); return realC(out_buffer, &pathname_c); }, diff --git a/std/os/time.zig b/std/os/time.zig index 638fb41ff1..bce6f265de 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -14,7 +14,7 @@ pub const epoch = @import("epoch.zig"); /// Sleep for the specified duration pub fn sleep(nanoseconds: u64) void { switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd => { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { const s = nanoseconds / ns_per_s; const ns = nanoseconds % ns_per_s; posixSleep(@intCast(u63, s), @intCast(u63, ns)); @@ -62,7 +62,7 @@ pub fn timestamp() u64 { /// Get the posix timestamp, UTC, in milliseconds pub const milliTimestamp = switch (builtin.os) { Os.windows => milliTimestampWindows, - Os.linux, Os.freebsd => milliTimestampPosix, + Os.linux, Os.freebsd, Os.netbsd => milliTimestampPosix, Os.macosx, Os.ios => milliTimestampDarwin, else => @compileError("Unsupported OS"), }; @@ -178,7 +178,7 @@ pub const Timer = struct { debug.assert(err != windows.FALSE); self.start_time = @intCast(u64, start_time); }, - Os.linux, Os.freebsd => { + Os.linux, Os.freebsd, Os.netbsd => { //On Linux, seccomp can do arbitrary things to our ability to call // syscalls, including return any errno value it wants and // inconsistently throwing errors. Since we can't account for @@ -214,7 +214,7 @@ pub const Timer = struct { var clock = clockNative() - self.start_time; return switch (builtin.os) { Os.windows => @divFloor(clock * ns_per_s, self.frequency), - Os.linux, Os.freebsd => clock, + Os.linux, Os.freebsd, Os.netbsd => clock, Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom), else => @compileError("Unsupported OS"), }; @@ -235,7 +235,7 @@ pub const Timer = struct { const clockNative = switch (builtin.os) { Os.windows => clockWindows, - Os.linux, Os.freebsd => clockLinux, + Os.linux, Os.freebsd, Os.netbsd => clockLinux, Os.macosx, Os.ios => clockDarwin, else => @compileError("Unsupported OS"), }; From 8d2a902945ef97f28152c3d5a68bb974809c8539 Mon Sep 17 00:00:00 2001 From: Maya Rashish Date: Sun, 17 Feb 2019 08:34:20 +0200 Subject: [PATCH 062/134] freebsd: fix pointer cast in mmap --- std/os/freebsd/index.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/freebsd/index.zig b/std/os/freebsd/index.zig index a3ab5e7749..48c0ea5b1e 100644 --- a/std/os/freebsd/index.zig +++ b/std/os/freebsd/index.zig @@ -617,7 +617,7 @@ pub fn mkdir(path: [*]const u8, mode: u32) 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 2878efee86276c99c68defe2acd3a2ff0b9ea29d Mon Sep 17 00:00:00 2001 From: Maya Rashish Date: Sun, 17 Feb 2019 14:14:17 +0200 Subject: [PATCH 063/134] Undo local, unneeded patch --- cmake/Findllvm.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake index 490bdae66e..2f0afa09b7 100644 --- a/cmake/Findllvm.cmake +++ b/cmake/Findllvm.cmake @@ -50,6 +50,7 @@ NEED_TARGET("AMDGPU") NEED_TARGET("ARM") NEED_TARGET("BPF") NEED_TARGET("Hexagon") +NEED_TARGET("Lanai") NEED_TARGET("Mips") NEED_TARGET("MSP430") NEED_TARGET("NVPTX") From 6cf38369d216ef676a21fc014a869042a924029d Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 17 Feb 2019 14:07:14 +0100 Subject: [PATCH 064/134] Silence gcc8 class-memaccess warnings Use an explicit cast to tell gcc we know what we're doing. --- src/translate_c.cpp | 6 +++--- src/zig_clang.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index a51a671b06..d84b57738a 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -129,17 +129,17 @@ static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope * static ZigClangSourceLocation bitcast(clang::SourceLocation src) { ZigClangSourceLocation dest; - memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangSourceLocation)); return dest; } static ZigClangQualType bitcast(clang::QualType src) { ZigClangQualType dest; - memcpy(&dest, &src, sizeof(ZigClangQualType)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangQualType)); return dest; } static clang::QualType bitcast(ZigClangQualType src) { clang::QualType dest; - memcpy(&dest, &src, sizeof(ZigClangQualType)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangQualType)); return dest; } diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 4220d778a0..95d74d4536 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -140,24 +140,24 @@ static_assert((clang::UnaryOperatorKind)ZigClangUO_Real == clang::UO_Real, ""); static_assert(sizeof(ZigClangSourceLocation) == sizeof(clang::SourceLocation), ""); static ZigClangSourceLocation bitcast(clang::SourceLocation src) { ZigClangSourceLocation dest; - memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangSourceLocation)); return dest; } static clang::SourceLocation bitcast(ZigClangSourceLocation src) { clang::SourceLocation dest; - memcpy(&dest, &src, sizeof(ZigClangSourceLocation)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangSourceLocation)); return dest; } static_assert(sizeof(ZigClangQualType) == sizeof(clang::QualType), ""); static ZigClangQualType bitcast(clang::QualType src) { ZigClangQualType dest; - memcpy(&dest, &src, sizeof(ZigClangQualType)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangQualType)); return dest; } static clang::QualType bitcast(ZigClangQualType src) { clang::QualType dest; - memcpy(&dest, &src, sizeof(ZigClangQualType)); + memcpy(&dest, static_cast(&src), sizeof(ZigClangQualType)); return dest; } From fcf65f06c4eb42c8ecffbe9b7558f9cb84ef44e2 Mon Sep 17 00:00:00 2001 From: Benoit Jauvin-Girard Date: Sun, 17 Feb 2019 14:52:40 -0500 Subject: [PATCH 065/134] Fix std.math.powi so powi(x, +-0) = 1 for any x. --- std/math/powi.zig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/std/math/powi.zig b/std/math/powi.zig index 9c2a4a4965..dffb564da7 100644 --- a/std/math/powi.zig +++ b/std/math/powi.zig @@ -25,7 +25,7 @@ pub fn powi(comptime T: type, x: T, y: T) (error{ // powi(x, +-0) = 1 for any x if (y == 0 or y == -0) { - return 0; + return 1; } switch (x) { @@ -174,4 +174,11 @@ test "math.powi.special" { testing.expectError(error.Overflow, powi(u64, 2, 64)); testing.expectError(error.Overflow, powi(u17, 2, 17)); testing.expectError(error.Overflow, powi(u42, 2, 42)); + + testing.expect((try powi(u8, 6, 0)) == 1); + testing.expect((try powi(u16, 5, 0)) == 1); + testing.expect((try powi(u32, 12, 0)) == 1); + testing.expect((try powi(u64, 34, 0)) == 1); + testing.expect((try powi(u17, 16, 0)) == 1); + testing.expect((try powi(u42, 34, 0)) == 1); } From b93405c24bd3c58f68a272f32fe764a994e7aae6 Mon Sep 17 00:00:00 2001 From: Maya Rashish Date: Sun, 17 Feb 2019 23:50:30 +0200 Subject: [PATCH 066/134] Don't provide a bogus definition of EVFILT_USER --- std/os/netbsd/index.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/std/os/netbsd/index.zig b/std/os/netbsd/index.zig index 53de61126c..ebabc3c801 100644 --- a/std/os/netbsd/index.zig +++ b/std/os/netbsd/index.zig @@ -283,9 +283,6 @@ pub const EVFILT_TIMER = 6; /// Filesystem events pub const EVFILT_FS = 7; -/// XXX no EVFILT_USER, but what is it -pub const EVFILT_USER = 0; - /// On input, NOTE_TRIGGER causes the event to be triggered for output. pub const NOTE_TRIGGER = 0x08000000; From 51783510b9b972fab52d429ff3311b0fe8402e42 Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Thu, 14 Feb 2019 15:54:37 -0800 Subject: [PATCH 067/134] Deduplicate compile log statement warnings --- src/all_types.hpp | 1 + src/ir.cpp | 10 +++++++--- src/parser.cpp | 2 ++ test/compile_errors.zig | 18 ++++++++++++++++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6fbd987b9e..f57c3124da 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -648,6 +648,7 @@ struct AstNodeFnCallExpr { ZigList params; bool is_builtin; bool is_async; + bool seen; // used by @compileLog AstNode *async_allocator; }; diff --git a/src/ir.cpp b/src/ir.cpp index 0fcbb60fe8..063be4e952 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17194,9 +17194,13 @@ static IrInstruction *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstr } fprintf(stderr, "\n"); - // Here we bypass higher level functions such as ir_add_error because we do not want - // invalidate_exec to be called. - add_node_error(ira->codegen, instruction->base.source_node, buf_sprintf("found compile log statement")); + auto *expr = &instruction->base.source_node->data.fn_call_expr; + if (!expr->seen) { + // Here we bypass higher level functions such as ir_add_error because we do not want + // invalidate_exec to be called. + add_node_error(ira->codegen, instruction->base.source_node, buf_sprintf("found compile log statement")); + } + expr->seen = true; return ir_const_void(ira, &instruction->base); } diff --git a/src/parser.cpp b/src/parser.cpp index 3a6ce04647..6fe78c14c3 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2739,6 +2739,7 @@ static AstNode *ast_parse_async_prefix(ParseContext *pc) { AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async); res->data.fn_call_expr.is_async = true; + res->data.fn_call_expr.seen = false; if (eat_token_if(pc, TokenIdCmpLessThan) != nullptr) { AstNode *prefix_expr = ast_expect(pc, ast_parse_prefix_expr); expect_token(pc, TokenIdCmpGreaterThan); @@ -2759,6 +2760,7 @@ static AstNode *ast_parse_fn_call_argumnets(ParseContext *pc) { AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren); res->data.fn_call_expr.params = params; + res->data.fn_call_expr.seen = false; return res; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9ef4af4162..7953eda2ab 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -137,6 +137,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", ); + cases.addTest( + "compile log statement warning deduplication in generic fn", + \\export fn entry() void { + \\ inner(1); + \\ inner(2); + \\} + \\fn inner(comptime n: usize) void { + \\ comptime var i = 0; + \\ inline while (i < n) : (i += 1) { @compileLog("!@#$"); } + \\} + , + ".tmp_source.zig:7:39: error: found compile log statement", + ); + cases.addTest( "@truncate undefined value", \\export fn entry() void { @@ -4920,7 +4934,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "non-printable invalid character", - "\xff\xfe" ++ + "\xff\xfe" ++ \\fn test() bool {\r \\ true\r \\} @@ -5480,7 +5494,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ Baz: void, \\}; \\comptime { - \\ var foo = Foo {.Baz = {}}; + \\ var foo = Foo {.Baz = {}}; \\ const bar_val = foo.Bar; \\} , From c70ee9177e5b0095c152b66fe8f22cd870e778b9 Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Sun, 17 Feb 2019 12:47:53 -0800 Subject: [PATCH 068/134] Check for duped error messages in compile tests --- test/compile_errors.zig | 30 ++++++++++------- test/tests.zig | 75 +++++++++++++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7953eda2ab..a18884cc9a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -137,19 +137,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", ); - cases.addTest( - "compile log statement warning deduplication in generic fn", - \\export fn entry() void { - \\ inner(1); - \\ inner(2); - \\} - \\fn inner(comptime n: usize) void { - \\ comptime var i = 0; - \\ inline while (i < n) : (i += 1) { @compileLog("!@#$"); } - \\} - , - ".tmp_source.zig:7:39: error: found compile log statement", - ); + cases.addCase(x: { + var tc = cases.create( + "compile log statement warning deduplication in generic fn", + \\export fn entry() void { + \\ inner(1); + \\ inner(2); + \\} + \\fn inner(comptime n: usize) void { + \\ comptime var i = 0; + \\ inline while (i < n) : (i += 1) { @compileLog("!@#$"); } + \\} + , + ".tmp_source.zig:7:39: error: found compile log statement", + ); + tc.expect_exact = true; + break :x tc; + }); cases.addTest( "@truncate undefined value", diff --git a/test/tests.zig b/test/tests.zig index 800ddc1ccd..656c05cb9b 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -536,6 +536,7 @@ pub const CompileErrorContext = struct { name: []const u8, sources: ArrayList(SourceFile), expected_errors: ArrayList([]const u8), + expect_exact: bool, link_libc: bool, is_exe: bool, is_test: bool, @@ -565,6 +566,26 @@ pub const CompileErrorContext = struct { case: *const TestCase, build_mode: Mode, + const ErrLineIter = struct { + lines: mem.SplitIterator, + + const source_file = ".tmp_source.zig"; + + fn init(input: []const u8) ErrLineIter { + return ErrLineIter { + .lines = mem.separate(input, "\n"), + }; + } + + fn next(self: *ErrLineIter) ?[]const u8 { + while (self.lines.next()) |line| { + if (mem.indexOf(u8, line, source_file) != null) + return line; + } + return null; + } + }; + pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; @@ -674,19 +695,50 @@ pub const CompileErrorContext = struct { return error.TestFailed; } - for (self.case.expected_errors.toSliceConst()) |expected_error| { - if (mem.indexOf(u8, stderr, expected_error) == null) { - warn( - \\ - \\========= Expected this compile error: ========= - \\{} - \\================================================ - \\{} - \\ - , expected_error, stderr); - return error.TestFailed; + var ok = true; + if (self.case.expect_exact) { + var err_iter = ErrLineIter.init(stderr); + var i: usize = 0; + ok = while (err_iter.next()) |line| : (i += 1) { + if (i >= self.case.expected_errors.len) break false; + const expected = self.case.expected_errors.at(i); + if (mem.indexOf(u8, line, expected) == null) break false; + continue; + } else true; + + ok = ok and i == self.case.expected_errors.len; + + if (!ok) { + warn("\n======== Expected these compile errors: ========\n"); + for (self.case.expected_errors.toSliceConst()) |expected| { + warn("{}\n", expected); + } + } + } else { + for (self.case.expected_errors.toSliceConst()) |expected| { + if (mem.indexOf(u8, stderr, expected) == null) { + warn( + \\=========== Expected compile error: ============ + \\{} + \\ + , expected + ); + ok = false; + break; + } } } + + if (!ok) { + warn( + \\================= Full output: ================= + \\{} + \\ + , stderr + ); + return error.TestFailed; + } + warn("OK\n"); } }; @@ -704,6 +756,7 @@ pub const CompileErrorContext = struct { .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_errors = ArrayList([]const u8).init(self.b.allocator), + .expect_exact = false, .link_libc = false, .is_exe = false, .is_test = false, From 7e549540527fec891bc67d5e657f82b7087530a3 Mon Sep 17 00:00:00 2001 From: Quetzal Bradley Date: Sun, 17 Feb 2019 13:08:08 -0800 Subject: [PATCH 069/134] fix openWriteNoClobber and add test --- std/io_test.zig | 11 +++++++++++ std/os/file.zig | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/std/io_test.zig b/std/io_test.zig index fb6e0ae7e9..87b970edf8 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -29,6 +29,17 @@ test "write a file, read it, then delete it" { try st.print("end"); try buf_stream.flush(); } + + { + // make sure openWriteNoClobber doesn't harm the file + if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { + unreachable; + } + else |err| { + std.debug.assert(err == os.File.OpenError.PathAlreadyExists); + } + } + { var file = try os.File.openRead(tmp_file_name); defer file.close(); diff --git a/std/os/file.zig b/std/os/file.zig index 2ae547c694..cdd28b233c 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -105,7 +105,7 @@ pub const File = struct { pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { if (is_posix) { const path_c = try os.toPosixPath(path); - return openWriteNoClobberC(path_c, file_mode); + return openWriteNoClobberC(&path_c, file_mode); } else if (is_windows) { const path_w = try windows_util.sliceToPrefixedFileW(path); return openWriteNoClobberW(&path_w, file_mode); From 74d9593d85eac83f1b3a42cb07e57dd3c9f8e61e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 00:13:18 -0500 Subject: [PATCH 070/134] README: add NetBSD to support table --- README.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 3ac4f429f2..c6d6e8895a 100644 --- a/README.md +++ b/README.md @@ -90,26 +90,26 @@ clarity. #### Support Table -| | freestanding | linux | macosx | windows | freebsd | UEFI | other | -|--------|--------------|--------|--------|---------|---------|--------|--------| -|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 3 | -|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | -|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | -|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | -|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | -|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | -|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | +| | freestanding | linux | macosx | windows | freebsd | netbsd | UEFI | other | +|--------|--------------|--------|--------|---------|---------|------- | -------|--------| +|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | +|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | +|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | ## Community From 7d762648a4f8cf20df3939a5b957bc751d6e4bb5 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 17 Feb 2019 23:39:13 +0100 Subject: [PATCH 071/134] Add align attribute for params pointers --- src/codegen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index d54d06f3b5..9085d4ea7c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2280,6 +2280,9 @@ 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 (get_codegen_ptr_type(param_type) != nullptr) { + addLLVMArgAttrInt(llvm_fn, (unsigned)gen_index, "align", get_ptr_align(g, param_type)); + } if (type_is_nonnull_ptr(param_type)) { addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); } From e280dce30f7a5d5cf6fc2001bac5e3918f8b5a39 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 18 Feb 2019 16:26:45 +0100 Subject: [PATCH 072/134] Translate parameterless C functions (#1978) Both FunctionNoProto and FunctionProto are subclasses of FunctionType, the only difference is that the former is parameterless. --- src/translate_c.cpp | 22 ++++++++++++++-------- test/translate_c.zig | 8 ++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index d84b57738a..1600fa9bf7 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -982,11 +982,12 @@ static AstNode *trans_type(Context *c, const clang::Type *ty, const clang::Sourc } } case clang::Type::FunctionProto: + case clang::Type::FunctionNoProto: { - const clang::FunctionProtoType *fn_proto_ty = static_cast(ty); + const clang::FunctionType *fn_ty = static_cast(ty); AstNode *proto_node = trans_create_node(c, NodeTypeFnProto); - switch (fn_proto_ty->getCallConv()) { + switch (fn_ty->getCallConv()) { case clang::CC_C: // __attribute__((cdecl)) proto_node->data.fn_proto.cc = CallingConventionC; proto_node->data.fn_proto.is_extern = true; @@ -1041,13 +1042,10 @@ static AstNode *trans_type(Context *c, const clang::Type *ty, const clang::Sourc return nullptr; } - proto_node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic(); - size_t param_count = fn_proto_ty->getNumParams(); - - if (fn_proto_ty->getNoReturnAttr()) { + if (fn_ty->getNoReturnAttr()) { proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "noreturn"); } else { - proto_node->data.fn_proto.return_type = trans_qual_type(c, fn_proto_ty->getReturnType(), + proto_node->data.fn_proto.return_type = trans_qual_type(c, fn_ty->getReturnType(), source_loc); if (proto_node->data.fn_proto.return_type == nullptr) { emit_warning(c, source_loc, "unsupported function proto return type"); @@ -1070,6 +1068,15 @@ static AstNode *trans_type(Context *c, const clang::Type *ty, const clang::Sourc proto_node->data.fn_proto.name = buf_create_from_str(fn_name); } + if (ty->getTypeClass() == clang::Type::FunctionNoProto) { + return proto_node; + } + + const clang::FunctionProtoType *fn_proto_ty = static_cast(ty); + + proto_node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic(); + size_t param_count = fn_proto_ty->getNumParams(); + for (size_t i = 0; i < param_count; i += 1) { clang::QualType qt = fn_proto_ty->getParamType(i); AstNode *param_type_node = trans_qual_type(c, qt, source_loc); @@ -1153,7 +1160,6 @@ static AstNode *trans_type(Context *c, const clang::Type *ty, const clang::Sourc case clang::Type::DependentSizedExtVector: case clang::Type::Vector: case clang::Type::ExtVector: - case clang::Type::FunctionNoProto: case clang::Type::UnresolvedUsing: case clang::Type::Adjusted: case clang::Type::TypeOfExpr: diff --git a/test/translate_c.zig b/test/translate_c.zig index 6a26b99104..eabd01ef62 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1417,6 +1417,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} ); + cases.addC("Parameterless function prototypes", + \\void foo() {} + \\void bar(void) {} + , + \\pub export fn foo() void {} + \\pub export fn bar() void {} + ); + // cases.add("empty array with initializer", // "int a[4] = {};" // , From d2fb95af8804229181c4d28506ec37d94b9926e6 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Mon, 18 Feb 2019 17:22:14 +0100 Subject: [PATCH 073/134] Fixed std.testing.expectEqual --- std/testing.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/std/testing.zig b/std/testing.zig index a47c5984a0..2f14974481 100644 --- a/std/testing.zig +++ b/std/testing.zig @@ -59,6 +59,7 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { switch (pointer.size) { builtin.TypeInfo.Pointer.Size.One, builtin.TypeInfo.Pointer.Size.Many, + builtin.TypeInfo.Pointer.Size.C, => { if (actual != expected) { std.debug.panic("expected {}, found {}", expected, actual); From 9b3013d2f6e416f31610f3dc94c5ff8b44836463 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sat, 9 Feb 2019 18:04:38 -0600 Subject: [PATCH 074/134] make @enumToInt work on union(enum) closes #1711 --- doc/langref.html.in | 2 +- src/ir.cpp | 55 ++++++++++++++++++++++++++-------- src/target.cpp | 3 +- test/stage1/behavior/union.zig | 11 ++++--- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 1341bf1be5..0d71dbc593 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5662,7 +5662,7 @@ test "main" { {#header_open|@enumToInt#}
{#syntax#}@enumToInt(enum_value: var) var{#endsyntax#}

- Converts an enumeration value into its integer tag type. + Converts an enumeration or tagged union value into its integer tag type.

If the enum has only 1 possible value, the resut is a {#syntax#}comptime_int{#endsyntax#} diff --git a/src/ir.cpp b/src/ir.cpp index 063be4e952..a33ba13eed 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10312,6 +10312,10 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt); ZigType *actual_type = target->value.type; + + if (actual_type->id == ZigTypeIdUnion) + actual_type = actual_type->data.unionation.tag_type; + if ((err = ensure_complete_type(ira->codegen, actual_type))) return ira->codegen->invalid_instruction; @@ -10330,7 +10334,11 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour if (!val) return ira->codegen->invalid_instruction; IrInstruction *result = ir_const(ira, source_instr, wanted_type); - init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); + if (target->value.type->id == ZigTypeIdUnion) + init_const_bigint(&result->value, wanted_type, &val->data.x_union.tag); + else + init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); + return result; } @@ -10341,12 +10349,18 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, - &actual_type->data.enumeration.fields[0].value); + &actual_type->data.enumeration.fields[0].value); + return result; } - IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, - source_instr->source_node, target); + IrInstruction *result = nullptr; + if (target->value.type->id == ZigTypeIdUnion) + result = ir_build_union_tag(&ira->new_irb, source_instr->scope, + source_instr->source_node, target); + else + result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, + source_instr->source_node, target); result->value.type = wanted_type; return result; } @@ -14358,12 +14372,12 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source if (dst_size == 0) return ErrorNone; opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %zu bytes from null pointer", + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer", dst_size)); return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } @@ -14377,7 +14391,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source src_size = elem_size * (array_val->type->data.array.len - elem_index); if (dst_size > src_size) { opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %zu bytes from %s at index %" ZIG_PRI_usize " which is %zu bytes", + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); return ErrorSemanticAnalyzeFail; } @@ -21960,21 +21974,36 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { Error err; IrInstruction *target = instruction->target->child; - if (type_is_invalid(target->value.type)) + ZigType *enum_type = target->value.type; + if (type_is_invalid(enum_type)) return ira->codegen->invalid_instruction; - if (target->value.type->id != ZigTypeIdEnum) { + if (enum_type->id == ZigTypeIdUnion) { + if ((err = ensure_complete_type(ira->codegen, enum_type))) + return ira->codegen->invalid_instruction; + + AstNode *decl_node = enum_type->data.unionation.decl_node; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + assert(enum_type->data.unionation.tag_type != nullptr); + enum_type = target->value.type->data.unionation.tag_type; + } else { + ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("union '%s' has no tag", + buf_ptr(&enum_type->name))); + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->invalid_instruction; + } + } else if (enum_type->id != ZigTypeIdEnum) { ir_add_error(ira, instruction->target, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected enum or union(enum), found type '%s'", buf_ptr(&enum_type->name))); return ira->codegen->invalid_instruction; } - if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown))) + if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - ZigType *tag_type = target->value.type->data.enumeration.tag_int_type; + ZigType *int_type = enum_type->data.enumeration.tag_int_type; - return ir_analyze_enum_to_int(ira, &instruction->base, target, tag_type); + return ir_analyze_enum_to_int(ira, &instruction->base, target, int_type); } static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { diff --git a/src/target.cpp b/src/target.cpp index cea7826313..5352a21826 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -9,6 +9,7 @@ #include "error.hpp" #include "target.hpp" #include "util.hpp" +#include "os.hpp" #include @@ -848,7 +849,7 @@ const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t versio if (is_static) { return ".a"; } else { - return buf_ptr(buf_sprintf(".so.%zu", version_major)); + return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize, version_major)); } } } diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index 0a4e2cfb92..f18cdf13ff 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -126,7 +126,7 @@ const MultipleChoice = union(enum(u32)) { test "simple union(enum(u32))" { var x = MultipleChoice.C; expect(x == MultipleChoice.C); - expect(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + expect(@enumToInt(x) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -148,7 +148,7 @@ test "union(enum(u32)) with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - expect(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + expect(@enumToInt(x) == 60); expect(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, @@ -345,8 +345,7 @@ test "union with only 1 field casted to its enum type which has enum value speci var e = Expr{ .Literal = Literal{ .Bool = true } }; comptime expect(@TagType(Tag) == comptime_int); - var t = Tag(e); - expect(t == Expr.Literal); - expect(@enumToInt(t) == 33); - comptime expect(@enumToInt(t) == 33); + expect(Tag(e) == Expr.Literal); + expect(@enumToInt(e) == 33); + comptime expect(@enumToInt(e) == 33); } From 7a84fe79b9d61bb3d4e21c169cc2b58722b194ce Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 12:56:17 -0500 Subject: [PATCH 075/134] pull request fixups --- doc/langref.html.in | 32 ++++---- src/ir.cpp | 137 ++++++++++++++------------------- test/stage1/behavior/union.zig | 26 +++++-- test/tests.zig | 12 +-- 4 files changed, 99 insertions(+), 108 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0d71dbc593..ac3864adda 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2221,8 +2221,9 @@ test "packed enum" { {#header_close#} {#header_open|union#} {#code_begin|test|union#} -const assert = @import("std").debug.assert; -const mem = @import("std").mem; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; // A union has only 1 active field at a time. const Payload = union { @@ -2231,16 +2232,19 @@ const Payload = union { Bool: bool, }; test "simple union" { - var payload = Payload {.Int = 1234}; + var payload = Payload{ .Int = 1234 }; // payload.Float = 12.34; // ERROR! field not active assert(payload.Int == 1234); // You can activate another field by assigning the entire union. - payload = Payload {.Float = 12.34}; + payload = Payload{ .Float = 12.34 }; assert(payload.Float == 12.34); } // Unions can be given an enum tag type: -const ComplexTypeTag = enum { Ok, NotOk }; +const ComplexTypeTag = enum { + Ok, + NotOk, +}; const ComplexType = union(ComplexTypeTag) { Ok: u8, NotOk: void, @@ -2248,11 +2252,11 @@ const ComplexType = union(ComplexTypeTag) { // Declare a specific instance of the union variant. test "declare union value" { - const c = ComplexType { .Ok = 0 }; + const c = ComplexType{ .Ok = 0 }; assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); } -// @TagType can be used to access the enum tag type of a union. +// @TagType can be used to access the enum tag type of a tagged union. test "@TagType" { assert(@TagType(ComplexType) == ComplexTypeTag); } @@ -2266,7 +2270,7 @@ const Foo = union(enum) { None, }; test "union variant switch" { - const p = Foo { .Number = 54 }; + const p = Foo{ .Number = 54 }; const what_is_it = switch (p) { // Capture by reference Foo.String => |*x| blk: { @@ -2301,14 +2305,13 @@ const Variant = union(enum) { }; test "union method" { - var v1 = Variant { .Int = 1 }; - var v2 = Variant { .Bool = false }; + var v1 = Variant{ .Int = 1 }; + var v2 = Variant{ .Bool = false }; assert(v1.truthy()); assert(!v2.truthy()); } - const Small = union { A: i32, B: bool, @@ -5660,12 +5663,13 @@ test "main" { {#header_close#} {#header_open|@enumToInt#} -

{#syntax#}@enumToInt(enum_value: var) var{#endsyntax#}
+
{#syntax#}@enumToInt(enum_or_tagged_union: var) var{#endsyntax#}

- Converts an enumeration or tagged union value into its integer tag type. + Converts an enumeration value into its integer tag type. When a tagged union is passed, + the tag value is used as the enumeration value.

- If the enum has only 1 possible value, the resut is a {#syntax#}comptime_int{#endsyntax#} + If there is only one possible enum value, the resut is a {#syntax#}comptime_int{#endsyntax#} known at {#link|comptime#}.

{#see_also|@intToEnum#} diff --git a/src/ir.cpp b/src/ir.cpp index a33ba13eed..d51df52373 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10305,63 +10305,75 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s return result; } -static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *target, ZigType *wanted_type) -{ +static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *union_type) { + assert(union_type->id == ZigTypeIdUnion); + Error err; - assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt); + if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; - ZigType *actual_type = target->value.type; + AstNode *decl_node = union_type->data.unionation.decl_node; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + assert(union_type->data.unionation.tag_type != nullptr); + return union_type->data.unionation.tag_type; + } else { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union '%s' has no tag", + buf_ptr(&union_type->name))); + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->builtin_types.entry_invalid; + } +} - if (actual_type->id == ZigTypeIdUnion) - actual_type = actual_type->data.unionation.tag_type; +static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) { + Error err; - if ((err = ensure_complete_type(ira->codegen, actual_type))) - return ira->codegen->invalid_instruction; - - if (wanted_type != actual_type->data.enumeration.tag_int_type) { - ir_add_error(ira, source_instr, - buf_sprintf("enum to integer cast to '%s' instead of its tag type, '%s'", - buf_ptr(&wanted_type->name), - buf_ptr(&actual_type->data.enumeration.tag_int_type->name))); + IrInstruction *enum_target; + ZigType *enum_type; + if (target->value.type->id == ZigTypeIdUnion) { + enum_type = ir_resolve_union_tag_type(ira, target, target->value.type); + if (type_is_invalid(enum_type)) + return ira->codegen->invalid_instruction; + enum_target = ir_implicit_cast(ira, target, enum_type); + if (type_is_invalid(enum_target->value.type)) + return ira->codegen->invalid_instruction; + } else if (target->value.type->id == ZigTypeIdEnum) { + enum_target = target; + enum_type = target->value.type; + } else { + ir_add_error(ira, target, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } - assert(actual_type->id == ZigTypeIdEnum); + if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; - if (instr_is_comptime(target)) { - ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + ZigType *tag_type = enum_type->data.enumeration.tag_int_type; + assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt); + + if (instr_is_comptime(enum_target)) { + ConstExprValue *val = ir_resolve_const(ira, enum_target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, source_instr, wanted_type); - if (target->value.type->id == ZigTypeIdUnion) - init_const_bigint(&result->value, wanted_type, &val->data.x_union.tag); - else - init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); - + IrInstruction *result = ir_const(ira, source_instr, tag_type); + init_const_bigint(&result->value, tag_type, &val->data.x_enum_tag); return result; } // If there is only one possible tag, then we know at comptime what it is. - if (actual_type->data.enumeration.layout == ContainerLayoutAuto && - actual_type->data.enumeration.src_field_count == 1) + if (enum_type->data.enumeration.layout == ContainerLayoutAuto && + enum_type->data.enumeration.src_field_count == 1) { - assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_const(ira, source_instr, wanted_type); - init_const_bigint(&result->value, wanted_type, - &actual_type->data.enumeration.fields[0].value); - + assert(tag_type == ira->codegen->builtin_types.entry_num_lit_int); + IrInstruction *result = ir_const(ira, source_instr, tag_type); + init_const_bigint(&result->value, tag_type, + &enum_type->data.enumeration.fields[0].value); return result; } - IrInstruction *result = nullptr; - if (target->value.type->id == ZigTypeIdUnion) - result = ir_build_union_tag(&ira->new_irb, source_instr->scope, - source_instr->source_node, target); - else - result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, - source_instr->source_node, target); - result->value.type = wanted_type; + IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope, + source_instr->source_node, enum_target); + result->value.type = tag_type; return result; } @@ -21378,20 +21390,10 @@ static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruct return ir_const_type(ira, &instruction->base, enum_type->data.enumeration.tag_int_type); } else if (enum_type->id == ZigTypeIdUnion) { - if ((err = ensure_complete_type(ira->codegen, enum_type))) + ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target, enum_type); + if (type_is_invalid(tag_type)) return ira->codegen->invalid_instruction; - - AstNode *decl_node = enum_type->data.unionation.decl_node; - if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { - assert(enum_type->data.unionation.tag_type != nullptr); - - return ir_const_type(ira, &instruction->base, enum_type->data.unionation.tag_type); - } else { - ErrorMsg *msg = ir_add_error(ira, target_inst, buf_sprintf("union '%s' has no tag", - buf_ptr(&enum_type->name))); - add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); - return ira->codegen->invalid_instruction; - } + return ir_const_type(ira, &instruction->base, tag_type); } else { ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", buf_ptr(&enum_type->name))); @@ -21972,38 +21974,11 @@ static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) { - Error err; IrInstruction *target = instruction->target->child; - ZigType *enum_type = target->value.type; - if (type_is_invalid(enum_type)) + if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; - if (enum_type->id == ZigTypeIdUnion) { - if ((err = ensure_complete_type(ira->codegen, enum_type))) - return ira->codegen->invalid_instruction; - - AstNode *decl_node = enum_type->data.unionation.decl_node; - if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { - assert(enum_type->data.unionation.tag_type != nullptr); - enum_type = target->value.type->data.unionation.tag_type; - } else { - ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("union '%s' has no tag", - buf_ptr(&enum_type->name))); - add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); - return ira->codegen->invalid_instruction; - } - } else if (enum_type->id != ZigTypeIdEnum) { - ir_add_error(ira, instruction->target, - buf_sprintf("expected enum or union(enum), found type '%s'", buf_ptr(&enum_type->name))); - return ira->codegen->invalid_instruction; - } - - if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_instruction; - - ZigType *int_type = enum_type->data.enumeration.tag_int_type; - - return ir_analyze_enum_to_int(ira, &instruction->base, target, int_type); + return ir_analyze_enum_to_int(ira, &instruction->base, target); } static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index f18cdf13ff..bbba867667 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -126,7 +126,7 @@ const MultipleChoice = union(enum(u32)) { test "simple union(enum(u32))" { var x = MultipleChoice.C; expect(x == MultipleChoice.C); - expect(@enumToInt(x) == 60); + expect(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -148,7 +148,7 @@ test "union(enum(u32)) with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - expect(@enumToInt(x) == 60); + expect(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); expect(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, @@ -345,7 +345,23 @@ test "union with only 1 field casted to its enum type which has enum value speci var e = Expr{ .Literal = Literal{ .Bool = true } }; comptime expect(@TagType(Tag) == comptime_int); - expect(Tag(e) == Expr.Literal); - expect(@enumToInt(e) == 33); - comptime expect(@enumToInt(e) == 33); + var t = Tag(e); + expect(t == Expr.Literal); + expect(@enumToInt(t) == 33); + comptime expect(@enumToInt(t) == 33); +} + +test "@enumToInt works on unions" { + const Bar = union(enum) { + A: bool, + B: u8, + C, + }; + + const a = Bar{ .A = true }; + var b = Bar{ .B = undefined }; + var c = Bar.C; + expect(@enumToInt(a) == 0); + expect(@enumToInt(b) == 1); + expect(@enumToInt(c) == 2); } diff --git a/test/tests.zig b/test/tests.zig index 656c05cb9b..f9c9207f05 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -572,9 +572,7 @@ pub const CompileErrorContext = struct { const source_file = ".tmp_source.zig"; fn init(input: []const u8) ErrLineIter { - return ErrLineIter { - .lines = mem.separate(input, "\n"), - }; + return ErrLineIter{ .lines = mem.separate(input, "\n") }; } fn next(self: *ErrLineIter) ?[]const u8 { @@ -718,11 +716,10 @@ pub const CompileErrorContext = struct { for (self.case.expected_errors.toSliceConst()) |expected| { if (mem.indexOf(u8, stderr, expected) == null) { warn( - \\=========== Expected compile error: ============ + \\\n=========== Expected compile error: ============ \\{} \\ - , expected - ); + , expected); ok = false; break; } @@ -734,8 +731,7 @@ pub const CompileErrorContext = struct { \\================= Full output: ================= \\{} \\ - , stderr - ); + , stderr); return error.TestFailed; } From 28bf768883d2411d4cd75582d396d465ab6a54b2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 16:47:30 -0500 Subject: [PATCH 076/134] export _mh_execute_header with weak linkage * also fix extern variables with initialiaztion values to generate runtime code * remove the workaround in example/shared_library/mathtest.zig * introduce the ability for global variables to have Weak and LinkOnce linkage * fix `@export` to work for non-functions. this code needs to be audited though. * fix comptime ptrcast not keeping bigger alignment * fix linker warnings when targeting darwin closes #1903 --- example/shared_library/mathtest.zig | 9 ---- src/all_types.hpp | 4 +- src/analyze.cpp | 2 +- src/codegen.cpp | 22 ++++++++-- src/ir.cpp | 66 +++++++++++++++++++++++------ src/link.cpp | 10 ++++- std/c/darwin.zig | 11 ++++- std/debug/index.zig | 2 +- std/os/index.zig | 4 +- test/stage1/behavior/ptrcast.zig | 8 ++++ 10 files changed, 107 insertions(+), 31 deletions(-) diff --git a/example/shared_library/mathtest.zig b/example/shared_library/mathtest.zig index 96e41f847c..a04ec1544d 100644 --- a/example/shared_library/mathtest.zig +++ b/example/shared_library/mathtest.zig @@ -1,12 +1,3 @@ -// TODO Remove this workaround -comptime { - const builtin = @import("builtin"); - if (builtin.os == builtin.Os.macosx) { - @export("__mh_execute_header", _mh_execute_header, builtin.GlobalLinkage.Weak); - } -} -var _mh_execute_header = extern struct {x: usize}{.x = 0}; - export fn add(a: i32, b: i32) i32 { return a + b; } diff --git a/src/all_types.hpp b/src/all_types.hpp index f57c3124da..87f4c889ac 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1853,7 +1853,9 @@ struct CodeGen { enum VarLinkage { VarLinkageInternal, - VarLinkageExport, + VarLinkageExportStrong, + VarLinkageExportWeak, + VarLinkageExportLinkOnce, VarLinkageExternal, }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 37c0041314..934da61186 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3746,7 +3746,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { VarLinkage linkage; if (is_export) { - linkage = VarLinkageExport; + linkage = VarLinkageExportStrong; } else if (is_extern) { linkage = VarLinkageExternal; } else { diff --git a/src/codegen.cpp b/src/codegen.cpp index 9085d4ea7c..843f8cfd17 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6421,6 +6421,22 @@ static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) { } } +static LLVMLinkage var_linkage_to_llvm(VarLinkage var_linkage) { + switch (var_linkage) { + case VarLinkageInternal: + return LLVMInternalLinkage; + case VarLinkageExportStrong: + return LLVMExternalLinkage; + case VarLinkageExportWeak: + return LLVMWeakODRLinkage; + case VarLinkageExportLinkOnce: + return LLVMLinkOnceODRLinkage; + case VarLinkageExternal: + return LLVMExternalLinkage; + } + zig_unreachable(); +} + static void do_code_gen(CodeGen *g) { assert(!g->errors.length); @@ -6501,21 +6517,21 @@ static void do_code_gen(CodeGen *g) { global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name)); // TODO debug info for the extern variable - LLVMSetLinkage(global_value, LLVMExternalLinkage); + LLVMSetLinkage(global_value, var_linkage_to_llvm(var->linkage)); maybe_import_dll(g, global_value, GlobalLinkageIdStrong); LLVMSetAlignment(global_value, var->align_bytes); LLVMSetGlobalConstant(global_value, var->gen_is_const); set_global_tls(g, var, global_value); } } else { - bool exported = (var->linkage == VarLinkageExport); + bool exported = (var->linkage != VarLinkageInternal); const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported)); render_const_val(g, var->const_value, mangled_name); render_const_val_global(g, var->const_value, mangled_name); global_value = var->const_value->global_refs->llvm_global; if (exported) { - LLVMSetLinkage(global_value, LLVMExternalLinkage); + LLVMSetLinkage(global_value, var_linkage_to_llvm(var->linkage)); maybe_export_dll(g, global_value, GlobalLinkageIdStrong); } if (tld_var->section_name) { diff --git a/src/ir.cpp b/src/ir.cpp index d51df52373..5c5893cdd5 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13133,6 +13133,20 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value); } +static VarLinkage global_linkage_to_var_linkage(GlobalLinkageId id) { + switch (id) { + case GlobalLinkageIdStrong: + return VarLinkageExportStrong; + case GlobalLinkageIdWeak: + return VarLinkageExportWeak; + case GlobalLinkageIdLinkOnce: + return VarLinkageExportLinkOnce; + case GlobalLinkageIdInternal: + return VarLinkageInternal; + } + zig_unreachable(); +} + static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { IrInstruction *name = instruction->name->child; Buf *symbol_name = ir_resolve_str(ira, name); @@ -13161,6 +13175,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here")); } + bool want_var_export = false; switch (target->value.type->id) { case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -13196,6 +13211,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported struct value must be declared extern")); add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here")); + } else { + want_var_export = true; } break; case ZigTypeIdUnion: @@ -13203,6 +13220,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported union value must be declared extern")); add_error_note(ira->codegen, msg, target->value.type->data.unionation.decl_node, buf_sprintf("declared here")); + } else { + want_var_export = true; } break; case ZigTypeIdEnum: @@ -13210,6 +13229,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio ErrorMsg *msg = ir_add_error(ira, target, buf_sprintf("exported enum value must be declared extern")); add_error_note(ira->codegen, msg, target->value.type->data.enumeration.decl_node, buf_sprintf("declared here")); + } else { + want_var_export = true; } break; case ZigTypeIdMetaType: { @@ -13299,6 +13320,16 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio break; } + // TODO audit the various ways to use @export + if (want_var_export && target->id == IrInstructionIdLoadPtr) { + IrInstructionLoadPtr *load_ptr = reinterpret_cast(target); + if (load_ptr->ptr->id == IrInstructionIdVarPtr) { + IrInstructionVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); + ZigVar *var = var_ptr->var; + var->linkage = global_linkage_to_var_linkage(global_linkage_id); + } + } + return ir_const_void(ira, &instruction->base); } @@ -13586,9 +13617,16 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; - bool comptime_var_mem = ir_get_var_is_comptime(var); - ConstExprValue *mem_slot = nullptr; + + bool comptime_var_mem = ir_get_var_is_comptime(var); + bool linkage_makes_it_runtime = var->linkage == VarLinkageExternal; + bool is_const = var->src_is_const; + bool is_volatile = false; + + if (linkage_makes_it_runtime) + goto no_mem_slot; + if (var->const_value->special == ConstValSpecialStatic) { mem_slot = var->const_value; } else { @@ -13602,8 +13640,6 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, } } - bool is_const = var->src_is_const; - bool is_volatile = false; if (mem_slot != nullptr) { switch (mem_slot->special) { case ConstValSpecialRuntime: @@ -20679,6 +20715,13 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); return ira->codegen->invalid_instruction; } + uint32_t src_align_bytes; + if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) + return ira->codegen->invalid_instruction; + + uint32_t dest_align_bytes; + if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) + return ira->codegen->invalid_instruction; if (instr_is_comptime(ptr)) { bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); @@ -20701,17 +20744,16 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ IrInstruction *result = ir_const(ira, source_instr, dest_type); copy_const_val(&result->value, val, false); result->value.type = dest_type; + + // Keep the bigger alignment, it can only help- + // unless the target is zero bits. + if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) { + result = ir_align_cast(ira, result, src_align_bytes, false); + } + return result; } - uint32_t src_align_bytes; - if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) - return ira->codegen->invalid_instruction; - - uint32_t dest_align_bytes; - if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) - return ira->codegen->invalid_instruction; - if (dest_align_bytes > src_align_bytes) { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); add_error_note(ira->codegen, msg, ptr->source_node, diff --git a/src/link.cpp b/src/link.cpp index 956e5b6bfc..f6f0c0ffcd 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -899,7 +899,11 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("-ios_simulator_version_min"); break; } - lj->args.append(buf_ptr(buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro))); + Buf *version_string = buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro); + lj->args.append(buf_ptr(version_string)); + + lj->args.append("-sdk_version"); + lj->args.append(buf_ptr(version_string)); if (g->out_type == OutTypeExe) { @@ -920,7 +924,9 @@ static void construct_linker_job_macho(LinkJob *lj) { add_rpath(lj, &g->output_file_path); if (shared) { - lj->args.append("-headerpad_max_install_names"); + if (g->system_linker_hack) { + lj->args.append("-headerpad_max_install_names"); + } } else if (g->is_static) { lj->args.append("-lcrt0.o"); } else { diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 4f0ea4a02b..940d949985 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -36,11 +36,20 @@ pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usi pub extern "c" fn bind(socket: c_int, address: ?*const sockaddr, address_len: socklen_t) c_int; pub extern "c" fn socket(domain: c_int, type: c_int, protocol: c_int) c_int; +const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header; + /// The value of the link editor defined symbol _MH_EXECUTE_SYM is the address /// of the mach header in a Mach-O executable file type. It does not appear in /// any file type other than a MH_EXECUTE file type. The type of the symbol is /// absolute as the header is not part of any section. -pub extern "c" var _mh_execute_header: if (@sizeOf(usize) == 8) mach_header_64 else mach_header; +/// This symbol is populated when linking the system's libc, which is guaranteed +/// on this operating system. However when building object files or libraries, +/// the system libc won't be linked until the final executable. So we +/// export a weak symbol here, to be overridden by the real one. +pub extern "c" var _mh_execute_header: mach_hdr = undefined; +comptime { + @export("__mh_execute_header", _mh_execute_header, @import("builtin").GlobalLinkage.Weak); +} pub const mach_header_64 = macho.mach_header_64; pub const mach_header = macho.mach_header; diff --git a/std/debug/index.zig b/std/debug/index.zig index b8ea7e7f84..1d93e0227e 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -574,7 +574,7 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach } fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - const base_addr = @ptrToInt(&std.c._mh_execute_header); + const base_addr = std.os.getBaseAddress(); const adjusted_addr = 0x100000000 + (address - base_addr); const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { diff --git a/std/os/index.zig b/std/os/index.zig index 5d749479de..148f4ebe06 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -701,7 +701,9 @@ pub fn getBaseAddress() usize { const phdr = linuxGetAuxVal(std.elf.AT_PHDR); return phdr - @sizeOf(std.elf.Ehdr); }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => return @ptrToInt(&std.c._mh_execute_header), + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { + return @ptrToInt(&std.c._mh_execute_header); + }, builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), else => @compileError("Unsupported OS"), } diff --git a/test/stage1/behavior/ptrcast.zig b/test/stage1/behavior/ptrcast.zig index 3787382aea..c83b2f7327 100644 --- a/test/stage1/behavior/ptrcast.zig +++ b/test/stage1/behavior/ptrcast.zig @@ -50,3 +50,11 @@ const Bytes = struct { return res; } }; + +test "comptime ptrcast keeps larger alignment" { + comptime { + const a: u32 = 1234; + const p = @ptrCast([*]const u8, &a); + std.debug.assert(@typeOf(p) == [*]align(@alignOf(u32)) const u8); + } +} From 8922008dec5d631da0305527f670fb34852dfee7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 17:41:55 -0500 Subject: [PATCH 077/134] docs: note top level declarations are order-independent closes #1244 --- doc/langref.html.in | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index ac3864adda..03afa1cf09 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -243,8 +243,9 @@ const Timestamp = struct { {#header_close#} {#header_open|Values#} {#code_begin|exe|values#} -const std = @import("std"); +// Top-level declarations are order-independent: const warn = std.debug.warn; +const std = @import("std"); const os = std.os; const assert = std.debug.assert; @@ -6745,11 +6746,7 @@ pub fn build(b: *Builder) void { {#header_open|Single Threaded Builds#}

Zig has a compile option --single-threaded which has the following effects:

    -
  • {#link|@atomicLoad#} is emitted as a normal load.
  • -
  • {#link|@atomicRmw#} is emitted as a normal memory load, modify, store.
  • -
  • {#link|@fence#} becomes a no-op.
  • -
  • Variables which have Thread Local Storage instead become globals. TODO thread local variables - are not implemented yet.
  • +
  • Variables which have Thread Local Storage instead become globals.
  • The overhead of {#link|Coroutines#} becomes equivalent to function call overhead. TODO: please note this will not be implemented until the upcoming Coroutine Rewrite
  • The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#} From 043090020f5052fdee0fffc02dc7ef379ea63fd0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 17:49:26 -0500 Subject: [PATCH 078/134] docs: shadowing closes #1245 --- doc/langref.html.in | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 03afa1cf09..34cae7282d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2377,7 +2377,36 @@ test "labeled break from labeled block expression" { {#code_end#}

    Here, {#syntax#}blk{#endsyntax#} can be any name.

    {#see_also|Labeled while|Labeled for#} + + {#header_open|Shadowing#} +

    It is never allowed for an identifier to "hide" another one by using the same name:

    + {#code_begin|test_err|redefinition#} +const pi = 3.14; + +test "inside test block" { + // Let's even go inside another block + { + var pi: i32 = 1234; + } +} + {#code_end#} +

    + Because of this, when you read Zig code you can rely on an identifier always meaning the same thing, + within the scope it is defined. Note that you can, however use the same name if the scopes are separate: +

    + {#code_begin|test#} +test "separate scopes" { + { + const pi = 3.14; + } + { + var pi: bool = true; + } +} + {#code_end#} {#header_close#} + {#header_close#} + {#header_open|switch#} {#code_begin|test|switch#} const assert = @import("std").debug.assert; From 99adda9df5a5ae6c16b60da6ba7d57fd2154b81c Mon Sep 17 00:00:00 2001 From: John Schmidt <3405586+schmee@users.noreply.github.com> Date: Mon, 18 Feb 2019 23:53:41 +0100 Subject: [PATCH 079/134] Some function doc tweaks (#1961) * Add docs for the inline function attribute * Remove outdated comment Seems like a leftover from when implicit returns were around. * Consistent terminology --- doc/langref.html.in | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 34cae7282d..3a3eaf708c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3169,7 +3169,6 @@ const assert = @import("std").debug.assert; // Functions are declared like this fn add(a: i8, b: i8) i8 { if (a == 0) { - // You can still return manually if needed. return b; } @@ -3193,12 +3192,18 @@ fn abort() noreturn { while (true) {} } -// nakedcc makes a function not have any function prologue or epilogue. +// The nakedcc specifier makes a function not have any function prologue or epilogue. // This can be useful when integrating with assembly. nakedcc fn _start() noreturn { abort(); } +// The inline specifier forces a function to be inlined at all call sites. +// If the function cannot be inlined, it is a compile-time error. +inline fn shiftLeftOne(a: u32) u32 { + return a << 1; +} + // The pub specifier allows the function to be visible when importing. // Another file can use @import and call sub2 pub fn sub2(a: i8, b: i8) i8 { return a - b; } From 74bdc1d1f89870588e7ac86d9384187fd1f6c10d Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Mon, 11 Feb 2019 18:25:56 +0900 Subject: [PATCH 080/134] src/analyze.cpp: default to using `param_node` upon callconv error; --- src/analyze.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 934da61186..b6d54c0da8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1634,7 +1634,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc } } else if (param_node->data.param_decl.var_token != nullptr) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - add_node_error(g, param_node->data.param_decl.type, + add_node_error(g, param_node, buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'", calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; From b0ceea1831047f374278734f1d86ac94f5438fb4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 18:11:12 -0500 Subject: [PATCH 081/134] zig fmt: fix infix operator before multiline string literal --- std/zig/parser_test.zig | 9 +++++++++ std/zig/render.zig | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 5b7b7aa2a9..508813759c 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,12 @@ +test "zig fmt: infix operator and then multiline string literal" { + try testCanonical( + \\const x = "" ++ + \\ \\ hi + \\; + \\ + ); +} + test "zig fmt: C pointers" { try testCanonical( \\const Ptr = [*c]i32; diff --git a/std/zig/render.zig b/std/zig/render.zig index 66aba75e1b..1abc845b7c 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -340,7 +340,6 @@ fn renderExpression( ast.Node.Id.InfixOp => { const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base); - const op_token = tree.tokens.at(infix_op_node.op_token); const op_space = switch (infix_op_node.op) { ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None, else => Space.Space, @@ -353,7 +352,9 @@ fn renderExpression( }; try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space); - if (after_op_space == Space.Newline) { + if (after_op_space == Space.Newline and + tree.tokens.at(tree.nextToken(infix_op_node.op_token)).id != Token.Id.MultilineStringLiteralLine) + { try stream.writeByteNTimes(' ', indent + indent_delta); start_col.* = indent + indent_delta; } From ea3d9d5c536cb1cf5e015114f8b6e5bc96b8a805 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Feb 2019 18:11:30 -0500 Subject: [PATCH 082/134] add test for 74bdc1d1f898705 --- test/compile_errors.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a18884cc9a..e9fe6f589c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "export generic function", + \\export fn foo(num: var) i32 { + \\ return 0; + \\} + , + ".tmp_source.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'ccc'", + ); + cases.addTest( "C pointer to c_void", \\export fn a() void { From c9fb5240d6305bcef7db5e12b7676ba3d741c11e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Feb 2019 08:39:36 -0500 Subject: [PATCH 083/134] remove --no-rosegment workaround now that valgrind bug is fixed See #896 Zig 0.3.0+ and Valgrind 3.14+ do not need the workaround. --- build.zig | 4 ---- src-self-hosted/link.zig | 4 ---- src/all_types.hpp | 1 - src/codegen.cpp | 1 - src/link.cpp | 3 --- src/main.cpp | 5 ----- std/build.zig | 19 ------------------- 7 files changed, 37 deletions(-) diff --git a/build.zig b/build.zig index 5c7c5b8a18..71bbfd0b23 100644 --- a/build.zig +++ b/build.zig @@ -49,7 +49,6 @@ pub fn build(b: *Builder) !void { .c_header_files = nextValue(&index, build_info), .dia_guids_lib = nextValue(&index, build_info), .llvm = undefined, - .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false, }; ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); @@ -289,8 +288,6 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 { } fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { - exe.setNoRoSegment(ctx.no_rosegment); - exe.addIncludeDir("src"); exe.addIncludeDir(ctx.cmake_binary_dir); addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); @@ -375,5 +372,4 @@ const Context = struct { c_header_files: []const u8, dia_guids_lib: []const u8, llvm: LibraryDep, - no_rosegment: bool, }; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index fadc9b0189..2d8c0b92c2 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -145,10 +145,6 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // lj->args.append("-T"); // lj->args.append(g->linker_script); //} - - //if (g->no_rosegment_workaround) { - // lj->args.append("--no-rosegment"); - //} try ctx.args.append(c"--gc-sections"); //lj->args.append("-m"); diff --git a/src/all_types.hpp b/src/all_types.hpp index 87f4c889ac..0c81f1f53c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1819,7 +1819,6 @@ struct CodeGen { bool is_single_threaded; bool is_native_target; bool linker_rdynamic; - bool no_rosegment_workaround; bool each_lib_rpath; bool disable_pic; diff --git a/src/codegen.cpp b/src/codegen.cpp index 843f8cfd17..f0bce2b035 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8487,7 +8487,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->is_single_threaded); cache_bool(ch, g->is_native_target); cache_bool(ch, g->linker_rdynamic); - cache_bool(ch, g->no_rosegment_workaround); cache_bool(ch, g->each_lib_rpath); cache_bool(ch, g->disable_pic); cache_buf_opt(ch, g->mmacosx_version_min); diff --git a/src/link.cpp b/src/link.cpp index f6f0c0ffcd..a3a68e24d4 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -231,9 +231,6 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(g->linker_script); } - if (g->no_rosegment_workaround) { - lj->args.append("--no-rosegment"); - } lj->args.append("--gc-sections"); lj->args.append("-m"); diff --git a/src/main.cpp b/src/main.cpp index 78446f9e98..441341c126 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,7 +92,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -L[dir] alias for --library-path\n" " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" - " --no-rosegment compromise security to workaround valgrind bug\n" " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" " -framework [name] (darwin) link against framework\n" " -mios-version-min [ver] (darwin) set iOS deployment target\n" @@ -393,7 +392,6 @@ int main(int argc, char **argv) { BuildMode build_mode = BuildModeDebug; ZigList test_exec_args = {0}; int runtime_args_start = -1; - bool no_rosegment_workaround = false; bool system_linker_hack = false; TargetSubsystem subsystem = TargetSubsystemAuto; bool is_single_threaded = false; @@ -573,8 +571,6 @@ int main(int argc, char **argv) { verbose_cimport = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; - } else if (strcmp(arg, "--no-rosegment") == 0) { - no_rosegment_workaround = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { each_lib_rpath = true; } else if (strcmp(arg, "-ftime-report") == 0) { @@ -977,7 +973,6 @@ int main(int argc, char **argv) { } codegen_set_rdynamic(g, rdynamic); - g->no_rosegment_workaround = no_rosegment_workaround; if (mmacosx_version_min && mios_version_min) { fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); return EXIT_FAILURE; diff --git a/std/build.zig b/std/build.zig index 0dbbded802..946a447a11 100644 --- a/std/build.zig +++ b/std/build.zig @@ -851,7 +851,6 @@ pub const LibExeObjStep = struct { disable_libc: bool, frameworks: BufSet, verbose_link: bool, - no_rosegment: bool, c_std: Builder.CStd, // zig only stuff @@ -924,7 +923,6 @@ pub const LibExeObjStep = struct { fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: Version) LibExeObjStep { var self = LibExeObjStep{ - .no_rosegment = false, .strip = false, .builder = builder, .verbose_link = false, @@ -967,7 +965,6 @@ pub const LibExeObjStep = struct { fn initC(builder: *Builder, name: []const u8, kind: Kind, version: Version, static: bool) LibExeObjStep { var self = LibExeObjStep{ - .no_rosegment = false, .builder = builder, .name = name, .kind = kind, @@ -1009,10 +1006,6 @@ pub const LibExeObjStep = struct { return self; } - pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void { - self.no_rosegment = value; - } - fn computeOutFileNames(self: *LibExeObjStep) void { switch (self.kind) { Kind.Obj => { @@ -1382,9 +1375,6 @@ pub const LibExeObjStep = struct { } } - if (self.no_rosegment) { - try zig_args.append("--no-rosegment"); - } if (self.system_linker_hack) { try zig_args.append("--system-linker-hack"); } @@ -1704,7 +1694,6 @@ pub const TestStep = struct { lib_paths: ArrayList([]const u8), packages: ArrayList(Pkg), object_files: ArrayList([]const u8), - no_rosegment: bool, output_path: ?[]const u8, system_linker_hack: bool, override_std_dir: ?[]const u8, @@ -1726,17 +1715,12 @@ pub const TestStep = struct { .lib_paths = ArrayList([]const u8).init(builder.allocator), .packages = ArrayList(Pkg).init(builder.allocator), .object_files = ArrayList([]const u8).init(builder.allocator), - .no_rosegment = false, .output_path = null, .system_linker_hack = false, .override_std_dir = null, }; } - pub fn setNoRoSegment(self: *TestStep, value: bool) void { - self.no_rosegment = value; - } - pub fn addLibPath(self: *TestStep, path: []const u8) void { self.lib_paths.append(path) catch unreachable; } @@ -1938,9 +1922,6 @@ pub const TestStep = struct { zig_args.append("--pkg-end") catch unreachable; } - if (self.no_rosegment) { - try zig_args.append("--no-rosegment"); - } if (self.system_linker_hack) { try zig_args.append("--system-linker-hack"); } From db74832e40f98960e5dc3e46c8196c59033ee0f9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Feb 2019 12:07:56 -0500 Subject: [PATCH 084/134] valgrind client requests for undefined values with this change, when you assign undefined, zig emits a few assembly instructions to tell valgrind that the memory is undefined it's on by default for debug builds, and disabled otherwise. only support for linux, darwin, solaris, mingw on x86_64 is currently implemented. --disable-valgrind turns it off even in debug mode. --enable-valgrind turns it on even in release modes. It's always disabled for compiler_rt.a and builtin.a. Adds `@import("builtin").valgrind_support` which lets code know at comptime whether valgrind client requests are enabled. See #1989 --- src/all_types.hpp | 24 ++++++++++---- src/codegen.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++++++ src/link.cpp | 1 + src/main.cpp | 11 +++++++ src/target.cpp | 20 ++++++++++-- src/target.hpp | 2 ++ 6 files changed, 130 insertions(+), 9 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 0c81f1f53c..5bac44f9da 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1345,15 +1345,11 @@ struct ZigFn { // in the case of async functions this is the implicit return type according to the // zig source code, not according to zig ir ZigType *src_implicit_return_type; - bool is_test; - FnInline fn_inline; - FnAnalState anal_state; IrExecutable ir_executable; IrExecutable analyzed_executable; size_t prealloc_bbc; AstNode **param_source_nodes; Buf **param_names; - uint32_t align_bytes; AstNode *fn_no_inline_set_node; AstNode *fn_static_eval_set_node; @@ -1363,13 +1359,22 @@ struct ZigFn { Buf *section_name; AstNode *set_alignstack_node; - uint32_t alignstack_value; AstNode *set_cold_node; - bool is_cold; ZigList export_list; + + LLVMValueRef valgrind_client_request_array; + + FnInline fn_inline; + FnAnalState anal_state; + + uint32_t align_bytes; + uint32_t alignstack_value; + bool calls_or_awaits_errorable_fn; + bool is_cold; + bool is_test; }; uint32_t fn_table_entry_hash(ZigFn*); @@ -1612,6 +1617,12 @@ struct LinkLib { bool provided_explicitly; }; +enum ValgrindSupport { + ValgrindSupportAuto, + ValgrindSupportDisabled, + ValgrindSupportEnabled, +}; + // When adding fields, check if they should be added to the hash computation in build_with_cache struct CodeGen { //////////////////////////// Runtime State @@ -1813,6 +1824,7 @@ struct CodeGen { OutType out_type; ZigTarget zig_target; TargetSubsystem subsystem; + ValgrindSupport valgrind_support; bool is_static; bool strip_debug_symbols; bool is_test_build; diff --git a/src/codegen.cpp b/src/codegen.cpp index f0bce2b035..86923a0bde 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3341,6 +3341,77 @@ static bool value_is_all_undef(ConstExprValue *const_val) { zig_unreachable(); } +static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default_value, LLVMValueRef request, + LLVMValueRef a1, LLVMValueRef a2, LLVMValueRef a3, LLVMValueRef a4, LLVMValueRef a5) +{ + if (!target_has_valgrind_support(&g->zig_target)) { + return default_value; + } + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; + bool asm_has_side_effects = true; + bool asm_is_alignstack = false; + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + if (g->zig_target.os == OsLinux || target_is_darwin(&g->zig_target) || g->zig_target.os == OsSolaris || + (g->zig_target.os == OsWindows && g->zig_target.env_type != ZigLLVM_MSVC)) + { + if (g->cur_fn->valgrind_client_request_array == nullptr) { + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMBasicBlockRef entry_block = LLVMGetEntryBasicBlock(g->cur_fn->llvm_value); + LLVMValueRef first_inst = LLVMGetFirstInstruction(entry_block); + LLVMPositionBuilderBefore(g->builder, first_inst); + LLVMTypeRef array_type_ref = LLVMArrayType(usize_type_ref, 6); + g->cur_fn->valgrind_client_request_array = LLVMBuildAlloca(g->builder, array_type_ref, ""); + LLVMPositionBuilderAtEnd(g->builder, prev_block); + } + LLVMValueRef array_ptr = g->cur_fn->valgrind_client_request_array; + LLVMValueRef array_elements[] = {request, a1, a2, a3, a4, a5}; + LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); + for (unsigned i = 0; i < 6; i += 1) { + LLVMValueRef indexes[] = { + zero, + LLVMConstInt(usize_type_ref, i, false), + }; + LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, ""); + LLVMBuildStore(g->builder, array_elements[i], elem_ptr); + } + + Buf *asm_template = buf_create_from_str( + "rolq $$3, %rdi ; rolq $$13, %rdi\n" + "rolq $$61, %rdi ; rolq $$51, %rdi\n" + "xchgq %rbx,%rbx\n" + ); + Buf *asm_constraints = buf_create_from_str( + "={rdx},{rax},0,~{cc},~{memory}" + ); + unsigned input_and_output_count = 2; + LLVMValueRef array_ptr_as_usize = LLVMBuildPtrToInt(g->builder, array_ptr, usize_type_ref, ""); + LLVMValueRef param_values[] = { array_ptr_as_usize, default_value }; + LLVMTypeRef param_types[] = {usize_type_ref, usize_type_ref}; + LLVMTypeRef function_type = LLVMFunctionType(usize_type_ref, param_types, + input_and_output_count, false); + LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(asm_template), buf_len(asm_template), + buf_ptr(asm_constraints), buf_len(asm_constraints), asm_has_side_effects, asm_is_alignstack, + LLVMInlineAsmDialectATT); + return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); + } + } + zig_unreachable(); +} + +static bool want_valgrind_support(CodeGen *g) { + if (!target_has_valgrind_support(&g->zig_target)) + return false; + switch (g->valgrind_support) { + case ValgrindSupportDisabled: + return false; + case ValgrindSupportEnabled: + return true; + case ValgrindSupportAuto: + return g->build_mode == BuildModeDebug; + } + zig_unreachable(); +} + static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { assert(type_has_bits(value_type)); uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); @@ -3353,6 +3424,14 @@ static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_ ZigType *usize = g->builtin_types.entry_usize; LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); + // then tell valgrind that the memory is undefined even though we just memset it + if (want_valgrind_support(g)) { + static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545; + LLVMValueRef zero = LLVMConstInt(usize->type_ref, 0, false); + LLVMValueRef req = LLVMConstInt(usize->type_ref, VG_USERREQ__MAKE_MEM_UNDEFINED, false); + LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->type_ref, ""); + gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero); + } } static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { @@ -7525,6 +7604,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); + buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g))); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); @@ -8489,6 +8569,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->each_lib_rpath); cache_bool(ch, g->disable_pic); + cache_bool(ch, g->valgrind_support); cache_buf_opt(ch, g->mmacosx_version_min); cache_buf_opt(ch, g->mios_version_min); cache_usize(ch, g->version_major); diff --git a/src/link.cpp b/src/link.cpp index a3a68e24d4..2878a00358 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -55,6 +55,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); codegen_set_is_static(child_gen, true); child_gen->disable_pic = parent_gen->disable_pic; + child_gen->valgrind_support = ValgrindSupportDisabled; codegen_set_out_name(child_gen, buf_create_from_str(aname)); diff --git a/src/main.cpp b/src/main.cpp index 441341c126..dd35cee5a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,6 +49,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --cache [auto|off|on] build in global cache, print out paths to stdout\n" " --color [auto|off|on] enable or disable colored error messages\n" " --disable-pic disable Position Independent Code for libraries\n" + " --disable-valgrind omit valgrind client requests in debug builds\n" + " --enable-valgrind include valgrind client requests release builds\n" " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " -ftime-report print timing diagnostics\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" @@ -396,6 +398,7 @@ int main(int argc, char **argv) { TargetSubsystem subsystem = TargetSubsystemAuto; bool is_single_threaded = false; Buf *override_std_dir = nullptr; + ValgrindSupport valgrind_support = ValgrindSupportAuto; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -433,6 +436,7 @@ int main(int argc, char **argv) { CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), override_std_dir); + g->valgrind_support = valgrind_support; g->enable_time_report = timing_info; buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); codegen_set_out_name(g, buf_create_from_str("build")); @@ -520,6 +524,7 @@ int main(int argc, char **argv) { os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), nullptr); + g->valgrind_support = valgrind_support; g->is_single_threaded = true; codegen_set_out_name(g, buf_create_from_str("fmt")); g->enable_cache = true; @@ -577,6 +582,10 @@ int main(int argc, char **argv) { timing_info = true; } else if (strcmp(arg, "--disable-pic") == 0) { disable_pic = true; + } else if (strcmp(arg, "--enable-valgrind") == 0) { + valgrind_support = ValgrindSupportEnabled; + } else if (strcmp(arg, "--disable-valgrind") == 0) { + valgrind_support = ValgrindSupportDisabled; } else if (strcmp(arg, "--system-linker-hack") == 0) { system_linker_hack = true; } else if (strcmp(arg, "--single-threaded") == 0) { @@ -849,6 +858,7 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuiltin: { CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); + g->valgrind_support = valgrind_support; g->is_single_threaded = is_single_threaded; Buf *builtin_source = codegen_generate_builtin_source(g); if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { @@ -909,6 +919,7 @@ int main(int argc, char **argv) { } CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); + g->valgrind_support = valgrind_support; g->subsystem = subsystem; if (disable_pic) { diff --git a/src/target.cpp b/src/target.cpp index 5352a21826..3e733da0e7 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -544,7 +544,7 @@ void get_target_triple(Buf *triple, const ZigTarget *target) { } } -static bool is_os_darwin(ZigTarget *target) { +bool target_is_darwin(const ZigTarget *target) { switch (target->os) { case OsMacOSX: case OsIOS: @@ -566,7 +566,7 @@ void resolve_target_object_format(ZigTarget *target) { case ZigLLVM_thumb: case ZigLLVM_x86: case ZigLLVM_x86_64: - if (is_os_darwin(target)) { + if (target_is_darwin(target)) { target->oformat = ZigLLVM_MachO; } else if (target->os == OsWindows) { target->oformat = ZigLLVM_COFF; @@ -626,7 +626,7 @@ void resolve_target_object_format(ZigTarget *target) { case ZigLLVM_ppc: case ZigLLVM_ppc64: - if (is_os_darwin(target)) { + if (target_is_darwin(target)) { target->oformat = ZigLLVM_MachO; } else { target->oformat= ZigLLVM_ELF; @@ -1084,3 +1084,17 @@ bool target_is_arm(const ZigTarget *target) { } zig_unreachable(); } + +// Valgrind supports more, but Zig does not support them yet. +bool target_has_valgrind_support(const ZigTarget *target) { + switch (target->arch.arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_x86_64: + return (target->os == OsLinux || target_is_darwin(target) || target->os == OsSolaris || + (target->os == OsWindows && target->env_type != ZigLLVM_MSVC)); + default: + return false; + } + zig_unreachable(); +} diff --git a/src/target.hpp b/src/target.hpp index 99d1cadf56..292ea8f94f 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -136,5 +136,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type); bool target_is_arm(const ZigTarget *target); bool target_allows_addr_zero(const ZigTarget *target); +bool target_has_valgrind_support(const ZigTarget *target); +bool target_is_darwin(const ZigTarget *target); #endif From 400006bbe790f2173fd6e40d80608691a95b437e Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 19 Feb 2019 21:18:57 +0100 Subject: [PATCH 085/134] Prevent crash in tagged enums rendering (#1986) * Prevent crash in tagged enums rendering * Add a test case --- src/analyze.cpp | 4 ++-- test/compile_errors.zig | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index b6d54c0da8..9fe656ff77 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6276,8 +6276,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdUnion: { - uint64_t tag = bigint_as_unsigned(&const_val->data.x_union.tag); - TypeUnionField *field = &type_entry->data.unionation.fields[tag]; + const BigInt *tag = &const_val->data.x_union.tag; + TypeUnionField *field = find_union_field_by_tag(type_entry, tag); buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name)); render_const_value(g, buf, const_val->data.x_union.payload); buf_append_str(buf, "}"); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index e9fe6f589c..b935b4825a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -5599,4 +5599,21 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", ); + + cases.add( + "compileLog of tagged enum doesn't crash the compiler", + \\const Bar = union(enum(u32)) { + \\ X: i32 = 1 + \\}; + \\ + \\fn testCompileLog(x: Bar) void { + \\ @compileLog(x); + \\} + \\ + \\pub fn main () void { + \\ comptime testCompileLog(Bar{.X = 123}); + \\} + , + ".tmp_source.zig:6:5: error: found compile log statement" + ); } From c8ce351ec982608d3ea0c60a34c7b18d894dee01 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 19 Feb 2019 15:34:44 -0500 Subject: [PATCH 086/134] pull request fixups --- src/ir.cpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index e5f09dd93a..3e48779b93 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1303,15 +1303,23 @@ static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *s return &ptr_type_of_instruction->base; } -static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, IrInstruction *value) { - IrInstructionUnOp *br_instruction = ir_build_instruction(irb, scope, source_node); - br_instruction->op_id = op_id; - br_instruction->value = value; - br_instruction->lval = LValNone; +static IrInstruction *ir_build_un_op_lval(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, + IrInstruction *value, LVal lval) +{ + IrInstructionUnOp *instruction = ir_build_instruction(irb, scope, source_node); + instruction->op_id = op_id; + instruction->value = value; + instruction->lval = lval; ir_ref_instruction(value, irb->current_basic_block); - return &br_instruction->base; + return &instruction->base; +} + +static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, + IrInstruction *value) +{ + return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone); } static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -7227,10 +7235,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop // We essentially just converted any lvalue from &(x.*) to (&x).*; // this inhibits checking that x is a pointer later, so we directly // record whether the pointer check is needed - IrInstructionUnOp *result = (IrInstructionUnOp*)ir_build_un_op(irb, scope, node, IrUnOpDereference, value); - result->lval = lval; - - return &result->base; + return ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval); } case NodeTypeUnwrapOptional: { AstNode *expr_node = node->data.unwrap_optional.expr; @@ -13685,6 +13690,8 @@ no_mem_slot: static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, IrInstruction *uncasted_value) { + assert(ptr->value.type->id == ZigTypeIdPointer); + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { return ir_const_void(ira, source_instr); } From 079728752eca4cffbb4f7e8dc06d5e23b81d7627 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Feb 2019 08:04:46 -0500 Subject: [PATCH 087/134] deduplicate compile errors for undeclared identifiers closes #111 --- src/ir.cpp | 19 ++++++++++++++++++- test/compile_errors.zig | 24 ++++++++++++++++++------ test/tests.zig | 3 ++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 3e48779b93..0eb1f2d533 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3674,6 +3674,22 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode return ir_build_const_null(irb, scope, node); } +static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) { + ScopeDecls *scope_decls = nullptr; + while (scope != nullptr) { + if (scope->id == ScopeIdDecls) { + scope_decls = reinterpret_cast(scope); + } + scope = scope->parent; + } + TldVar *tld_var = allocate(1); + init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base); + tld_var->base.resolution = TldResolutionInvalid; + tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false, + &g->invalid_instruction->value, &tld_var->base, g->builtin_types.entry_invalid); + scope_decls->decl_table.put(var_name, &tld_var->base); +} + static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { Error err; assert(node->type == NodeTypeSymbol); @@ -3727,8 +3743,9 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, return irb->codegen->invalid_instruction; } - // TODO put a variable of same name with invalid type in global scope + // put a variable of same name with invalid type in global scope // so that future references to this same name will find a variable with an invalid type + populate_invalid_variable_in_scope(irb->codegen, scope, node, variable_name); add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 86dfd59bee..a9c748bcda 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,22 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addCase(x: { + var tc = cases.create( + "deduplicate undeclared identifier", + \\export fn a() void { + \\ x += 1; + \\} + \\export fn b() void { + \\ x += 1; + \\} + , + ".tmp_source.zig:2:5: error: use of undeclared identifier 'x'", + ); + tc.expect_exact = true; + break :x tc; + }); + cases.addTest( "export generic function", \\export fn foo(num: var) i32 { @@ -2280,7 +2296,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} , ".tmp_source.zig:2:5: error: use of undeclared identifier 'i'", - ".tmp_source.zig:2:12: error: use of undeclared identifier 'i'", ); cases.add( @@ -5618,8 +5633,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ".tmp_source.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", ); - cases.add( - "compileLog of tagged enum doesn't crash the compiler", + cases.add("compileLog of tagged enum doesn't crash the compiler", \\const Bar = union(enum(u32)) { \\ X: i32 = 1 \\}; @@ -5631,7 +5645,5 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\pub fn main () void { \\ comptime testCompileLog(Bar{.X = 123}); \\} - , - ".tmp_source.zig:6:5: error: found compile log statement" - ); + , ".tmp_source.zig:6:5: error: found compile log statement"); } diff --git a/test/tests.zig b/test/tests.zig index f9c9207f05..2591ab4f2a 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -716,7 +716,8 @@ pub const CompileErrorContext = struct { for (self.case.expected_errors.toSliceConst()) |expected| { if (mem.indexOf(u8, stderr, expected) == null) { warn( - \\\n=========== Expected compile error: ============ + \\ + \\=========== Expected compile error: ============ \\{} \\ , expected); From 3ee9d06cbdb6bcaf561e7215c4c103c7ad65a72d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Feb 2019 22:40:41 -0500 Subject: [PATCH 088/134] packed structs support comptime bitcasting * `type_size_store` is no longer a thing. loading and storing a pointer to a value may dereference up to `@sizeOf(T)` bytes, even for integers such as `u24`. * fix `types_have_same_zig_comptime_repr` to not think that the same `ZigTypeId` means the `ConstExprValue` neccesarily has the same representation. * implement `buf_write_value_bytes` and `buf_read_value_bytes` for `ContainerLayoutPacked` closes #1120 --- src/analyze.cpp | 22 ---- src/analyze.hpp | 1 - src/codegen.cpp | 5 +- src/ir.cpp | 178 ++++++++++++++++++++++++++--- test/compile_errors.zig | 4 +- test/stage1/behavior.zig | 1 + test/stage1/behavior/bitcast.zig | 32 ++++++ test/stage1/behavior/bugs/1120.zig | 23 ++++ 8 files changed, 226 insertions(+), 40 deletions(-) create mode 100644 test/stage1/behavior/bugs/1120.zig diff --git a/src/analyze.cpp b/src/analyze.cpp index 9fe656ff77..040483a05e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -359,28 +359,6 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) { return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); } -uint64_t type_size_store(CodeGen *g, ZigType *type_entry) { - assert(type_is_complete(type_entry)); - - if (!type_has_bits(type_entry)) - return 0; - - if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { - uint64_t size_in_bits = type_size_bits(g, type_entry); - return (size_in_bits + 7) / 8; - } else if (type_entry->id == ZigTypeIdArray) { - ZigType *child_type = type_entry->data.array.child_type; - if (child_type->id == ZigTypeIdStruct && - child_type->data.structure.layout == ContainerLayoutPacked) - { - uint64_t size_in_bits = type_size_bits(g, type_entry); - return (size_in_bits + 7) / 8; - } - } - - return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); -} - uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { assert(type_is_complete(type_entry)); diff --git a/src/analyze.hpp b/src/analyze.hpp index 956ef47309..39c1450678 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -19,7 +19,6 @@ ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, ZigType *type_entry); -uint64_t type_size_store(CodeGen *g, ZigType *type_entry); uint64_t type_size_bits(CodeGen *g, ZigType *type_entry); ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index 86923a0bde..dbe0291299 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3070,7 +3070,10 @@ static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, { ZigType *wanted_type = instruction->base.value.type; LLVMValueRef value = ir_llvm_value(g, instruction->value); - return LLVMBuildBitCast(g->builder, value, wanted_type->type_ref, ""); + // We either bitcast the value directly or bitcast the pointer which does a pointer cast + LLVMTypeRef wanted_type_ref = handle_is_ptr(wanted_type) ? + LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref; + return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); } static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, diff --git a/src/ir.cpp b/src/ir.cpp index 0eb1f2d533..177133788f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -198,10 +198,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; break; } - case ConstPtrSpecialBaseStruct: - result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ - const_val->data.x_ptr.data.base_struct.field_index]; + case ConstPtrSpecialBaseStruct: { + ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; + result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; break; + } case ConstPtrSpecialBaseErrorUnionCode: result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; break; @@ -230,20 +231,55 @@ static bool is_opt_err_set(ZigType *ty) { (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); } +// This function returns true when you can change the type of a ConstExprValue and the +// value remains meaningful. static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; - if (a->id == b->id) - return true; - if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; if (is_opt_err_set(a) && is_opt_err_set(b)) return true; - return false; + if (a->id != b->id) + return false; + + switch (a->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdPointer: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdErrorSet: + case ZigTypeIdOpaque: + return true; + case ZigTypeIdFloat: + return a->data.floating.bit_count == b->data.floating.bit_count; + case ZigTypeIdInt: + return a->data.integral.is_signed == b->data.integral.is_signed; + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdArgTuple: + case ZigTypeIdPromise: + case ZigTypeIdVector: + return false; + } + zig_unreachable(); } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -14421,12 +14457,11 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; - // We don't need to read the padding bytes, so we look at type_size_store bytes - size_t src_size = type_size_store(codegen, pointee->type); - size_t dst_size = type_size_store(codegen, out_val->type); + size_t src_size = type_size(codegen, pointee->type); + size_t dst_size = type_size(codegen, out_val->type); if (dst_size <= src_size) { - if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { + if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); return ErrorNone; } @@ -20885,7 +20920,68 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdVector: return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); case ZigTypeIdStruct: - zig_panic("TODO buf_write_value_bytes struct type"); + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutExtern: + zig_panic("TODO buf_write_value_bytes extern struct"); + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref, + (unsigned)gen_i); + size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type); + size_t big_int_byte_count = big_int_bit_count / 8; + if (big_int_byte_count > child_buf_len) { + child_buf = allocate_nonzero(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_init_unsigned(&big_int, 0); + size_t used_bits = 0; + while (src_i < src_field_count) { + TypeStructField *field = &val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]); + BigInt child_val; + bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, + false); + if (is_big_endian) { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, packed_bits_size); + BigInt shifted; + bigint_shl(&shifted, &big_int, &shift_amt); + bigint_or(&big_int, &shifted, &child_val); + } else { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, used_bits); + BigInt child_val_shifted; + bigint_shl(&child_val_shifted, &child_val, &shift_amt); + BigInt tmp; + bigint_or(&tmp, &big_int, &child_val_shifted); + big_int = tmp; + used_bits += packed_bits_size; + } + src_i += 1; + } + bigint_write_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian); + offset += big_int_byte_count; + gen_i += 1; + } + } + } + return; case ZigTypeIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case ZigTypeIdErrorUnion: @@ -21012,8 +21108,62 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou } return ErrorNone; } - case ContainerLayoutPacked: - zig_panic("TODO buf_read_value_bytes packed struct"); + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + val->data.x_struct.fields = create_const_vals(src_field_count); + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref, + (unsigned)gen_i); + size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type); + size_t big_int_byte_count = big_int_bit_count / 8; + if (big_int_byte_count > child_buf_len) { + child_buf = allocate_nonzero(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_read_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian, false); + while (src_i < src_field_count) { + TypeStructField *field = &val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + ConstExprValue *field_val = &val->data.x_struct.fields[src_i]; + field_val->special = ConstValSpecialStatic; + field_val->type = field->type_entry; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + + BigInt child_val; + if (is_big_endian) { + zig_panic("TODO buf_read_value_bytes packed struct big endian"); + } else { + BigInt packed_bits_size_bi; + bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size); + bigint_truncate(&child_val, &big_int, packed_bits_size, false); + BigInt tmp; + bigint_shr(&tmp, &big_int, &packed_bits_size_bi); + big_int = tmp; + } + + bigint_write_twos_complement(&child_val, child_buf, big_int_bit_count, is_big_endian); + if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) { + return err; + } + + src_i += 1; + } + offset += big_int_byte_count; + gen_i += 1; + } + return ErrorNone; + } } zig_unreachable(); case ZigTypeIdOptional: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a9c748bcda..07e677b8ed 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -318,12 +318,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "reading past end of pointer casted array", \\comptime { \\ const array = "aoeu"; - \\ const slice = array[2..]; + \\ const slice = array[1..]; \\ const int_ptr = @ptrCast(*const u24, slice.ptr); \\ const deref = int_ptr.*; \\} , - ".tmp_source.zig:5:26: error: attempt to read 3 bytes from [4]u8 at index 2 which is 2 bytes", + ".tmp_source.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", ); cases.add( diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index df311637fa..708bb91ac0 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -11,6 +11,7 @@ comptime { _ = @import("behavior/bswap.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1120.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1322.zig"); _ = @import("behavior/bugs/1381.zig"); diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index 9607d2e3ef..e498905f4e 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const expect = std.testing.expect; const maxInt = std.math.maxInt; @@ -34,3 +35,34 @@ test "@bitCast extern enum to its integer type" { SOCK.testBitCastExternEnum(); comptime SOCK.testBitCastExternEnum(); } + +test "@bitCast packed structs at runtime and comptime" { + const Full = packed struct { + number: u16, + }; + const Divided = packed struct { + half1: u8, + quarter3: u4, + quarter4: u4, + }; + const S = struct { + fn doTheTest() void { + var full = Full{ .number = 0x1234 }; + var two_halves = @bitCast(Divided, full); + switch (builtin.endian) { + builtin.Endian.Big => { + expect(two_halves.half1 == 0x12); + expect(two_halves.quarter3 == 0x3); + expect(two_halves.quarter4 == 0x4); + }, + builtin.Endian.Little => { + expect(two_halves.half1 == 0x34); + expect(two_halves.quarter3 == 0x2); + expect(two_halves.quarter4 == 0x1); + }, + } + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/bugs/1120.zig b/test/stage1/behavior/bugs/1120.zig new file mode 100644 index 0000000000..dda46e8e1c --- /dev/null +++ b/test/stage1/behavior/bugs/1120.zig @@ -0,0 +1,23 @@ +const std = @import("std"); +const expect = std.testing.expect; + +const A = packed struct { + a: u2, + b: u6, +}; +const B = packed struct { + q: u8, + a: u2, + b: u6, +}; +test "bug 1120" { + var a = A{ .a = 2, .b = 2 }; + var b = B{ .q = 22, .a = 3, .b = 2 }; + var t: usize = 0; + const ptr = switch (t) { + 0 => &a.a, + 1 => &b.a, + else => unreachable, + }; + expect(ptr.* == 2); +} From db31c2524df4352593128aaceaa0b085ef8bb674 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Feb 2019 08:46:43 -0500 Subject: [PATCH 089/134] extern structs support comptime bitcasting --- src/ir.cpp | 17 ++++++++++++++--- test/stage1/behavior/bitcast.zig | 28 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 177133788f..03555c78f4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -20923,8 +20923,19 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue switch (val->type->data.structure.layout) { case ContainerLayoutAuto: zig_unreachable(); - case ContainerLayoutExtern: - zig_panic("TODO buf_write_value_bytes extern struct"); + case ContainerLayoutExtern: { + size_t src_field_count = val->type->data.structure.src_field_count; + for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { + TypeStructField *type_field = &val->type->data.structure.fields[field_i]; + if (type_field->gen_index == SIZE_MAX) + continue; + ConstExprValue *field_val = &val->data.x_struct.fields[field_i]; + size_t offset = LLVMOffsetOfElement(codegen->target_data_ref, val->type->type_ref, + type_field->gen_index); + buf_write_value_bytes(codegen, buf + offset, field_val); + } + return; + } case ContainerLayoutPacked: { size_t src_field_count = val->type->data.structure.src_field_count; size_t gen_field_count = val->type->data.structure.gen_field_count; @@ -20979,9 +20990,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue offset += big_int_byte_count; gen_i += 1; } + return; } } - return; case ZigTypeIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case ZigTypeIdErrorUnion: diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index e498905f4e..d409975548 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -66,3 +66,31 @@ test "@bitCast packed structs at runtime and comptime" { S.doTheTest(); comptime S.doTheTest(); } + +test "@bitCast extern structs at runtime and comptime" { + const Full = extern struct { + number: u16, + }; + const TwoHalves = extern struct { + half1: u8, + half2: u8, + }; + const S = struct { + fn doTheTest() void { + var full = Full{ .number = 0x1234 }; + var two_halves = @bitCast(TwoHalves, full); + switch (builtin.endian) { + builtin.Endian.Big => { + expect(two_halves.half1 == 0x12); + expect(two_halves.half2 == 0x34); + }, + builtin.Endian.Little => { + expect(two_halves.half1 == 0x34); + expect(two_halves.half2 == 0x12); + }, + } + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From 2bb795dc455823e76ef3e0c9b3fcee6bcb15fddb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Feb 2019 10:07:11 -0500 Subject: [PATCH 090/134] `@sliceToBytes` works at comptime closes #262 --- src/all_types.hpp | 33 +++--- src/codegen.cpp | 138 ++++++++++++++------------ src/ir.cpp | 59 +++++++++-- src/ir_print.cpp | 9 ++ test/stage1/behavior.zig | 1 + test/stage1/behavior/slicetobytes.zig | 29 ++++++ 6 files changed, 185 insertions(+), 84 deletions(-) create mode 100644 test/stage1/behavior/slicetobytes.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 2a359fa49b..0ee0fef2ca 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -630,19 +630,6 @@ struct AstNodeUnwrapOptional { AstNode *expr; }; -enum CastOp { - CastOpNoCast, // signifies the function call expression is not a cast - CastOpNoop, // fn call expr is a cast, but does nothing - CastOpIntToFloat, - CastOpFloatToInt, - CastOpBoolToInt, - CastOpResizeSlice, - CastOpNumLitToConcrete, - CastOpErrSet, - CastOpBitCast, - CastOpPtrOfArrayToSlice, -}; - struct AstNodeFnCallExpr { AstNode *fn_ref_expr; ZigList params; @@ -2142,6 +2129,7 @@ enum IrInstructionId { IrInstructionIdConst, IrInstructionIdReturn, IrInstructionIdCast, + IrInstructionIdResizeSlice, IrInstructionIdContainerInitList, IrInstructionIdContainerInitFields, IrInstructionIdStructInit, @@ -2503,6 +2491,18 @@ struct IrInstructionReturn { IrInstruction *value; }; +enum CastOp { + CastOpNoCast, // signifies the function call expression is not a cast + CastOpNoop, // fn call expr is a cast, but does nothing + CastOpIntToFloat, + CastOpFloatToInt, + CastOpBoolToInt, + CastOpNumLitToConcrete, + CastOpErrSet, + CastOpBitCast, + CastOpPtrOfArrayToSlice, +}; + // TODO get rid of this instruction, replace with instructions for each op code struct IrInstructionCast { IrInstruction base; @@ -2513,6 +2513,13 @@ struct IrInstructionCast { LLVMValueRef tmp_ptr; }; +struct IrInstructionResizeSlice { + IrInstruction base; + + IrInstruction *operand; + LLVMValueRef tmp_ptr; +}; + struct IrInstructionContainerInitList { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index dbe0291299..bc4005c87b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2882,6 +2882,76 @@ static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *in } } +static LLVMValueRef ir_render_resize_slice(CodeGen *g, IrExecutable *executable, + IrInstructionResizeSlice *instruction) +{ + ZigType *actual_type = instruction->operand->value.type; + ZigType *wanted_type = instruction->base.value.type; + LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand); + assert(expr_val); + + assert(instruction->tmp_ptr); + assert(wanted_type->id == ZigTypeIdStruct); + assert(wanted_type->data.structure.is_slice); + assert(actual_type->id == ZigTypeIdStruct); + assert(actual_type->data.structure.is_slice); + + ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; + ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; + ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; + ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; + + + size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; + size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; + size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; + size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; + + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); + LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, + wanted_type->data.structure.fields[0].type_entry->type_ref, ""); + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + (unsigned)wanted_ptr_index, ""); + gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); + + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_len_index, ""); + LLVMValueRef src_len = gen_load_untyped(g, src_len_ptr, 0, false, ""); + uint64_t src_size = type_size(g, actual_child_type); + uint64_t dest_size = type_size(g, wanted_child_type); + + LLVMValueRef new_len; + if (dest_size == 1) { + LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); + new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); + } else if (src_size == 1) { + LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + new_len = LLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); + } else { + zig_unreachable(); + } + + LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, + (unsigned)wanted_len_index, ""); + gen_store_untyped(g, new_len, dest_len_ptr, 0, false); + + + return instruction->tmp_ptr; +} + static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, IrInstructionCast *cast_instruction) { @@ -2896,69 +2966,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, zig_unreachable(); case CastOpNoop: return expr_val; - case CastOpResizeSlice: - { - assert(cast_instruction->tmp_ptr); - assert(wanted_type->id == ZigTypeIdStruct); - assert(wanted_type->data.structure.is_slice); - assert(actual_type->id == ZigTypeIdStruct); - assert(actual_type->data.structure.is_slice); - - ZigType *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; - ZigType *actual_child_type = actual_pointer_type->data.pointer.child_type; - ZigType *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; - ZigType *wanted_child_type = wanted_pointer_type->data.pointer.child_type; - - - size_t actual_ptr_index = actual_type->data.structure.fields[slice_ptr_index].gen_index; - size_t actual_len_index = actual_type->data.structure.fields[slice_len_index].gen_index; - size_t wanted_ptr_index = wanted_type->data.structure.fields[slice_ptr_index].gen_index; - size_t wanted_len_index = wanted_type->data.structure.fields[slice_len_index].gen_index; - - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, - wanted_type->data.structure.fields[0].type_entry->type_ref, ""); - LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_ptr_index, ""); - gen_store_untyped(g, src_ptr_casted, dest_ptr_ptr, 0, false); - - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, (unsigned)actual_len_index, ""); - LLVMValueRef src_len = gen_load_untyped(g, src_len_ptr, 0, false, ""); - uint64_t src_size = type_size(g, actual_child_type); - uint64_t dest_size = type_size(g, wanted_child_type); - - LLVMValueRef new_len; - if (dest_size == 1) { - LLVMValueRef src_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, src_size, false); - new_len = LLVMBuildMul(g->builder, src_len, src_size_val, ""); - } else if (src_size == 1) { - LLVMValueRef dest_size_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, dest_size, false); - if (ir_want_runtime_safety(g, &cast_instruction->base)) { - LLVMValueRef remainder_val = LLVMBuildURem(g->builder, src_len, dest_size_val, ""); - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SliceWidenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdSliceWidenRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - new_len = LLVMBuildExactUDiv(g->builder, src_len, dest_size_val, ""); - } else { - zig_unreachable(); - } - - LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr, - (unsigned)wanted_len_index, ""); - gen_store_untyped(g, new_len, dest_len_ptr, 0, false); - - - return cast_instruction->tmp_ptr; - } case CastOpIntToFloat: assert(actual_type->id == ZigTypeIdInt); if (actual_type->data.integral.is_signed) { @@ -5625,6 +5632,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); case IrInstructionIdAssertZero: return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction); + case IrInstructionIdResizeSlice: + return ir_render_resize_slice(g, executable, (IrInstructionResizeSlice *)instruction); } zig_unreachable(); } @@ -6716,6 +6725,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdCmpxchgGen) { IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction; slot = &cmpxchg_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdResizeSlice) { + IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction; + slot = &resize_slice_instruction->tmp_ptr; } else if (instruction->id == IrInstructionIdVectorToArray) { IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); diff --git a/src/ir.cpp b/src/ir.cpp index 03555c78f4..a385592e2d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -456,6 +456,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) { return IrInstructionIdCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionResizeSlice *) { + return IrInstructionIdResizeSlice; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) { return IrInstructionIdContainerInitList; } @@ -1475,6 +1479,19 @@ static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *sourc return &decl_var_instruction->base; } +static IrInstruction *ir_build_resize_slice(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *operand, ZigType *ty) +{ + IrInstructionResizeSlice *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ty; + instruction->operand = operand; + + ir_ref_instruction(operand, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { @@ -9674,9 +9691,6 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } const_val->type = new_type; break; - case CastOpResizeSlice: - // can't do it - zig_unreachable(); case CastOpIntToFloat: { assert(new_type->id == ZigTypeIdFloat); @@ -9740,9 +9754,7 @@ static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, Z static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { - if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && - cast_op != CastOpResizeSlice) - { + if (instr_is_comptime(value) || !type_has_bits(wanted_type)) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) @@ -19082,7 +19094,9 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru } } - return ir_resolve_cast(ira, &instruction->base, casted_value, dest_slice_type, CastOpResizeSlice, true); + IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, casted_value, dest_slice_type); + ir_add_alloca(ira, result, dest_slice_type); + return result; } static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) { @@ -19109,7 +19123,34 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct alignment, 0, 0); ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type); - return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); + if (instr_is_comptime(target)) { + ConstExprValue *target_val = ir_resolve_const(ira, target, UndefBad); + if (target_val == nullptr) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_const(ira, &instruction->base, dest_slice_type); + result->value.data.x_struct.fields = create_const_vals(2); + + ConstExprValue *ptr_val = &result->value.data.x_struct.fields[slice_ptr_index]; + ConstExprValue *target_ptr_val = &target_val->data.x_struct.fields[slice_ptr_index]; + copy_const_val(ptr_val, target_ptr_val, false); + ptr_val->type = dest_ptr_type; + + ConstExprValue *len_val = &result->value.data.x_struct.fields[slice_len_index]; + len_val->special = ConstValSpecialStatic; + len_val->type = ira->codegen->builtin_types.entry_usize; + ConstExprValue *target_len_val = &target_val->data.x_struct.fields[slice_len_index]; + ZigType *elem_type = src_ptr_type->data.pointer.child_type; + BigInt elem_size_bigint; + bigint_init_unsigned(&elem_size_bigint, type_size(ira->codegen, elem_type)); + bigint_mul(&len_val->data.x_bigint, &target_len_val->data.x_bigint, &elem_size_bigint); + + return result; + } + + IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, target, dest_slice_type); + ir_add_alloca(ira, result, dest_slice_type); + return result; } static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { @@ -22274,6 +22315,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdArrayToVector: case IrInstructionIdVectorToArray: case IrInstructionIdAssertZero: + case IrInstructionIdResizeSlice: zig_unreachable(); case IrInstructionIdReturn: @@ -22673,6 +22715,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCmpxchgGen: case IrInstructionIdCmpxchgSrc: case IrInstructionIdAssertZero: + case IrInstructionIdResizeSlice: return true; case IrInstructionIdPhi: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 75da24d1a9..935519fde3 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -990,6 +990,12 @@ static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruct fprintf(irp->f, ")"); } +static void ir_print_resize_slice(IrPrint *irp, IrInstructionResizeSlice *instruction) { + fprintf(irp->f, "@resizeSlice("); + ir_print_other_instruction(irp, instruction->operand); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1852,6 +1858,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdAssertZero: ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction); break; + case IrInstructionIdResizeSlice: + ir_print_resize_slice(irp, (IrInstructionResizeSlice *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 708bb91ac0..525d65d7fc 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -61,6 +61,7 @@ comptime { _ = @import("behavior/reflection.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); _ = @import("behavior/slice.zig"); + _ = @import("behavior/slicetobytes.zig"); _ = @import("behavior/struct.zig"); _ = @import("behavior/struct_contains_null_ptr_itself.zig"); _ = @import("behavior/struct_contains_slice_of_itself.zig"); diff --git a/test/stage1/behavior/slicetobytes.zig b/test/stage1/behavior/slicetobytes.zig new file mode 100644 index 0000000000..b86b38beaf --- /dev/null +++ b/test/stage1/behavior/slicetobytes.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const expect = std.testing.expect; + +test "@sliceToBytes packed struct at runtime and comptime" { + const Foo = packed struct { + a: u4, + b: u4, + }; + const S = struct { + fn doTheTest() void { + var foo: Foo = undefined; + var slice = @sliceToBytes(((*[1]Foo)(&foo))[0..1]); + slice[0] = 0x13; + switch (builtin.endian) { + builtin.Endian.Big => { + expect(foo.a == 0x1); + expect(foo.b == 0x3); + }, + builtin.Endian.Little => { + expect(foo.a == 0x3); + expect(foo.b == 0x1); + }, + } + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From 1066004b79d014b4c3d10da19c84c679e21b88e5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Feb 2019 14:44:14 -0500 Subject: [PATCH 091/134] better handling of arrays in packed structs * Separate LoadPtr IR instructions into pass1 and pass2 variants. * Define `type_size_bits` for extern structs to be the same as their `@sizeOf(T) * 8` and allow them in packed structs. * More helpful error messages when trying to use types in packed structs that are not allowed. * Support arrays in packed structs even when they are not byte-aligned. * Add compile error for using arrays in packed structs when the padding bits would be problematic. This is necessary since we do not have packed arrays. closes #677 --- src/all_types.hpp | 8 +++ src/analyze.cpp | 103 +++++++++++++++++++++++--------- src/codegen.cpp | 50 +++++++++++++--- src/ir.cpp | 32 ++++++++-- src/ir_print.cpp | 8 +++ test/compile_errors.zig | 54 +++++++++++++++++ test/stage1/behavior/struct.zig | 45 ++++++++++---- 7 files changed, 248 insertions(+), 52 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 0ee0fef2ca..fe36eb3a5d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2119,6 +2119,7 @@ enum IrInstructionId { IrInstructionIdUnOp, IrInstructionIdBinOp, IrInstructionIdLoadPtr, + IrInstructionIdLoadPtrGen, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, IrInstructionIdStructFieldPtr, @@ -2414,6 +2415,13 @@ struct IrInstructionLoadPtr { IrInstruction *ptr; }; +struct IrInstructionLoadPtrGen { + IrInstruction base; + + IrInstruction *ptr; + LLVMValueRef tmp_ptr; +}; + struct IrInstructionStorePtr { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 040483a05e..020446eb44 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -365,19 +365,19 @@ uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { if (!type_has_bits(type_entry)) return 0; - if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { - uint64_t result = 0; - for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry); + if (type_entry->id == ZigTypeIdStruct) { + if (type_entry->data.structure.layout == ContainerLayoutPacked) { + uint64_t result = 0; + for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + result += type_size_bits(g, type_entry->data.structure.fields[i].type_entry); + } + return result; + } else if (type_entry->data.structure.layout == ContainerLayoutExtern) { + return type_size(g, type_entry) * 8; } - return result; } else if (type_entry->id == ZigTypeIdArray) { ZigType *child_type = type_entry->data.array.child_type; - if (child_type->id == ZigTypeIdStruct && - child_type->data.structure.layout == ContainerLayoutPacked) - { - return type_entry->data.array.len * type_size_bits(g, child_type); - } + return type_entry->data.array.len * type_size_bits(g, child_type); } return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref); @@ -1444,7 +1444,10 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** return true; } -static bool type_allowed_in_packed_struct(ZigType *type_entry) { +static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType *type_entry, + AstNode *source_node) +{ + Error err; switch (type_entry->id) { case ZigTypeIdInvalid: zig_unreachable(); @@ -1461,27 +1464,74 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: - return false; + add_node_error(g, source_node, + buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation", + buf_ptr(&type_entry->name))); + return ErrorSemanticAnalyzeFail; case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: case ZigTypeIdFloat: case ZigTypeIdPointer: - case ZigTypeIdArray: case ZigTypeIdFn: case ZigTypeIdVector: - return true; + return ErrorNone; + case ZigTypeIdArray: { + ZigType *elem_type = type_entry->data.array.child_type; + if ((err = emit_error_unless_type_allowed_in_packed_struct(g, elem_type, source_node))) + return err; + if (type_size(g, type_entry) * 8 == type_size_bits(g, type_entry)) + return ErrorNone; + add_node_error(g, source_node, + buf_sprintf("array of '%s' not allowed in packed struct due to padding bits", + buf_ptr(&elem_type->name))); + return ErrorSemanticAnalyzeFail; + } case ZigTypeIdStruct: - return type_entry->data.structure.layout == ContainerLayoutPacked; - case ZigTypeIdUnion: - return type_entry->data.unionation.layout == ContainerLayoutPacked; - case ZigTypeIdOptional: - { - ZigType *child_type = type_entry->data.maybe.child_type; - return type_is_codegen_pointer(child_type); + switch (type_entry->data.structure.layout) { + case ContainerLayoutPacked: + case ContainerLayoutExtern: + return ErrorNone; + case ContainerLayoutAuto: + add_node_error(g, source_node, + buf_sprintf("non-packed, non-extern struct '%s' not allowed in packed struct; no guaranteed in-memory representation", + buf_ptr(&type_entry->name))); + return ErrorSemanticAnalyzeFail; } - case ZigTypeIdEnum: - return type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr; + zig_unreachable(); + case ZigTypeIdUnion: + switch (type_entry->data.unionation.layout) { + case ContainerLayoutPacked: + case ContainerLayoutExtern: + return ErrorNone; + case ContainerLayoutAuto: + add_node_error(g, source_node, + buf_sprintf("non-packed, non-extern union '%s' not allowed in packed struct; no guaranteed in-memory representation", + buf_ptr(&type_entry->name))); + return ErrorSemanticAnalyzeFail; + } + zig_unreachable(); + case ZigTypeIdOptional: + if (get_codegen_ptr_type(type_entry) != nullptr) { + return ErrorNone; + } else { + add_node_error(g, source_node, + buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation", + buf_ptr(&type_entry->name))); + return ErrorSemanticAnalyzeFail; + } + case ZigTypeIdEnum: { + AstNode *decl_node = type_entry->data.enumeration.decl_node; + if (decl_node->data.container_decl.init_arg_expr != nullptr) { + return ErrorNone; + } + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("type '%s' not allowed in packed struct; no guaranteed in-memory representation", + buf_ptr(&type_entry->name))); + add_error_note(g, msg, decl_node, + buf_sprintf("enum declaration does not specify an integer tag type")); + return ErrorSemanticAnalyzeFail; + } } zig_unreachable(); } @@ -2051,11 +2101,8 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { type_struct_field->gen_index = gen_field_index; if (packed) { - if (!type_allowed_in_packed_struct(field_type)) { - AstNode *field_source_node = decl_node->data.container_decl.fields.at(i); - add_node_error(g, field_source_node, - buf_sprintf("packed structs cannot contain fields of type '%s'", - buf_ptr(&field_type->name))); + AstNode *field_source_node = decl_node->data.container_decl.fields.at(i); + if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field_type, field_source_node))) { struct_type->data.structure.resolve_status = ResolveStatusInvalid; break; } diff --git a/src/codegen.cpp b/src/codegen.cpp index bc4005c87b..6c9e8ccc4b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3281,7 +3281,7 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, return nullptr; } -static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) { +static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtrGen *instruction) { ZigType *child_type = instruction->base.value.type; if (!type_has_bits(child_type)) return nullptr; @@ -3296,7 +3296,6 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI bool big_endian = g->is_big_endian; - assert(!handle_is_ptr(child_type)); LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); assert(host_bit_count == host_int_bytes * 8); @@ -3308,7 +3307,16 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); - return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); + if (!handle_is_ptr(child_type)) + return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); + + assert(instruction->tmp_ptr != nullptr); + LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, + LLVMPointerType(same_size_int, 0), ""); + LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); + return instruction->tmp_ptr; } static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) { @@ -5460,6 +5468,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdDeclVarSrc: case IrInstructionIdPtrCastSrc: case IrInstructionIdCmpxchgSrc: + case IrInstructionIdLoadPtr: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5478,8 +5487,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_br(g, executable, (IrInstructionBr *)instruction); case IrInstructionIdUnOp: return ir_render_un_op(g, executable, (IrInstructionUnOp *)instruction); - case IrInstructionIdLoadPtr: - return ir_render_load_ptr(g, executable, (IrInstructionLoadPtr *)instruction); + case IrInstructionIdLoadPtrGen: + return ir_render_load_ptr(g, executable, (IrInstructionLoadPtrGen *)instruction); case IrInstructionIdStorePtr: return ir_render_store_ptr(g, executable, (IrInstructionStorePtr *)instruction); case IrInstructionIdVarPtr: @@ -5836,8 +5845,32 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref); return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); } - case ZigTypeIdArray: - zig_panic("TODO bit pack an array"); + case ZigTypeIdArray: { + LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); + if (const_val->data.x_array.special == ConstArraySpecialUndef) { + return val; + } + expand_undef_array(g, const_val); + bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type + uint32_t packed_bits_size = type_size_bits(g, type_entry->data.array.child_type); + size_t used_bits = 0; + for (size_t i = 0; i < type_entry->data.array.len; i += 1) { + ConstExprValue *elem_val = &const_val->data.x_array.data.s_none.elements[i]; + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); + + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += packed_bits_size; + } + } + return val; + } case ZigTypeIdVector: zig_panic("TODO bit pack a vector"); case ZigTypeIdUnion: @@ -6728,6 +6761,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdResizeSlice) { IrInstructionResizeSlice *resize_slice_instruction = (IrInstructionResizeSlice *)instruction; slot = &resize_slice_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdLoadPtrGen) { + IrInstructionLoadPtrGen *load_ptr_inst = (IrInstructionLoadPtrGen *)instruction; + slot = &load_ptr_inst->tmp_ptr; } else if (instruction->id == IrInstructionIdVectorToArray) { IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); diff --git a/src/ir.cpp b/src/ir.cpp index a385592e2d..284f50e3a2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -416,6 +416,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) { return IrInstructionIdLoadPtr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) { + return IrInstructionIdLoadPtrGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) { return IrInstructionIdStorePtr; } @@ -2292,6 +2296,19 @@ static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *sourc return &instruction->base; } +static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *ptr, ZigType *ty) +{ + IrInstructionLoadPtrGen *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ty; + instruction->ptr = ptr; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { @@ -11534,10 +11551,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc IrInstructionRef *ref_inst = reinterpret_cast(ptr); return ref_inst->value; } - IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, ptr); - load_ptr_instruction->value.type = child_type; - return load_ptr_instruction; + IrInstruction *result = ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type); + if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) { + ir_add_alloca(ira, result, child_type); + } + return result; } else { ir_add_error_node(ira, source_instruction->source_node, buf_sprintf("attempt to dereference non-pointer type '%s'", @@ -13398,8 +13416,8 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio } // TODO audit the various ways to use @export - if (want_var_export && target->id == IrInstructionIdLoadPtr) { - IrInstructionLoadPtr *load_ptr = reinterpret_cast(target); + if (want_var_export && target->id == IrInstructionIdLoadPtrGen) { + IrInstructionLoadPtrGen *load_ptr = reinterpret_cast(target); if (load_ptr->ptr->id == IrInstructionIdVarPtr) { IrInstructionVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); ZigVar *var = var_ptr->var; @@ -22316,6 +22334,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdVectorToArray: case IrInstructionIdAssertZero: case IrInstructionIdResizeSlice: + case IrInstructionIdLoadPtrGen: zig_unreachable(); case IrInstructionIdReturn: @@ -22722,6 +22741,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdUnOp: case IrInstructionIdBinOp: case IrInstructionIdLoadPtr: + case IrInstructionIdLoadPtrGen: case IrInstructionIdConst: case IrInstructionIdCast: case IrInstructionIdContainerInitList: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 935519fde3..c9058d4bc7 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -336,6 +336,11 @@ static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { fprintf(irp->f, ".*"); } +static void ir_print_load_ptr_gen(IrPrint *irp, IrInstructionLoadPtrGen *instruction) { + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ".*"); +} + static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { fprintf(irp->f, "*"); ir_print_var_instruction(irp, instruction->ptr); @@ -1468,6 +1473,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdLoadPtr: ir_print_load_ptr(irp, (IrInstructionLoadPtr *)instruction); break; + case IrInstructionIdLoadPtrGen: + ir_print_load_ptr_gen(irp, (IrInstructionLoadPtrGen *)instruction); + break; case IrInstructionIdStorePtr: ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction); break; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 07e677b8ed..c39d34b3e9 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,60 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "packed struct with fields of not allowed types", + \\const A = packed struct { + \\ x: anyerror, + \\}; + \\const B = packed struct { + \\ x: [2]u24, + \\}; + \\const C = packed struct { + \\ x: [1]anyerror, + \\}; + \\const D = packed struct { + \\ x: [1]S, + \\}; + \\const E = packed struct { + \\ x: [1]U, + \\}; + \\const F = packed struct { + \\ x: ?anyerror, + \\}; + \\const G = packed struct { + \\ x: Enum, + \\}; + \\export fn entry() void { + \\ var a: A = undefined; + \\ var b: B = undefined; + \\ var r: C = undefined; + \\ var d: D = undefined; + \\ var e: E = undefined; + \\ var f: F = undefined; + \\ var g: G = undefined; + \\} + \\const S = struct { + \\ x: i32, + \\}; + \\const U = struct { + \\ A: i32, + \\ B: u32, + \\}; + \\const Enum = enum { + \\ A, + \\ B, + \\}; + , + ".tmp_source.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", + ".tmp_source.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits", + ".tmp_source.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", + ".tmp_source.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation", + ".tmp_source.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation", + ".tmp_source.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation", + ".tmp_source.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation", + ".tmp_source.zig:38:14: note: enum declaration does not specify an integer tag type", + ); + cases.addCase(x: { var tc = cases.create( "deduplicate undeclared identifier", diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index a045f482a2..e23ea49fe7 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -1,5 +1,6 @@ const std = @import("std"); const expect = std.testing.expect; +const expectEqualSlices = std.testing.expectEqualSlices; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -103,19 +104,20 @@ fn structInitializer() void { } test "fn call of struct field" { - expect(callStructField(Foo{ .ptr = aFunc }) == 13); -} + const Foo = struct { + ptr: fn () i32, + }; + const S = struct { + fn aFunc() i32 { + return 13; + } -const Foo = struct { - ptr: fn () i32, -}; + fn callStructField(foo: Foo) i32 { + return foo.ptr(); + } + }; -fn aFunc() i32 { - return 13; -} - -fn callStructField(foo: Foo) i32 { - return foo.ptr(); + expect(S.callStructField(Foo{ .ptr = S.aFunc }) == 13); } test "store member function in variable" { @@ -468,3 +470,24 @@ test "pointer to packed struct member in a stack variable" { b_ptr.* = 2; expect(s.b == 2); } + +test "non-byte-aligned array inside packed struct" { + const Foo = packed struct { + a: bool, + b: [0x16]u8, + }; + const S = struct { + fn bar(slice: []const u8) void { + expectEqualSlices(u8, slice, "abcdefghijklmnopqurstu"); + } + fn doTheTest() void { + var foo = Foo{ + .a = true, + .b = "abcdefghijklmnopqurstu", + }; + bar(foo.b); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From cbce61a209897598cf093180ef6b5d71c9566d6a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Feb 2019 16:09:14 -0500 Subject: [PATCH 092/134] better field access of types which have one possible value * When you do field access of a type which only has one possible value, the result is comptime-known. * StorePtr instructions which operate on pointers to types which only have one possible value, the result is a comptime no-op. closes #1554 --- src/ir.cpp | 32 ++++++++++++++++++++++---------- test/stage1/behavior/struct.zig | 8 ++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 284f50e3a2..5fae81f0ce 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13779,16 +13779,26 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return ir_const_void(ira, source_instr); } + ZigType *child_type = ptr->value.type->data.pointer.child_type; + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } - ZigType *child_type = ptr->value.type->data.pointer.child_type; IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type); if (value == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + case OnePossibleValueNo: + break; + } + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); @@ -13809,15 +13819,7 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar; copy_const_val(dest_val, &value->value, same_global_refs); if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - switch (type_has_one_possible_value(ira->codegen, child_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_instruction; - case OnePossibleValueNo: - ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; - break; - case OnePossibleValueYes: - break; - } + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; } return ir_const_void(ira, source_instr); } @@ -15346,6 +15348,16 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (bare_type->id == ZigTypeIdStruct) { TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field) { + switch (type_has_one_possible_value(ira->codegen, field->type_entry)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: { + IrInstruction *elem = ir_const(ira, source_instr, field->type_entry); + return ir_get_ref(ira, source_instr, elem, false, false); + } + case OnePossibleValueNo: + break; + } bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked); uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); uint32_t ptr_bit_offset = container_ptr->value.type->data.pointer.bit_offset_in_host; diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index e23ea49fe7..aa77892602 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -491,3 +491,11 @@ test "non-byte-aligned array inside packed struct" { S.doTheTest(); comptime S.doTheTest(); } + +test "packed struct with u0 field access" { + const S = packed struct { + f0: u0, + }; + var s = S{ .f0 = 0 }; + comptime expect(s.f0 == 0); +} From 0c5f8979045ff05e713bb1b7341496012189650f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Feb 2019 08:49:27 -0500 Subject: [PATCH 093/134] fix `@bitCast` when src/dest types have mismatched handle_is_ptr * separate BitCast and BitCastGen instructions * closes #991 * closes #1934 * unrelated: fix typo in docs (thanks gamester for pointing it out) --- doc/langref.html.in | 2 +- src/all_types.hpp | 8 +++++++ src/codegen.cpp | 38 +++++++++++++++++++++++++------- src/ir.cpp | 28 +++++++++++++++++++---- src/ir_print.cpp | 13 ++++++++--- test/stage1/behavior/bitcast.zig | 18 +++++++++++++++ 6 files changed, 91 insertions(+), 16 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 3a3eaf708c..9a286cb063 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7823,7 +7823,7 @@ Environments: coreclr opencl

    - The Zig Standard Library ({#syntax#}@import("std"){#endsyntax#}) has architecture, environment, and operating sytsem + The Zig Standard Library ({#syntax#}@import("std"){#endsyntax#}) has architecture, environment, and operating system abstractions, and thus takes additional work to support more platforms. Not all standard library code requires operating system abstractions, however, so things such as generic data structures work an all above platforms. diff --git a/src/all_types.hpp b/src/all_types.hpp index fe36eb3a5d..29598672cd 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2198,6 +2198,7 @@ enum IrInstructionId { IrInstructionIdPtrCastSrc, IrInstructionIdPtrCastGen, IrInstructionIdBitCast, + IrInstructionIdBitCastGen, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, IrInstructionIdPtrToInt, @@ -3055,6 +3056,13 @@ struct IrInstructionBitCast { IrInstruction *value; }; +struct IrInstructionBitCastGen { + IrInstruction base; + + IrInstruction *operand; + LLVMValueRef tmp_ptr; +}; + struct IrInstructionWidenOrShorten { IrInstruction base; diff --git a/src/codegen.cpp b/src/codegen.cpp index 6c9e8ccc4b..c43611e8af 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3073,14 +3073,32 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, - IrInstructionBitCast *instruction) + IrInstructionBitCastGen *instruction) { ZigType *wanted_type = instruction->base.value.type; - LLVMValueRef value = ir_llvm_value(g, instruction->value); - // We either bitcast the value directly or bitcast the pointer which does a pointer cast - LLVMTypeRef wanted_type_ref = handle_is_ptr(wanted_type) ? - LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref; - return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); + ZigType *actual_type = instruction->operand->value.type; + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + + bool wanted_is_ptr = handle_is_ptr(wanted_type); + bool actual_is_ptr = handle_is_ptr(actual_type); + if (wanted_is_ptr == actual_is_ptr) { + // We either bitcast the value directly or bitcast the pointer which does a pointer cast + LLVMTypeRef wanted_type_ref = wanted_is_ptr ? + LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref; + return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); + } else if (actual_is_ptr) { + LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(wanted_type->type_ref, 0); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); + uint32_t alignment = get_abi_alignment(g, actual_type); + return gen_load_untyped(g, bitcasted_ptr, alignment, false, ""); + } else { + assert(instruction->tmp_ptr != nullptr); + LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(actual_type->type_ref, 0); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, wanted_ptr_type_ref, ""); + uint32_t alignment = get_abi_alignment(g, wanted_type); + gen_store_untyped(g, value, bitcasted_ptr, alignment, false); + return instruction->tmp_ptr; + } } static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, @@ -5469,6 +5487,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdPtrCastSrc: case IrInstructionIdCmpxchgSrc: case IrInstructionIdLoadPtr: + case IrInstructionIdBitCast: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5565,8 +5584,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); case IrInstructionIdPtrCastGen: return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); - case IrInstructionIdBitCast: - return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); + case IrInstructionIdBitCastGen: + return ir_render_bit_cast(g, executable, (IrInstructionBitCastGen *)instruction); case IrInstructionIdWidenOrShorten: return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction); case IrInstructionIdPtrToInt: @@ -6764,6 +6783,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdLoadPtrGen) { IrInstructionLoadPtrGen *load_ptr_inst = (IrInstructionLoadPtrGen *)instruction; slot = &load_ptr_inst->tmp_ptr; + } else if (instruction->id == IrInstructionIdBitCastGen) { + IrInstructionBitCastGen *bit_cast_inst = (IrInstructionBitCastGen *)instruction; + slot = &bit_cast_inst->tmp_ptr; } else if (instruction->id == IrInstructionIdVectorToArray) { IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); diff --git a/src/ir.cpp b/src/ir.cpp index 5fae81f0ce..36f11cb108 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -744,6 +744,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { return IrInstructionIdBitCast; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCastGen *) { + return IrInstructionIdBitCastGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) { return IrInstructionIdWidenOrShorten; } @@ -2317,12 +2321,25 @@ static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *s instruction->dest_type = dest_type; instruction->value = value; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(value, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_bit_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *operand, ZigType *ty) +{ + IrInstructionBitCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ty; + instruction->operand = operand; + + ir_ref_instruction(operand, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) { @@ -21335,9 +21352,10 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_ return result; } - IrInstruction *result = ir_build_bit_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, value); - result->value.type = dest_type; + IrInstruction *result = ir_build_bit_cast_gen(ira, source_instr, value, dest_type); + if (handle_is_ptr(dest_type) && !handle_is_ptr(src_type)) { + ir_add_alloca(ira, result, dest_type); + } return result; } @@ -22347,6 +22365,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdAssertZero: case IrInstructionIdResizeSlice: case IrInstructionIdLoadPtrGen: + case IrInstructionIdBitCastGen: zig_unreachable(); case IrInstructionIdReturn: @@ -22804,6 +22823,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdPtrCastSrc: case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: + case IrInstructionIdBitCastGen: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: case IrInstructionIdIntToPtr: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c9058d4bc7..4572b30ded 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -920,14 +920,18 @@ static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruc static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { fprintf(irp->f, "@bitCast("); - if (instruction->dest_type) { - ir_print_other_instruction(irp, instruction->dest_type); - } + ir_print_other_instruction(irp, instruction->dest_type); fprintf(irp->f, ","); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); } +static void ir_print_bit_cast_gen(IrPrint *irp, IrInstructionBitCastGen *instruction) { + fprintf(irp->f, "@bitCast("); + ir_print_other_instruction(irp, instruction->operand); + fprintf(irp->f, ")"); +} + static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); @@ -1692,6 +1696,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBitCast: ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); break; + case IrInstructionIdBitCastGen: + ir_print_bit_cast_gen(irp, (IrInstructionBitCastGen *)instruction); + break; case IrInstructionIdWidenOrShorten: ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction); break; diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index d409975548..e86c50885e 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -94,3 +94,21 @@ test "@bitCast extern structs at runtime and comptime" { S.doTheTest(); comptime S.doTheTest(); } + +test "bitcast packed struct to integer and back" { + const LevelUpMove = packed struct { + move_id: u9, + level: u7, + }; + const S = struct { + fn doTheTest() void { + var move = LevelUpMove{ .move_id = 1, .level = 2 }; + var v = @bitCast(u16, move); + var back_to_a_move = @bitCast(LevelUpMove, v); + expect(back_to_a_move.move_id == 1); + expect(back_to_a_move.level == 2); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} From d0c39895aae085e421ada85d9d07f417d549c7e6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Feb 2019 10:56:49 -0500 Subject: [PATCH 094/134] docs for packed structs closes #1513 --- doc/langref.html.in | 260 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 228 insertions(+), 32 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 9a286cb063..b0c3a9e914 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8,13 +8,7 @@ body{ background-color:#111; color: #bbb; - font-family: system-ui, - /* Fallbacks for browsers that don't support system-ui */ - /* https://caniuse.com/#search=system-ui */ - -apple-system, /* iOS and macOS */ - Roboto, /* Android */ - "Segoe UI", /* Windows */ - sans-serif; + font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif; } a { color: #88f; @@ -263,7 +257,7 @@ pub fn main() void { true and false, true or false, !true); - + // optional var optional_value: ?[]const u8 = null; assert(optional_value == null); @@ -282,7 +276,7 @@ pub fn main() void { warn("\nerror union 1\ntype: {}\nvalue: {}\n", @typeName(@typeOf(number_or_error)), number_or_error); - + number_or_error = 1234; warn("\nerror union 2\ntype: {}\nvalue: {}\n", @@ -707,15 +701,21 @@ fn divide(a: i32, b: i32) i32 { {#code_end#}

    In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime, - and thus this division operation is vulnerable to both integer overflow and - division by zero. + and thus this division operation is vulnerable to both {#link|Integer Overflow#} and + {#link|Division by Zero#}.

    Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.

    - {#see_also|Integer Overflow|Division by Zero|Wrapping Operations#} +

    + Zig supports arbitrary bit-width integers, referenced by using + an identifier of i or u followed by digits. For example, the identifier + {#syntax#}i7{#endsyntax#} refers to a signed 7-bit integer. The maximum allowed bit-width of an + integer type is {#syntax#}65535{#endsyntax#}. +

    + {#see_also|Wrapping Operations#} {#header_close#} {#header_close#} {#header_open|Floats#} @@ -1652,7 +1652,7 @@ test "pointer slicing" { assert(array[3] == 5); } {#code_end#} -

    Pointers work at compile-time too, as long as the code does not depend on +

    Pointers work at compile-time too, as long as the code does not depend on an undefined memory layout:

    {#code_begin|test#} const assert = @import("std").debug.assert; @@ -2047,13 +2047,203 @@ test "linked list" { } {#code_end#} {#header_open|packed struct#} -

    {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.

    -

    TODO bit fields

    -

    TODO alignment

    -

    TODO endianness

    -

    TODO @bitOffsetOf and @byteOffsetOf

    -

    TODO mention how volatile loads and stores of bit packed fields could be more efficient when - done by hand instead of with packed struct

    +

    + Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: +

    +
      +
    • Fields remain in the order declared.
    • +
    • There is no padding between fields.
    • +
    • Zig supports arbitrary width {#link|Integers#} and although normally, integers with fewer + than 8 bits will still use 1 byte of memory, in packed structs, they use + exactly their bit width. +
    • +
    • {#syntax#}bool{#endsyntax#} fields use exactly 1 bit.
    • +
    • A {#link|packed enum#} field uses exactly the bit width of its integer tag type.
    • +
    • A {#link|packed union#} field uses exactly the bit width of the union field with + the largest bit width.
    • +
    • Non-byte-aligned fields are packed into the smallest possible + byte-aligned integers in accordance with the target endianness. +
    • +
    +

    + This means that a {#syntax#}packed struct{#endsyntax#} can participate + in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory. + This even works at {#link|comptime#}: +

    + {#code_begin|test#} +const std = @import("std"); +const builtin = @import("builtin"); +const assert = std.debug.assert; + +const Full = packed struct { + number: u16, +}; +const Divided = packed struct { + half1: u8, + quarter3: u4, + quarter4: u4, +}; + +test "@bitCast between packed structs" { + doTheTest(); + comptime doTheTest(); +} + +fn doTheTest() void { + assert(@sizeOf(Full) == 2); + assert(@sizeOf(Divided) == 2); + var full = Full{ .number = 0x1234 }; + var divided = @bitCast(Divided, full); + switch (builtin.endian) { + builtin.Endian.Big => { + assert(divided.half1 == 0x12); + assert(divided.quarter3 == 0x3); + assert(divided.quarter4 == 0x4); + }, + builtin.Endian.Little => { + assert(divided.half1 == 0x34); + assert(divided.quarter3 == 0x2); + assert(divided.quarter4 == 0x1); + }, + } +} + {#code_end#} +

    + Zig allows the address to be taken of a non-byte-aligned field: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +var foo = BitField{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "pointer to non-byte-aligned field" { + const ptr = &foo.b; + assert(ptr.* == 2); +} + {#code_end#} +

    + However, the pointer to a non-byte-aligned field has special properties and cannot + be passed when a normal pointer is expected: +

    + {#code_begin|test_err|expected type#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +var bit_field = BitField{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "pointer to non-bit-aligned field" { + assert(bar(&bit_field.b) == 2); +} + +fn bar(x: *const u3) u3 { + return x.*; +} + {#code_end#} +

    + In this case, the function {#syntax#}bar{#endsyntax#} cannot be called becuse the pointer + to the non-byte-aligned field mentions the bit offset, but the function expects a byte-aligned pointer. +

    +

    + Pointers to non-byte-aligned fields share the same address as the other fields within their host integer: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +var bit_field = BitField{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "pointer to non-bit-aligned field" { + assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b)); + assert(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c)); +} + {#code_end#} +

    + This can be observed with {#link|@bitOffsetOf#} and {#link|byteOffsetOf#}: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +const BitField = packed struct { + a: u3, + b: u3, + c: u2, +}; + +test "pointer to non-bit-aligned field" { + comptime { + assert(@bitOffsetOf(BitField, "a") == 0); + assert(@bitOffsetOf(BitField, "b") == 3); + assert(@bitOffsetOf(BitField, "c") == 6); + + assert(@byteOffsetOf(BitField, "a") == 0); + assert(@byteOffsetOf(BitField, "b") == 0); + assert(@byteOffsetOf(BitField, "c") == 0); + } +} + {#code_end#} +

    + Packed structs have 1-byte alignment. However if you have an overaligned pointer to a packed struct, + Zig should correctly understand the alignment of fields. However there is + a bug: +

    + {#code_begin|test_err#} +const S = packed struct { + a: u32, + b: u32, +}; +test "overaligned pointer to packed struct" { + var foo: S align(4) = undefined; + const ptr: *align(4) S = &foo; + const ptr_to_b: *u32 = &ptr.b; +} + {#code_end#} +

    When this bug is fixed, the above test in the documentation will unexpectedly pass, which will + cause the test suite to fail, notifying the bug fixer to update these docs. +

    +

    + It's also + planned to be able to set alignment of struct fields. +

    +

    + Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future. + For details on this subscribe to + this issue. + TODO update these docs with a recommendation on how to use packed structs with MMIO + (the use case for volatile packed structs) once this issue is resolved. + Don't worry, there will be a good solution for this use case in zig. +

    {#header_close#} {#header_open|struct Naming#}

    Since all structs are anonymous, Zig infers the type name based on a few rules.

    @@ -2203,8 +2393,8 @@ export fn entry(foo: Foo) void { } {#header_close#} {#header_open|packed enum#}

    By default, the size of enums is not guaranteed.

    -

    {#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the integer tag type - of the enum:

    +

    {#syntax#}packed enum{#endsyntax#} causes the size of the enum to be the same as the size of the + integer tag type of the enum:

    {#code_begin|test#} const std = @import("std"); @@ -2217,6 +2407,7 @@ test "packed enum" { std.debug.assert(@sizeOf(Number) == @sizeOf(u8)); } {#code_end#} +

    This makes the enum eligible to be in a {#link|packed struct#}.

    {#header_close#} {#see_also|@memberName|@memberCount|@tagName|@sizeOf#} {#header_close#} @@ -2344,7 +2535,12 @@ test "@tagName" { Unions with an enum tag are generated as a struct with a tag field and union field. Zig sorts the order of the tag and union field by the largest alignment.

    + {#header_open|packed union#} +

    A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible + to be in a {#link|packed struct#}. {#header_close#} + {#header_close#} + {#header_open|blocks#}

    Blocks are used to limit the scope of variable declarations: @@ -3771,7 +3967,7 @@ fn bang2() void { Here, the stack trace does not explain how the control flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call. One would have to open a debugger or further instrument the application - in order to find out. The error return trace, on the other hand, + in order to find out. The error return trace, on the other hand, shows exactly how the error bubbled up.

    @@ -3963,7 +4159,7 @@ test "optional type" { cast it to a different type:

    {#code_begin|syntax#} -const optional_value: ?i32 = null; +const optional_value: ?i32 = null; {#code_end#} {#header_close#} {#header_open|Optional Pointers#} @@ -5141,7 +5337,7 @@ async fn testResumeFromSuspend(my_result: *i32) void {

    {#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes as an operand a promise handle. - If the async function associated with the promise handle has already returned, + If the async function associated with the promise handle has already returned, then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value. Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its promise handle with the target coroutine. It becomes the target coroutine's responsibility @@ -5225,7 +5421,7 @@ fn seq(c: u8) void {

{#header_close#} - + {#header_close#} {#header_open|Builtin Functions#}

@@ -5580,13 +5776,13 @@ const warn = @import("std").debug.warn; const num1 = blk: { var val1: i32 = 99; - @compileLog("comptime val1 = ", val1); + @compileLog("comptime val1 = ", val1); val1 = val1 + 1; break :blk val1; }; test "main" { - @compileLog("comptime in main"); + @compileLog("comptime in main"); warn("Runtime in main, num1 = {}.\n", num1); } @@ -5596,10 +5792,10 @@ test "main" { will ouput:

- If all {#syntax#}@compileLog{#endsyntax#} calls are removed or + If all {#syntax#}@compileLog{#endsyntax#} calls are removed or not encountered by analysis, the program compiles successfully and the generated executable prints: -

+

{#code_begin|test#} const warn = @import("std").debug.warn; @@ -6425,7 +6621,7 @@ fn List(comptime T: type) type {

When {#syntax#}@This(){#endsyntax#} is used at global scope, it returns a reference to the current import. There is a proposal to remove the import type and use an empty struct - type instead. See + type instead. See #1047 for details.

{#header_close#} @@ -7560,7 +7756,7 @@ const c = @cImport({ {#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 + {#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#}.
  • From 2fe8a0831f2830a3357cdf65b2ebfe1214b13a83 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Feb 2019 11:09:17 -0500 Subject: [PATCH 095/134] add regression test for bitcast to array closes #421 --- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/421.zig | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 test/stage1/behavior/bugs/421.zig diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 525d65d7fc..f7fc1c42a4 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -20,6 +20,7 @@ comptime { _ = @import("behavior/bugs/1486.zig"); _ = @import("behavior/bugs/1851.zig"); _ = @import("behavior/bugs/394.zig"); + _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/726.zig"); diff --git a/test/stage1/behavior/bugs/421.zig b/test/stage1/behavior/bugs/421.zig new file mode 100644 index 0000000000..e5f67f61b7 --- /dev/null +++ b/test/stage1/behavior/bugs/421.zig @@ -0,0 +1,16 @@ +const assert = @import("std").debug.assert; + +test "bitCast to array" { + comptime testBitCastArray(); + testBitCastArray(); +} + +fn testBitCastArray() void { + assert(extractOne64(0x0123456789abcdef0123456789abcdef) == 0x0123456789abcdef); +} + +fn extractOne64(a: u128) u64 { + const x = @bitCast([2]u64, a); + return x[1]; +} + From 52bb71867d8d6e738eefb7dafead875eb21a4ae7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Feb 2019 13:28:57 -0500 Subject: [PATCH 096/134] implement vector negation also fix vector behavior tests, they weren't actually testing runtime vectors, but now they are. See #903 --- src/codegen.cpp | 11 ++-- src/ir.cpp | 104 ++++++++++++++++++++++---------- test/compile_errors.zig | 12 ++++ test/runtime_safety.zig | 41 +++++++++++++ test/stage1/behavior/vector.zig | 36 ++++++++--- 5 files changed, 157 insertions(+), 47 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index c43611e8af..0cb1e93211 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3229,7 +3229,8 @@ static LLVMValueRef ir_render_br(CodeGen *g, IrExecutable *executable, IrInstruc static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInstructionUnOp *un_op_instruction) { IrUnOp op_id = un_op_instruction->op_id; LLVMValueRef expr = ir_llvm_value(g, un_op_instruction->value); - ZigType *expr_type = un_op_instruction->value->value.type; + ZigType *operand_type = un_op_instruction->value->value.type; + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; switch (op_id) { case IrUnOpInvalid: @@ -3239,16 +3240,16 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst case IrUnOpNegation: case IrUnOpNegationWrap: { - if (expr_type->id == ZigTypeIdFloat) { + if (scalar_type->id == ZigTypeIdFloat) { ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base)); return LLVMBuildFNeg(g->builder, expr, ""); - } else if (expr_type->id == ZigTypeIdInt) { + } else if (scalar_type->id == ZigTypeIdInt) { if (op_id == IrUnOpNegationWrap) { return LLVMBuildNeg(g->builder, expr, ""); } else if (ir_want_runtime_safety(g, &un_op_instruction->base)) { LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); - return gen_overflow_op(g, expr_type, AddSubMulSub, zero, expr); - } else if (expr_type->data.integral.is_signed) { + return gen_overflow_op(g, operand_type, AddSubMulSub, zero, expr); + } else if (scalar_type->data.integral.is_signed) { return LLVMBuildNSWNeg(g->builder, expr, ""); } else { return LLVMBuildNUWNeg(g->builder, expr, ""); diff --git a/src/ir.cpp b/src/ir.cpp index 36f11cb108..a3c15dbd0f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14620,6 +14620,41 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_ zig_unreachable(); } +static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type, + ConstExprValue *operand_val, ConstExprValue *scalar_out_val, bool is_wrap_op) +{ + bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); + + bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || + scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); + + if (!ok_type) { + const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; + return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name))); + } + + if (is_float) { + float_negate(scalar_out_val, operand_val); + } else if (is_wrap_op) { + bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, + scalar_type->data.integral.bit_count); + } else { + bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); + } + + scalar_out_val->type = scalar_type; + scalar_out_val->special = ConstValSpecialStatic; + + if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) { + return nullptr; + } + + if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) { + return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow")); + } + return nullptr; +} + static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) { IrInstruction *value = instruction->value->child; ZigType *expr_type = value->value.type; @@ -14628,47 +14663,50 @@ static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *ins bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap); - bool is_float = (expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat); + ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; - if ((expr_type->id == ZigTypeIdInt && expr_type->data.integral.is_signed) || - expr_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)) - { - if (instr_is_comptime(value)) { - ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad); - if (!target_const_val) - return ira->codegen->invalid_instruction; + if (instr_is_comptime(value)) { + ConstExprValue *operand_val = ir_resolve_const(ira, value, UndefBad); + if (!operand_val) + return ira->codegen->invalid_instruction; - IrInstruction *result = ir_const(ira, &instruction->base, expr_type); - ConstExprValue *out_val = &result->value; - if (is_float) { - float_negate(out_val, target_const_val); - } else if (is_wrap_op) { - bigint_negate_wrap(&out_val->data.x_bigint, &target_const_val->data.x_bigint, - expr_type->data.integral.bit_count); - } else { - bigint_negate(&out_val->data.x_bigint, &target_const_val->data.x_bigint); + IrInstruction *result_instruction = ir_const(ira, &instruction->base, expr_type); + ConstExprValue *out_val = &result_instruction->value; + if (expr_type->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, operand_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = expr_type->data.vector.len; + for (size_t i = 0; i < len; i += 1) { + ConstExprValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i]; + ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_operand_val->type == scalar_type); + assert(scalar_out_val->type == scalar_type); + ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base, scalar_type, + scalar_operand_val, scalar_out_val, is_wrap_op); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, instruction->base.source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_instruction; + } } - if (is_wrap_op || is_float || expr_type->id == ZigTypeIdComptimeInt) { - return result; - } - - if (!bigint_fits_in_bits(&out_val->data.x_bigint, expr_type->data.integral.bit_count, true)) { - ir_add_error(ira, &instruction->base, buf_sprintf("negation caused overflow")); + out_val->type = expr_type; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_negation_scalar(ira, &instruction->base, scalar_type, operand_val, out_val, + is_wrap_op) != nullptr) + { return ira->codegen->invalid_instruction; } - return result; } - - IrInstruction *result = ir_build_un_op(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, - instruction->op_id, value); - result->value.type = expr_type; - return result; + return result_instruction; } - const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; - ir_add_error(ira, &instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name))); - return ira->codegen->invalid_instruction; + IrInstruction *result = ir_build_un_op(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, + instruction->op_id, value); + result->value.type = expr_type; + return result; } static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c39d34b3e9..48eb7cd85d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "comptime vector overflow shows the index", + \\comptime { + \\ var a: @Vector(4, u8) = []u8{ 1, 2, 255, 4 }; + \\ var b: @Vector(4, u8) = []u8{ 5, 6, 1, 8 }; + \\ var x = a + b; + \\} + , + ".tmp_source.zig:4:15: error: operation caused overflow", + ".tmp_source.zig:4:15: note: when computing vector element at index 2", + ); + cases.addTest( "packed struct with fields of not allowed types", \\const A = packed struct { diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 12cac64b3a..0427efabd5 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -118,6 +118,47 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} ); + cases.addRuntimeSafety("vector integer subtraction overflow", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ var a: @Vector(4, u32) = []u32{ 1, 2, 8, 4 }; + \\ var b: @Vector(4, u32) = []u32{ 5, 6, 7, 8 }; + \\ const x = sub(b, a); + \\} + \\fn sub(a: @Vector(4, u32), b: @Vector(4, u32)) @Vector(4, u32) { + \\ return a - b; + \\} + ); + + cases.addRuntimeSafety("vector integer multiplication overflow", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ var a: @Vector(4, u8) = []u8{ 1, 2, 200, 4 }; + \\ var b: @Vector(4, u8) = []u8{ 5, 6, 2, 8 }; + \\ const x = mul(b, a); + \\} + \\fn mul(a: @Vector(4, u8), b: @Vector(4, u8)) @Vector(4, u8) { + \\ return a * b; + \\} + ); + + cases.addRuntimeSafety("vector integer negation overflow", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\pub fn main() void { + \\ var a: @Vector(4, i16) = []i16{ 1, -32768, 200, 4 }; + \\ const x = neg(a); + \\} + \\fn neg(a: @Vector(4, i16)) @Vector(4, i16) { + \\ return -a; + \\} + ); + cases.addRuntimeSafety("integer subtraction overflow", \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index b0d2871454..21bbe3160d 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -5,11 +5,28 @@ const expect = std.testing.expect; test "vector wrap operators" { const S = struct { fn doTheTest() void { - const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; - const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; - expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 })); - expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 })); - expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 })); + var v: @Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 }; + var x: @Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 }; + expect(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ -2147483648, 2147483645, 33, 44 })); + expect(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 2147483646, 2147483647, 27, 36 })); + expect(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 2147483647, 2, 90, 160 })); + var z: @Vector(4, i32) = [4]i32{ 1, 2, 3, -2147483648 }; + expect(mem.eql(i32, ([4]i32)(-%z), [4]i32{ -1, -2, -3, -2147483648 })); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + +test "vector int operators" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; + var x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; + expect(mem.eql(i32, ([4]i32)(v + x), [4]i32{ 11, 22, 33, 44 })); + expect(mem.eql(i32, ([4]i32)(v - x), [4]i32{ 9, 18, 27, 36 })); + expect(mem.eql(i32, ([4]i32)(v * x), [4]i32{ 10, 40, 90, 160 })); + expect(mem.eql(i32, ([4]i32)(-v), [4]i32{ -10, -20, -30, -40 })); } }; S.doTheTest(); @@ -19,11 +36,12 @@ test "vector wrap operators" { test "vector float operators" { const S = struct { fn doTheTest() void { - const v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; - const x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 }; + var v: @Vector(4, f32) = [4]f32{ 10, 20, 30, 40 }; + var x: @Vector(4, f32) = [4]f32{ 1, 2, 3, 4 }; expect(mem.eql(f32, ([4]f32)(v + x), [4]f32{ 11, 22, 33, 44 })); expect(mem.eql(f32, ([4]f32)(v - x), [4]f32{ 9, 18, 27, 36 })); expect(mem.eql(f32, ([4]f32)(v * x), [4]f32{ 10, 40, 90, 160 })); + expect(mem.eql(f32, ([4]f32)(-x), [4]f32{ -1, -2, -3, -4 })); } }; S.doTheTest(); @@ -33,8 +51,8 @@ test "vector float operators" { test "vector bit operators" { const S = struct { fn doTheTest() void { - const v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; - const x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 }; + var v: @Vector(4, u8) = [4]u8{ 0b10101010, 0b10101010, 0b10101010, 0b10101010 }; + var x: @Vector(4, u8) = [4]u8{ 0b11110000, 0b00001111, 0b10101010, 0b01010101 }; expect(mem.eql(u8, ([4]u8)(v ^ x), [4]u8{ 0b01011010, 0b10100101, 0b00000000, 0b11111111 })); expect(mem.eql(u8, ([4]u8)(v | x), [4]u8{ 0b11111010, 0b10101111, 0b10101010, 0b11111111 })); expect(mem.eql(u8, ([4]u8)(v & x), [4]u8{ 0b10100000, 0b00001010, 0b10101010, 0b00000000 })); From 6fd8d455bcc6765e7411fa00d23dca1eb270aac9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 23 Feb 2019 09:35:56 -0500 Subject: [PATCH 097/134] better libc detection (#1996) * better libc detection This introduces a new command `zig libc` which prints the various paths of libc files. It outputs them to stdout in a simple text file format that it is capable of parsing. You can use `zig libc libc.txt` to validate a file. These arguments are gone: --libc-lib-dir [path] directory where libc crt1.o resides --libc-static-lib-dir [path] directory where libc crtbegin.o resides --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides Instead we have this argument: --libc [file] Provide a file which specifies libc paths This is used to pass a libc text file (which can be generated with `zig libc`). So it is easier to manage multiple cross compilation environments. `--cache on` now works when linking against libc. `ZigTarget` now has a bool field `is_native` Better error messaging when you try to link against libc or use `@cImport` but the various paths cannot be found. It should also be faster. * save native_libc.txt in zig-cache This avoids having to detect libc at runtime on every invocation. --- CMakeLists.txt | 1 + src/all_types.hpp | 27 +-- src/analyze.cpp | 198 +----------------- src/analyze.hpp | 2 - src/codegen.cpp | 234 ++++++++++++---------- src/codegen.hpp | 9 +- src/error.cpp | 2 + src/error.hpp | 2 + src/ir.cpp | 2 - src/libc_installation.cpp | 408 ++++++++++++++++++++++++++++++++++++++ src/libc_installation.hpp | 33 +++ src/link.cpp | 149 +++++--------- src/main.cpp | 127 +++++++----- src/os.cpp | 14 +- src/os.hpp | 6 +- src/target.cpp | 125 ++++-------- src/target.hpp | 19 +- src/translate_c.cpp | 8 +- 18 files changed, 774 insertions(+), 592 deletions(-) create mode 100644 src/libc_installation.cpp create mode 100644 src/libc_installation.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 814a05c49f..5eb35d2281 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,6 +414,7 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/error.cpp" "${CMAKE_SOURCE_DIR}/src/ir.cpp" "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" + "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp" "${CMAKE_SOURCE_DIR}/src/link.cpp" "${CMAKE_SOURCE_DIR}/src/main.cpp" "${CMAKE_SOURCE_DIR}/src/os.cpp" diff --git a/src/all_types.hpp b/src/all_types.hpp index 29598672cd..f29411476e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -18,6 +18,7 @@ #include "bigfloat.hpp" #include "target.hpp" #include "tokenizer.hpp" +#include "libc_installation.hpp" struct AstNode; struct ImportTableEntry; @@ -1743,6 +1744,9 @@ struct CodeGen { Buf *wanted_output_file_path; Buf cache_dir; + Buf *zig_c_headers_dir; // Cannot be overridden; derived from zig_lib_dir. + Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir. + IrInstruction *invalid_instruction; IrInstruction *unreach_instruction; @@ -1791,6 +1795,8 @@ struct CodeGen { bool system_linker_hack; //////////////////////////// Participates in Input Parameter Cache Hash + /////// Note: there is a separate cache hash for builtin.zig, when adding fields, + /////// consider if they need to go into both. ZigList link_libs_list; // add -framework [name] args to linker ZigList darwin_frameworks; @@ -1801,6 +1807,8 @@ struct CodeGen { ZigList assembly_files; ZigList lib_dirs; + ZigLibCInstallation *libc; + size_t version_major; size_t version_minor; size_t version_patch; @@ -1809,14 +1817,13 @@ struct CodeGen { EmitFileType emit_file_type; BuildMode build_mode; OutType out_type; - ZigTarget zig_target; + const ZigTarget *zig_target; TargetSubsystem subsystem; ValgrindSupport valgrind_support; bool is_static; bool strip_debug_symbols; bool is_test_build; bool is_single_threaded; - bool is_native_target; bool linker_rdynamic; bool each_lib_rpath; bool disable_pic; @@ -1827,26 +1834,14 @@ struct CodeGen { Buf *test_filter; Buf *test_name_prefix; PackageTableEntry *root_package; + Buf *zig_lib_dir; + Buf *zig_std_dir; const char **llvm_argv; size_t llvm_argv_len; const char **clang_argv; size_t clang_argv_len; - - //////////////////////////// Unsorted - - Buf *libc_lib_dir; - Buf *libc_static_lib_dir; - Buf *libc_include_dir; - Buf *msvc_lib_dir; - Buf *kernel32_lib_dir; - Buf *zig_lib_dir; - Buf *zig_std_dir; - Buf *zig_c_headers_dir; - Buf *zig_std_special_dir; - Buf *dynamic_linker; - ZigWindowsSDK *win_sdk; }; enum VarLinkage { diff --git a/src/analyze.cpp b/src/analyze.cpp index 020446eb44..0ecff11142 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1102,10 +1102,10 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { if (type_is_c_abi_int(g, fn_type_id->return_type)) { return false; } - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + if (g->zig_target->arch.arch == ZigLLVM_x86_64) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); return abi_class == X64CABIClass_MEMORY; - } else if (target_is_arm(&g->zig_target)) { + } else if (target_is_arm(g->zig_target)) { return type_size(g, fn_type_id->return_type) > 16; } zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481"); @@ -3304,16 +3304,16 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLi g->have_c_main = true; g->subsystem = TargetSubsystemConsole; } else if (buf_eql_str(symbol_name, "WinMain") && - g->zig_target.os == OsWindows) + g->zig_target->os == OsWindows) { g->have_winmain = true; g->subsystem = TargetSubsystemWindows; } else if (buf_eql_str(symbol_name, "WinMainCRTStartup") && - g->zig_target.os == OsWindows) + g->zig_target->os == OsWindows) { g->have_winmain_crt_startup = true; } else if (buf_eql_str(symbol_name, "DllMainCRTStartup") && - g->zig_target.os == OsWindows) + g->zig_target->os == OsWindows) { g->have_dllmain_crt_startup = true; } @@ -4651,186 +4651,6 @@ bool handle_is_ptr(ZigType *type_entry) { zig_unreachable(); } -static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { - if (g->win_sdk == nullptr) { - if (zig_find_windows_sdk(&g->win_sdk)) { - fprintf(stderr, "unable to determine windows sdk path\n"); - exit(1); - } - } - assert(g->win_sdk != nullptr); - return g->win_sdk; -} - - -static Buf *get_linux_libc_lib_path(const char *o_file) { - const char *cc_exe = getenv("CC"); - cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; - ZigList args = {}; - args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file))); - Termination term; - Buf *out_stderr = buf_alloc(); - Buf *out_stdout = buf_alloc(); - Error err; - if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { - zig_panic("unable to determine libc lib path: executing C compiler: %s", err_str(err)); - } - if (term.how != TerminationIdClean || term.code != 0) { - zig_panic("unable to determine libc lib path: executing C compiler command failed"); - } - if (buf_ends_with_str(out_stdout, "\n")) { - buf_resize(out_stdout, buf_len(out_stdout) - 1); - } - if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, o_file)) { - zig_panic("unable to determine libc lib path: C compiler could not find %s", o_file); - } - Buf *result = buf_alloc(); - os_path_dirname(out_stdout, result); - return result; -} - -static Buf *get_posix_libc_include_path(void) { - const char *cc_exe = getenv("CC"); - cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; - ZigList args = {}; - args.append("-E"); - args.append("-Wp,-v"); - args.append("-xc"); - args.append("/dev/null"); - Termination term; - Buf *out_stderr = buf_alloc(); - Buf *out_stdout = buf_alloc(); - Error err; - if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { - zig_panic("unable to determine libc include path: executing C compiler: %s", err_str(err)); - } - if (term.how != TerminationIdClean || term.code != 0) { - zig_panic("unable to determine libc include path: executing C compiler command failed"); - } - char *prev_newline = buf_ptr(out_stderr); - ZigList search_paths = {}; - for (;;) { - char *newline = strchr(prev_newline, '\n'); - if (newline == nullptr) { - break; - } - *newline = 0; - if (prev_newline[0] == ' ') { - search_paths.append(prev_newline); - } - prev_newline = newline + 1; - } - if (search_paths.length == 0) { - zig_panic("unable to determine libc include path: even C compiler does not know where libc headers are"); - } - for (size_t i = 0; i < search_paths.length; i += 1) { - // search in reverse order - const char *search_path = search_paths.items[search_paths.length - i - 1]; - // cut off spaces - while (*search_path == ' ') { - search_path += 1; - } - Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path); - bool exists; - if ((err = os_file_exists(stdlib_path, &exists))) { - exists = false; - } - if (exists) { - return buf_create_from_str(search_path); - } - } - zig_panic("unable to determine libc include path: stdlib.h not found in C compiler search paths"); -} - -void find_libc_include_path(CodeGen *g) { - if (g->libc_include_dir == nullptr) { - if (!g->is_native_target) { - return; - } - - if (g->zig_target.os == OsWindows) { - ZigWindowsSDK *sdk = get_windows_sdk(g); - g->libc_include_dir = buf_alloc(); - if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) { - fprintf(stderr, "Unable to determine libc include path. --libc-include-dir"); - exit(1); - } - } else if (g->zig_target.os == OsLinux || - g->zig_target.os == OsMacOSX || - g->zig_target.os == OsFreeBSD || - g->zig_target.os == OsNetBSD) - { - g->libc_include_dir = get_posix_libc_include_path(); - } else { - fprintf(stderr, "Unable to determine libc include path.\n" - "TODO: implement finding libc at runtime for other operating systems.\n" - "in the meantime, you can use as a workaround: --libc-include-dir\n"); - exit(1); - } - } - assert(buf_len(g->libc_include_dir) != 0); -} - -void find_libc_lib_path(CodeGen *g) { - // later we can handle this better by reporting an error via the normal mechanism - if (g->libc_lib_dir == nullptr || - (g->zig_target.os == OsWindows && (g->msvc_lib_dir == nullptr || g->kernel32_lib_dir == nullptr))) - { - if (g->zig_target.os == OsWindows) { - ZigWindowsSDK *sdk = get_windows_sdk(g); - - if (g->msvc_lib_dir == nullptr) { - if (sdk->msvc_lib_dir_ptr == nullptr) { - fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir"); - exit(1); - } - g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); - } - - if (g->libc_lib_dir == nullptr) { - Buf* ucrt_lib_path = buf_alloc(); - if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { - fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir"); - exit(1); - } - g->libc_lib_dir = ucrt_lib_path; - } - - if (g->kernel32_lib_dir == nullptr) { - Buf* kern_lib_path = buf_alloc(); - if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { - fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir"); - exit(1); - } - g->kernel32_lib_dir = kern_lib_path; - } - - } else if (g->zig_target.os == OsLinux) { - g->libc_lib_dir = get_linux_libc_lib_path("crt1.o"); - } else if ((g->zig_target.os == OsFreeBSD) || (g->zig_target.os == OsNetBSD)) { - g->libc_lib_dir = buf_create_from_str("/usr/lib"); - } else { - zig_panic("Unable to determine libc lib path."); - } - } else { - assert(buf_len(g->libc_lib_dir) != 0); - } - - if (g->libc_static_lib_dir == nullptr) { - if ((g->zig_target.os == OsWindows) && (g->msvc_lib_dir != NULL)) { - return; - } else if (g->zig_target.os == OsLinux) { - g->libc_static_lib_dir = get_linux_libc_lib_path("crtbegin.o"); - } else if ((g->zig_target.os == OsFreeBSD) || (g->zig_target.os == OsNetBSD)) { - g->libc_static_lib_dir = buf_create_from_str("/usr/lib"); - } else { - zig_panic("Unable to determine libc static lib path."); - } - } else { - assert(buf_len(g->libc_static_lib_dir) != 0); - } -} - static uint32_t hash_ptr(void *ptr) { return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX); } @@ -6736,14 +6556,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; - if (g->enable_cache && is_libc && g->zig_target.os != OsMacOSX && - g->zig_target.os != OsIOS && g->zig_target.os != OsFreeBSD && - g->zig_target.os != OsNetBSD) { - fprintf(stderr, "TODO linking against libc is currently incompatible with `--cache on`.\n" - "Zig is not yet capable of determining whether the libc installation has changed on subsequent builds.\n"); - exit(1); - } - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 39c1450678..de4785fb27 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -40,8 +40,6 @@ ZigType *get_promise_type(CodeGen *g, ZigType *result_type); ZigType *get_promise_frame_type(CodeGen *g, ZigType *return_type); ZigType *get_test_fn_type(CodeGen *g); bool handle_is_ptr(ZigType *type_entry); -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); diff --git a/src/codegen.cpp b/src/codegen.cpp index 0cb1e93211..54da63e9f9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -29,9 +29,9 @@ static void init_darwin_native(CodeGen *g) { // Allow conflicts among OSX and iOS, but choose the default platform. if (osx_target && ios_target) { - if (g->zig_target.arch.arch == ZigLLVM_arm || - g->zig_target.arch.arch == ZigLLVM_aarch64 || - g->zig_target.arch.arch == ZigLLVM_thumb) + if (g->zig_target->arch.arch == ZigLLVM_arm || + g->zig_target->arch.arch == ZigLLVM_aarch64 || + g->zig_target->arch.arch == ZigLLVM_thumb) { osx_target = nullptr; } else { @@ -43,7 +43,7 @@ static void init_darwin_native(CodeGen *g) { g->mmacosx_version_min = buf_create_from_str(osx_target); } else if (ios_target) { g->mios_version_min = buf_create_from_str(ios_target); - } else if (g->zig_target.os != OsIOS) { + } else if (g->zig_target->os != OsIOS) { g->mmacosx_version_min = buf_create_from_str("10.10"); } } @@ -88,13 +88,15 @@ static const char *symbols_that_llvm_depends_on[] = { }; CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, - Buf *zig_lib_dir, Buf *override_std_dir) + Buf *zig_lib_dir, Buf *override_std_dir, ZigLibCInstallation *libc) { CodeGen *g = allocate(1); codegen_add_time_event(g, "Initialize"); + g->libc = libc; g->zig_lib_dir = zig_lib_dir; + g->zig_target = target; if (override_std_dir == nullptr) { g->zig_std_dir = buf_alloc(); @@ -149,44 +151,19 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->zig_std_special_dir = buf_alloc(); os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); - if (target) { - // cross compiling, so we can't rely on all the configured stuff since - // that's for native compilation - g->zig_target = *target; - resolve_target_object_format(&g->zig_target); - g->dynamic_linker = nullptr; - g->libc_lib_dir = nullptr; - g->libc_static_lib_dir = nullptr; - g->libc_include_dir = nullptr; - g->msvc_lib_dir = nullptr; - g->kernel32_lib_dir = nullptr; + assert(target != nullptr); + if (!target->is_native) { g->each_lib_rpath = false; } else { - // native compilation, we can rely on the configuration stuff - g->is_native_target = true; - get_native_target(&g->zig_target); - g->dynamic_linker = nullptr; // find it at runtime - g->libc_lib_dir = nullptr; // find it at runtime - g->libc_static_lib_dir = nullptr; // find it at runtime - g->libc_include_dir = nullptr; // find it at runtime - g->msvc_lib_dir = nullptr; // find it at runtime - g->kernel32_lib_dir = nullptr; // find it at runtime g->each_lib_rpath = true; - if (g->zig_target.os == OsMacOSX || - g->zig_target.os == OsIOS) - { + if (target_is_darwin(g->zig_target)) { init_darwin_native(g); } } - // On Darwin/MacOS/iOS, we always link libSystem which contains libc. - if (g->zig_target.os == OsMacOSX || - g->zig_target.os == OsIOS || - g->zig_target.os == OsFreeBSD || - g->zig_target.os == OsNetBSD) - { + if (target_requires_libc(g->zig_target)) { g->libc_link_lib = create_link_lib(buf_create_from_str("c")); g->link_libs_list.append(g->libc_link_lib); } @@ -254,30 +231,6 @@ void codegen_set_out_name(CodeGen *g, Buf *out_name) { g->root_out_name = out_name; } -void codegen_set_libc_lib_dir(CodeGen *g, Buf *libc_lib_dir) { - g->libc_lib_dir = libc_lib_dir; -} - -void codegen_set_libc_static_lib_dir(CodeGen *g, Buf *libc_static_lib_dir) { - g->libc_static_lib_dir = libc_static_lib_dir; -} - -void codegen_set_libc_include_dir(CodeGen *g, Buf *libc_include_dir) { - g->libc_include_dir = libc_include_dir; -} - -void codegen_set_msvc_lib_dir(CodeGen *g, Buf *msvc_lib_dir) { - g->msvc_lib_dir = msvc_lib_dir; -} - -void codegen_set_kernel32_lib_dir(CodeGen *g, Buf *kernel32_lib_dir) { - g->kernel32_lib_dir = kernel32_lib_dir; -} - -void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker) { - g->dynamic_linker = dynamic_linker; -} - void codegen_add_lib_dir(CodeGen *g, const char *dir) { g->lib_dirs.append(dir); } @@ -390,11 +343,11 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { case CallingConventionC: return LLVMCCallConv; case CallingConventionCold: // cold calling convention only works on x86. - if (g->zig_target.arch.arch == ZigLLVM_x86 || - g->zig_target.arch.arch == ZigLLVM_x86_64) + if (g->zig_target->arch.arch == ZigLLVM_x86 || + g->zig_target->arch.arch == ZigLLVM_x86_64) { // cold calling convention is not supported on windows - if (g->zig_target.os == OsWindows) { + if (g->zig_target->os == OsWindows) { return LLVMCCallConv; } else { return LLVMColdCallConv; @@ -407,7 +360,7 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { zig_unreachable(); case CallingConventionStdcall: // stdcall calling convention only works on x86. - if (g->zig_target.arch.arch == ZigLLVM_x86) { + if (g->zig_target->arch.arch == ZigLLVM_x86) { return LLVMX86StdcallCallConv; } else { return LLVMCCallConv; @@ -419,7 +372,7 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { } static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) { - if (g->zig_target.os == OsWindows) { + if (g->zig_target->os == OsWindows) { addLLVMFnAttr(fn_val, "uwtable"); } } @@ -455,13 +408,13 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) { } static void maybe_export_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { - if (linkage != GlobalLinkageIdInternal && g->zig_target.os == OsWindows) { + if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows) { LLVMSetDLLStorageClass(global_value, LLVMDLLExportStorageClass); } } static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { - if (linkage != GlobalLinkageIdInternal && g->zig_target.os == OsWindows) { + if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows) { // TODO come up with a good explanation/understanding for why we never do // DLLImportStorageClass. Empirically it only causes problems. But let's have // this documented and then clean up the code accordingly. @@ -506,7 +459,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { bool external_linkage = linkage != GlobalLinkageIdInternal; CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; if (cc == CallingConventionStdcall && external_linkage && - g->zig_target.arch.arch == ZigLLVM_x86) + g->zig_target->arch.arch == ZigLLVM_x86) { // prevent llvm name mangling symbol_name = buf_sprintf("\x01_%s", buf_ptr(symbol_name)); @@ -2138,7 +2091,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ return true; } - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + if (g->zig_target->arch.arch == ZigLLVM_x86_64) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); size_t ty_size = type_size(g, ty); if (abi_class == X64CABIClass_MEMORY) { @@ -3381,15 +3334,15 @@ static bool value_is_all_undef(ConstExprValue *const_val) { static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default_value, LLVMValueRef request, LLVMValueRef a1, LLVMValueRef a2, LLVMValueRef a3, LLVMValueRef a4, LLVMValueRef a5) { - if (!target_has_valgrind_support(&g->zig_target)) { + if (!target_has_valgrind_support(g->zig_target)) { return default_value; } LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; bool asm_has_side_effects = true; bool asm_is_alignstack = false; - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { - if (g->zig_target.os == OsLinux || target_is_darwin(&g->zig_target) || g->zig_target.os == OsSolaris || - (g->zig_target.os == OsWindows && g->zig_target.env_type != ZigLLVM_MSVC)) + if (g->zig_target->arch.arch == ZigLLVM_x86_64) { + if (g->zig_target->os == OsLinux || target_is_darwin(g->zig_target) || g->zig_target->os == OsSolaris || + (g->zig_target->os == OsWindows && g->zig_target->env_type != ZigLLVM_MSVC)) { if (g->cur_fn->valgrind_client_request_array == nullptr) { LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); @@ -3436,7 +3389,7 @@ static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default } static bool want_valgrind_support(CodeGen *g) { - if (!target_has_valgrind_support(&g->zig_target)) + if (!target_has_valgrind_support(g->zig_target)) return false; switch (g->valgrind_support) { case ValgrindSupportDisabled: @@ -3629,7 +3582,7 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { LLVMValueRef write_register_fn_val = get_write_register_fn_val(g); if (g->sp_md_node == nullptr) { - Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(&g->zig_target.arch)); + Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(&g->zig_target->arch)); LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1); g->sp_md_node = LLVMMDNode(&str_node, 1); } @@ -7042,7 +6995,7 @@ static void define_builtin_types(CodeGen *g) { for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) { const CIntTypeInfo *info = &c_int_type_infos[i]; - uint32_t size_in_bits = target_c_type_size_in_bits(&g->zig_target, info->id); + uint32_t size_in_bits = target_c_type_size_in_bits(g->zig_target, info->id); bool is_signed = info->is_signed; ZigType *entry = new_type_table_entry(ZigTypeIdInt); @@ -7309,6 +7262,9 @@ static const char *build_mode_to_str(BuildMode build_mode) { Buf *codegen_generate_builtin_source(CodeGen *g) { Buf *contents = buf_alloc(); + // NOTE: when editing this file, you may need to make modifications to the + // cache input parameters in define_builtin_compile_vars + // Modifications to this struct must be coordinated with code that does anything with // g->stack_trace_type. There are hard-coded references to the field indexes. buf_append_str(contents, @@ -7328,7 +7284,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *name = get_target_os_name(os_type); buf_appendf(contents, " %s,\n", name); - if (os_type == g->zig_target.os) { + if (os_type == g->zig_target->os) { g->target_os_index = i; cur_os = name; } @@ -7350,8 +7306,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, " %s,\n", buf_ptr(arch_name)); - if (arch_type->arch == g->zig_target.arch.arch && - arch_type->sub_arch == g->zig_target.arch.sub_arch) + if (arch_type->arch == g->zig_target->arch.arch && + arch_type->sub_arch == g->zig_target->arch.sub_arch) { g->target_arch_index = i; cur_arch = buf_ptr(arch_name); @@ -7370,7 +7326,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *name = ZigLLVMGetEnvironmentTypeName(environ_type); buf_appendf(contents, " %s,\n", name); - if (environ_type == g->zig_target.env_type) { + if (environ_type == g->zig_target->env_type) { g->target_environ_index = i; cur_environ = name; } @@ -7388,7 +7344,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *name = get_target_oformat_name(oformat); buf_appendf(contents, " %s,\n", name); - if (oformat == g->zig_target.oformat) { + ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target); + if (oformat == target_oformat) { g->target_oformat_index = i; cur_obj_fmt = name; } @@ -7707,14 +7664,15 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_int(&cache_hash, g->build_mode); cache_bool(&cache_hash, g->is_test_build); cache_bool(&cache_hash, g->is_single_threaded); - cache_int(&cache_hash, g->zig_target.arch.arch); - cache_int(&cache_hash, g->zig_target.arch.sub_arch); - cache_int(&cache_hash, g->zig_target.vendor); - cache_int(&cache_hash, g->zig_target.os); - cache_int(&cache_hash, g->zig_target.env_type); - cache_int(&cache_hash, g->zig_target.oformat); + cache_int(&cache_hash, g->zig_target->is_native); + cache_int(&cache_hash, g->zig_target->arch.arch); + cache_int(&cache_hash, g->zig_target->arch.sub_arch); + cache_int(&cache_hash, g->zig_target->vendor); + cache_int(&cache_hash, g->zig_target->os); + cache_int(&cache_hash, g->zig_target->env_type); cache_bool(&cache_hash, g->have_err_ret_tracing); cache_bool(&cache_hash, g->libc_link_lib != nullptr); + cache_bool(&cache_hash, g->valgrind_support); Buf digest = BUF_INIT; buf_resize(&digest, 0); @@ -7783,11 +7741,11 @@ static void init(CodeGen *g) { assert(g->root_out_name); g->module = LLVMModuleCreateWithName(buf_ptr(g->root_out_name)); - get_target_triple(&g->triple_str, &g->zig_target); + get_target_triple(&g->triple_str, g->zig_target); LLVMSetTarget(g->module, buf_ptr(&g->triple_str)); - if (g->zig_target.oformat == ZigLLVM_COFF) { + if (target_object_format(g->zig_target) == ZigLLVM_COFF) { ZigLLVMAddModuleCodeViewFlag(g->module); } else { ZigLLVMAddModuleDebugInfoFlag(g->module); @@ -7815,11 +7773,11 @@ static void init(CodeGen *g) { const char *target_specific_cpu_args; const char *target_specific_features; - if (g->is_native_target) { + if (g->zig_target->is_native) { // LLVM creates invalid binaries on Windows sometimes. // See https://github.com/ziglang/zig/issues/508 // As a workaround we do not use target native features on Windows. - if (g->zig_target.os == OsWindows || g->zig_target.os == OsUefi) { + if (g->zig_target->os == OsWindows || g->zig_target->os == OsUefi) { target_specific_cpu_args = ""; target_specific_features = ""; } else { @@ -7892,9 +7850,59 @@ static void init(CodeGen *g) { } } -void codegen_translate_c(CodeGen *g, Buf *full_path) { - find_libc_include_path(g); +static void detect_libc(CodeGen *g) { + Error err; + if (g->libc != nullptr || g->libc_link_lib == nullptr) + return; + + if (g->zig_target->is_native) { + g->libc = allocate(1); + + // Look for zig-cache/native_libc.txt + Buf *native_libc_txt = buf_alloc(); + os_path_join(&g->cache_dir, buf_create_from_str("native_libc.txt"), native_libc_txt); + if ((err = zig_libc_parse(g->libc, native_libc_txt, g->zig_target, false))) { + if ((err = zig_libc_find_native(g->libc, true))) { + fprintf(stderr, + "Unable to link against libc: Unable to find libc installation: %s\n" + "See `zig libc --help` for more details.\n", err_str(err)); + exit(1); + } + if ((err = os_make_path(&g->cache_dir))) { + fprintf(stderr, "Unable to create %s directory: %s\n", + buf_ptr(&g->cache_dir), err_str(err)); + exit(1); + } + Buf *native_libc_tmp = buf_sprintf("%s.tmp", buf_ptr(native_libc_txt)); + FILE *file = fopen(buf_ptr(native_libc_tmp), "wb"); + if (file == nullptr) { + fprintf(stderr, "Unable to open %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); + exit(1); + } + zig_libc_render(g->libc, file); + if (fclose(file) != 0) { + fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); + exit(1); + } + if (rename(buf_ptr(native_libc_tmp), buf_ptr(native_libc_txt)) == -1) { + fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(native_libc_txt), strerror(errno)); + exit(1); + } + } + } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) && + !target_is_darwin(g->zig_target)) + { + // Currently darwin is the only platform that we can link libc on when not compiling natively, + // without a cross compiling libc kit. + fprintf(stderr, + "Cannot link against libc for non-native OS '%s' without providing a libc installation file.\n" + "See `zig libc --help` for more details.\n", get_target_os_name(g->zig_target->os)); + exit(1); + } +} + +void codegen_translate_c(CodeGen *g, Buf *full_path) { Buf *src_basename = buf_alloc(); Buf *src_dirname = buf_alloc(); os_path_split(full_path, src_dirname, src_basename); @@ -7905,6 +7913,8 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) { g->root_import = import; import->decls_scope = create_decls_scope(g, nullptr, nullptr, nullptr, import); + detect_libc(g); + init(g); import->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); @@ -8064,14 +8074,14 @@ static void gen_root_source(CodeGen *g) { } report_errors_and_maybe_exit(g); - if (!g->is_test_build && g->zig_target.os != OsFreestanding && - g->zig_target.os != OsUefi && + if (!g->is_test_build && g->zig_target->os != OsFreestanding && + g->zig_target->os != OsUefi && !g->have_c_main && !g->have_winmain && !g->have_winmain_crt_startup && ((g->have_pub_main && g->out_type == OutTypeObj) || g->out_type == OutTypeExe)) { g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap.zig"); } - if (g->zig_target.os == OsWindows && !g->have_dllmain_crt_startup && + if (g->zig_target->os == OsWindows && !g->have_dllmain_crt_startup && g->out_type == OutTypeLib && !g->is_static) { g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap_lib.zig"); @@ -8619,6 +8629,8 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { } cache_buf(ch, compiler_id); cache_buf(ch, g->root_out_name); + cache_buf(ch, g->zig_lib_dir); + cache_buf(ch, g->zig_std_dir); cache_list_of_link_lib(ch, g->link_libs_list.items, g->link_libs_list.length); cache_list_of_buf(ch, g->darwin_frameworks.items, g->darwin_frameworks.length); cache_list_of_buf(ch, g->rpath_list.items, g->rpath_list.length); @@ -8628,18 +8640,17 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->emit_file_type); cache_int(ch, g->build_mode); cache_int(ch, g->out_type); - cache_int(ch, g->zig_target.arch.arch); - cache_int(ch, g->zig_target.arch.sub_arch); - cache_int(ch, g->zig_target.vendor); - cache_int(ch, g->zig_target.os); - cache_int(ch, g->zig_target.env_type); - cache_int(ch, g->zig_target.oformat); + cache_bool(ch, g->zig_target->is_native); + cache_int(ch, g->zig_target->arch.arch); + cache_int(ch, g->zig_target->arch.sub_arch); + cache_int(ch, g->zig_target->vendor); + cache_int(ch, g->zig_target->os); + cache_int(ch, g->zig_target->env_type); cache_int(ch, g->subsystem); cache_bool(ch, g->is_static); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); cache_bool(ch, g->is_single_threaded); - cache_bool(ch, g->is_native_target); cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->each_lib_rpath); cache_bool(ch, g->disable_pic); @@ -8654,6 +8665,14 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len); cache_list_of_str(ch, g->clang_argv, g->clang_argv_len); cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); + if (g->libc) { + cache_buf(ch, &g->libc->include_dir); + cache_buf(ch, &g->libc->lib_dir); + cache_buf(ch, &g->libc->static_lib_dir); + cache_buf(ch, &g->libc->msvc_lib_dir); + cache_buf(ch, &g->libc->kernel32_lib_dir); + cache_buf(ch, &g->libc->dynamic_linker_path); + } buf_resize(digest, 0); if ((err = cache_hit(ch, digest))) @@ -8668,19 +8687,19 @@ static void resolve_out_paths(CodeGen *g) { switch (g->emit_file_type) { case EmitFileTypeBinary: { - const char *o_ext = target_o_file_ext(&g->zig_target); + const char *o_ext = target_o_file_ext(g->zig_target); buf_append_str(o_basename, o_ext); break; } case EmitFileTypeAssembly: { - const char *asm_ext = target_asm_file_ext(&g->zig_target); + const char *asm_ext = target_asm_file_ext(g->zig_target); buf_append_str(o_basename, asm_ext); break; } case EmitFileTypeLLVMIr: { - const char *llvm_ir_ext = target_llvm_ir_file_ext(&g->zig_target); + const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target); buf_append_str(o_basename, llvm_ir_ext); break; } @@ -8706,7 +8725,7 @@ static void resolve_out_paths(CodeGen *g) { Buf basename = BUF_INIT; buf_init_from_buf(&basename, g->root_out_name); - buf_append_str(&basename, target_exe_file_ext(&g->zig_target)); + buf_append_str(&basename, target_exe_file_ext(g->zig_target)); if (g->enable_cache || g->is_test_build) { os_path_join(&g->artifact_dir, &basename, &g->output_file_path); } else { @@ -8719,7 +8738,7 @@ static void resolve_out_paths(CodeGen *g) { } else { Buf basename = BUF_INIT; buf_init_from_buf(&basename, g->root_out_name); - buf_append_str(&basename, target_lib_file_ext(&g->zig_target, g->is_static, + buf_append_str(&basename, target_lib_file_ext(g->zig_target, g->is_static, g->version_major, g->version_minor, g->version_patch)); if (g->enable_cache) { os_path_join(&g->artifact_dir, &basename, &g->output_file_path); @@ -8732,11 +8751,12 @@ static void resolve_out_paths(CodeGen *g) { } } - void codegen_build_and_link(CodeGen *g) { Error err; assert(g->out_type != OutTypeUnknown); + detect_libc(g); + Buf *stage1_dir = get_stage1_cache_path(); Buf *artifact_dir = buf_alloc(); Buf digest = BUF_INIT; diff --git a/src/codegen.hpp b/src/codegen.hpp index 4bd8f2dcca..035b759ec5 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -11,11 +11,12 @@ #include "parser.hpp" #include "errmsg.hpp" #include "target.hpp" +#include "libc_installation.hpp" #include CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, - Buf *zig_lib_dir, Buf *override_std_dir); + Buf *zig_lib_dir, Buf *override_std_dir, ZigLibCInstallation *libc); void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); @@ -27,12 +28,6 @@ void codegen_set_is_static(CodeGen *codegen, bool is_static); void codegen_set_strip(CodeGen *codegen, bool strip); void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color); void codegen_set_out_name(CodeGen *codegen, Buf *out_name); -void codegen_set_libc_lib_dir(CodeGen *codegen, Buf *libc_lib_dir); -void codegen_set_libc_static_lib_dir(CodeGen *g, Buf *libc_static_lib_dir); -void codegen_set_libc_include_dir(CodeGen *codegen, Buf *libc_include_dir); -void codegen_set_msvc_lib_dir(CodeGen *g, Buf *msvc_lib_dir); -void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir); -void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); diff --git a/src/error.cpp b/src/error.cpp index 10186fbde5..9e01a86d8c 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -34,6 +34,8 @@ const char *err_str(Error err) { case ErrorPipeBusy: return "pipe busy"; case ErrorPrimitiveTypeNotFound: return "primitive type not found"; case ErrorCacheUnavailable: return "cache unavailable"; + case ErrorPathTooLong: return "path too long"; + case ErrorCCompilerCannotFindFile: return "C compiler cannot find file"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index b60cb8517e..0e14f37a69 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -36,6 +36,8 @@ enum Error { ErrorPipeBusy, ErrorPrimitiveTypeNotFound, ErrorCacheUnavailable, + ErrorPathTooLong, + ErrorCCompilerCannotFindFile, }; const char *err_str(Error err); diff --git a/src/ir.cpp b/src/ir.cpp index a3c15dbd0f..ea2fcb289f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18690,8 +18690,6 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; - find_libc_include_path(ira->codegen); - ImportTableEntry *child_import = allocate(1); child_import->decls_scope = create_decls_scope(ira->codegen, node, nullptr, nullptr, child_import); child_import->c_import_node = node; diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp new file mode 100644 index 0000000000..9228ea95b6 --- /dev/null +++ b/src/libc_installation.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "libc_installation.hpp" +#include "os.hpp" +#include "windows_sdk.h" +#include "target.hpp" + +static const size_t zig_libc_keys_len = 6; + +static const char *zig_libc_keys[] = { + "include_dir", + "lib_dir", + "static_lib_dir", + "msvc_lib_dir", + "kernel32_lib_dir", + "dynamic_linker_path", +}; + +static bool zig_libc_match_key(Slice name, Slice value, bool *found_keys, + size_t index, Buf *field_ptr) +{ + if (!memEql(name, str(zig_libc_keys[index]))) return false; + buf_init_from_mem(field_ptr, (const char*)value.ptr, value.len); + found_keys[index] = true; + return true; +} + +static void zig_libc_init_empty(ZigLibCInstallation *libc) { + *libc = {}; + buf_init_from_str(&libc->include_dir, ""); + buf_init_from_str(&libc->lib_dir, ""); + buf_init_from_str(&libc->static_lib_dir, ""); + buf_init_from_str(&libc->msvc_lib_dir, ""); + buf_init_from_str(&libc->kernel32_lib_dir, ""); + buf_init_from_str(&libc->dynamic_linker_path, ""); +} + +Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget *target, bool verbose) { + Error err; + zig_libc_init_empty(libc); + + bool found_keys[6] = {}; // zig_libc_keys_len + + Buf *contents = buf_alloc(); + if ((err = os_fetch_file_path(libc_file, contents, false))) { + if (err != ErrorFileNotFound && verbose) { + fprintf(stderr, "Unable to read '%s': %s\n", buf_ptr(libc_file), err_str(err)); + } + return err; + } + + SplitIterator it = memSplit(buf_to_slice(contents), str("\n")); + for (;;) { + Optional> opt_line = SplitIterator_next(&it); + if (!opt_line.is_some) + break; + + if (opt_line.value.len == 0 || opt_line.value.ptr[0] == '#') + continue; + + SplitIterator line_it = memSplit(opt_line.value, str("=")); + Slice name; + if (!SplitIterator_next(&line_it).unwrap(&name)) { + if (verbose) { + fprintf(stderr, "missing equal sign after field name\n"); + } + return ErrorSemanticAnalyzeFail; + } + Slice value = SplitIterator_rest(&line_it); + bool match = false; + match = match || zig_libc_match_key(name, value, found_keys, 0, &libc->include_dir); + match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->static_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->msvc_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->kernel32_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->dynamic_linker_path); + } + + for (size_t i = 0; i < zig_libc_keys_len; i += 1) { + if (!found_keys[i]) { + if (verbose) { + fprintf(stderr, "missing field: %s\n", zig_libc_keys[i]); + } + return ErrorSemanticAnalyzeFail; + } + } + + if (buf_len(&libc->include_dir) == 0) { + if (verbose) { + fprintf(stderr, "include_dir may not be empty\n"); + } + return ErrorSemanticAnalyzeFail; + } + + if (buf_len(&libc->lib_dir) == 0) { + if (!target_is_darwin(target)) { + if (verbose) { + fprintf(stderr, "lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + } + return ErrorSemanticAnalyzeFail; + } + } + + if (buf_len(&libc->static_lib_dir) == 0) { + if (!target_is_darwin(target) && target->os != OsWindows) { + if (verbose) { + fprintf(stderr, "static_lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + } + return ErrorSemanticAnalyzeFail; + } + } + + if (buf_len(&libc->msvc_lib_dir) == 0) { + if (target->os == OsWindows) { + if (verbose) { + fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + } + return ErrorSemanticAnalyzeFail; + } + } + + if (buf_len(&libc->kernel32_lib_dir) == 0) { + if (target->os == OsWindows) { + if (verbose) { + fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + } + return ErrorSemanticAnalyzeFail; + } + } + + if (buf_len(&libc->dynamic_linker_path) == 0) { + if (target->os == OsLinux) { + if (verbose) { + fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", get_target_os_name(target->os)); + } + return ErrorSemanticAnalyzeFail; + } + } + + return ErrorNone; +} + +#if defined(ZIG_OS_WINDOWS) +static Error zig_libc_find_native_include_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { + Error err; + if ((err = os_get_win32_ucrt_include_path(sdk, &self->include_dir))) { + if (verbose) { + fprintf(stderr, "Unable to determine libc include path: %s\n", err_str(err)); + } + return err; + } + return ErrorNone; +} +static Error zig_libc_find_lib_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target, + bool verbose) +{ + Error err; + if ((err = os_get_win32_ucrt_lib_path(sdk, &self->lib_dir, target->arch.arch))) { + if (verbose) { + fprintf(stderr, "Unable to determine ucrt path: %s\n", err_str(err)); + } + return err; + } + return ErrorNone; +} +static Error zig_libc_find_kernel32_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target, + bool verbose) +{ + Error err; + if ((err = os_get_win32_kern32_path(sdk, &self->kernel32_lib_dir, target->arch.arch))) { + if (verbose) { + fprintf(stderr, "Unable to determine kernel32 path: %s\n", err_str(err)); + } + return err; + } + return ErrorNone; +} +static Error zig_libc_find_native_msvc_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { + if (sdk->msvc_lib_dir_ptr == nullptr) { + if (verbose) { + fprintf(stderr, "Unable to determine vcruntime path\n"); + } + return ErrorFileNotFound; + } + buf_init_from_mem(&self->msvc_lib_dir, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); + return ErrorNone; +} +#else +static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, bool verbose) { + const char *cc_exe = getenv("CC"); + cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; + ZigList args = {}; + args.append("-E"); + args.append("-Wp,-v"); + args.append("-xc"); + args.append("/dev/null"); + Termination term; + Buf *out_stderr = buf_alloc(); + Buf *out_stdout = buf_alloc(); + Error err; + if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { + if (verbose) { + fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err)); + } + return err; + } + if (term.how != TerminationIdClean || term.code != 0) { + if (verbose) { + fprintf(stderr, "unable to determine libc include path: executing '%s' failed\n", cc_exe); + } + return ErrorCCompileErrors; + } + char *prev_newline = buf_ptr(out_stderr); + ZigList search_paths = {}; + for (;;) { + char *newline = strchr(prev_newline, '\n'); + if (newline == nullptr) { + break; + } + *newline = 0; + if (prev_newline[0] == ' ') { + search_paths.append(prev_newline); + } + prev_newline = newline + 1; + } + if (search_paths.length == 0) { + if (verbose) { + fprintf(stderr, "unable to determine libc include path: '%s' cannot find libc headers\n", cc_exe); + } + return ErrorCCompileErrors; + } + for (size_t i = 0; i < search_paths.length; i += 1) { + // search in reverse order + const char *search_path = search_paths.items[search_paths.length - i - 1]; + // cut off spaces + while (*search_path == ' ') { + search_path += 1; + } + Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path); + bool exists; + if ((err = os_file_exists(stdlib_path, &exists))) { + exists = false; + } + if (exists) { + buf_init_from_str(&self->include_dir, search_path); + return ErrorNone; + } + } + if (verbose) { + fprintf(stderr, "unable to determine libc include path: stdlib.h not found in '%s' search paths\n", cc_exe); + } + return ErrorFileNotFound; +} +#if !defined(ZIG_OS_DARWIN) +static Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose) { + const char *cc_exe = getenv("CC"); + cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; + ZigList args = {}; + args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file))); + Termination term; + Buf *out_stderr = buf_alloc(); + Buf *out_stdout = buf_alloc(); + Error err; + if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { + if (verbose) { + fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err)); + } + return err; + } + if (term.how != TerminationIdClean || term.code != 0) { + if (verbose) { + fprintf(stderr, "unable to determine libc include path: executing '%s' failed\n", cc_exe); + } + return ErrorCCompileErrors; + } + if (buf_ends_with_str(out_stdout, "\n")) { + buf_resize(out_stdout, buf_len(out_stdout) - 1); + } + if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, o_file)) { + return ErrorCCompilerCannotFindFile; + } + if (want_dirname) { + os_path_dirname(out_stdout, out); + } else { + buf_init_from_buf(out, out_stdout); + } + return ErrorNone; +} +static Error zig_libc_find_native_lib_dir_posix(ZigLibCInstallation *self, bool verbose) { + return zig_libc_cc_print_file_name("crt1.o", &self->lib_dir, true, verbose); +} + +static Error zig_libc_find_native_static_lib_dir_posix(ZigLibCInstallation *self, bool verbose) { + return zig_libc_cc_print_file_name("crtbegin.o", &self->static_lib_dir, true, verbose); +} +#endif + +static Error zig_libc_find_native_dynamic_linker_posix(ZigLibCInstallation *self, bool verbose) { +#if defined(ZIG_OS_LINUX) + Error err; + static const char *dyn_tests[] = { + "ld-linux-x86-64.so.2", + "ld-musl-x86_64.so.1", + }; + for (size_t i = 0; i < array_length(dyn_tests); i += 1) { + const char *lib_name = dyn_tests[i]; + if ((err = zig_libc_cc_print_file_name(lib_name, &self->dynamic_linker_path, false, true))) { + if (err != ErrorCCompilerCannotFindFile) + return err; + continue; + } + return ErrorNone; + } +#endif + ZigTarget native_target; + get_native_target(&native_target); + Buf *dynamic_linker_path = target_dynamic_linker(&native_target); + buf_init_from_buf(&self->dynamic_linker_path, dynamic_linker_path); + return ErrorNone; +} +#endif + +void zig_libc_render(ZigLibCInstallation *self, FILE *file) { + fprintf(file, + "# The directory that contains `stdlib.h`.\n" + "# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`\n" + "include_dir=%s\n" + "\n" + "# The directory that contains `crt1.o`.\n" + "# On Linux, can be found with `cc -print-file-name=crt1.o`.\n" + "# Not needed when targeting MacOS.\n" + "lib_dir=%s\n" + "\n" + "# The directory that contains `crtbegin.o`.\n" + "# On Linux, can be found with `cc -print-file-name=crtbegin.o`.\n" + "# Not needed when targeting MacOS or Windows.\n" + "static_lib_dir=%s\n" + "\n" + "# The directory that contains `vcruntime.lib`.\n" + "# Only needed when targeting Windows.\n" + "msvc_lib_dir=%s\n" + "\n" + "# The directory that contains `kernel32.lib`.\n" + "# Only needed when targeting Windows.\n" + "kernel32_lib_dir=%s\n" + "\n" + "# The full path to the dynamic linker, on the target system.\n" + "# Only needed when targeting Linux.\n" + "dynamic_linker_path=%s\n" + "\n" + , + buf_ptr(&self->include_dir), + buf_ptr(&self->lib_dir), + buf_ptr(&self->static_lib_dir), + buf_ptr(&self->msvc_lib_dir), + buf_ptr(&self->kernel32_lib_dir), + buf_ptr(&self->dynamic_linker_path) + ); +} + +Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) { + Error err; + zig_libc_init_empty(self); +#if defined(ZIG_OS_WINDOWS) + ZigTarget native_target; + get_native_target(&native_target); + ZigWindowsSDK *sdk; + switch (zig_find_windows_sdk(&sdk)) { + case ZigFindWindowsSdkErrorNone: + if ((err = zig_libc_find_native_msvc_lib_dir(self, sdk, verbose))) + return err; + if ((err = zig_libc_find_kernel32_lib_dir(self, sdk, &native_target, verbose))) + return err; + if ((err = zig_libc_find_native_include_dir_windows(self, sdk, verbose))) + return err; + if ((err = zig_libc_find_lib_dir_windows(self, sdk, &native_target, verbose))) + return err; + return ErrorNone; + case ZigFindWindowsSdkErrorOutOfMemory: + return ErrorNoMem; + case ZigFindWindowsSdkErrorNotFound: + return ErrorFileNotFound; + case ZigFindWindowsSdkErrorPathTooLong: + return ErrorPathTooLong; + } + zig_unreachable(); +#else + if ((err = zig_libc_find_native_include_dir_posix(self, verbose))) + return err; +#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) + buf_init_from_str(&self->lib_dir, "/usr/lib"); + buf_init_from_str(&self->static_lib_dir, "/usr/lib"); +#elif !defined(ZIG_OS_DARWIN) + if ((err = zig_libc_find_native_lib_dir_posix(self, verbose))) + return err; + if ((err = zig_libc_find_native_static_lib_dir_posix(self, verbose))) + return err; +#endif + if ((err = zig_libc_find_native_dynamic_linker_posix(self, verbose))) + return err; + return ErrorNone; +#endif +} diff --git a/src/libc_installation.hpp b/src/libc_installation.hpp new file mode 100644 index 0000000000..4cb77f1451 --- /dev/null +++ b/src/libc_installation.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_LIBC_INSTALLATION_HPP +#define ZIG_LIBC_INSTALLATION_HPP + +#include + +#include "buffer.hpp" +#include "error.hpp" +#include "target.hpp" + +// Must be synchronized with zig_libc_keys +struct ZigLibCInstallation { + Buf include_dir; + Buf lib_dir; + Buf static_lib_dir; + Buf msvc_lib_dir; + Buf kernel32_lib_dir; + Buf dynamic_linker_path; +}; + +Error ATTRIBUTE_MUST_USE zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, + const ZigTarget *target, bool verbose); +void zig_libc_render(ZigLibCInstallation *self, FILE *file); + +Error ATTRIBUTE_MUST_USE zig_libc_find_native(ZigLibCInstallation *self, bool verbose); + +#endif diff --git a/src/link.cpp b/src/link.cpp index 2878a00358..2068b8efb0 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -18,31 +18,32 @@ struct LinkJob { }; static const char *get_libc_file(CodeGen *g, const char *file) { + assert(g->libc != nullptr); Buf *out_buf = buf_alloc(); - os_path_join(g->libc_lib_dir, buf_create_from_str(file), out_buf); + os_path_join(&g->libc->lib_dir, buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } static const char *get_libc_static_file(CodeGen *g, const char *file) { + assert(g->libc != nullptr); Buf *out_buf = buf_alloc(); - os_path_join(g->libc_static_lib_dir, buf_create_from_str(file), out_buf); + os_path_join(&g->libc->static_lib_dir, buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) { - ZigTarget *child_target = parent_gen->is_native_target ? nullptr : &parent_gen->zig_target; - // The Mach-O LLD code is not well maintained, and trips an assertion // when we link compiler_rt and builtin as libraries rather than objects. // Here we workaround this by having compiler_rt and builtin be objects. // TODO write our own linker. https://github.com/ziglang/zig/issues/1535 OutType child_out_type = OutTypeLib; - if (parent_gen->zig_target.os == OsMacOSX) { + if (parent_gen->zig_target->os == OsMacOSX) { child_out_type = OutTypeObj; } - CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir); + CodeGen *child_gen = codegen_create(full_path, parent_gen->zig_target, child_out_type, + parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, + parent_gen->libc); child_gen->out_h_path = nullptr; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; @@ -171,62 +172,11 @@ static void add_rpath(LinkJob *lj, Buf *rpath) { lj->rpath_table.put(rpath, true); } -static Buf *try_dynamic_linker_path(const char *ld_name) { - const char *cc_exe = getenv("CC"); - cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; - ZigList args = {}; - args.append(buf_ptr(buf_sprintf("-print-file-name=%s", ld_name))); - Termination term; - Buf *out_stderr = buf_alloc(); - Buf *out_stdout = buf_alloc(); - int err; - if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { - return nullptr; - } - if (term.how != TerminationIdClean || term.code != 0) { - return nullptr; - } - if (buf_ends_with_str(out_stdout, "\n")) { - buf_resize(out_stdout, buf_len(out_stdout) - 1); - } - if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, ld_name)) { - return nullptr; - } - return out_stdout; -} - -static Buf *get_dynamic_linker_path(CodeGen *g) { - if (g->zig_target.os == OsFreeBSD) { - return buf_create_from_str("/libexec/ld-elf.so.1"); - } - if (g->zig_target.os == OsNetBSD) { - return buf_create_from_str("/libexec/ld.elf_so"); - } - if (g->is_native_target && g->zig_target.arch.arch == ZigLLVM_x86_64) { - static const char *ld_names[] = { - "ld-linux-x86-64.so.2", - "ld-musl-x86_64.so.1", - }; - for (size_t i = 0; i < array_length(ld_names); i += 1) { - const char *ld_name = ld_names[i]; - Buf *result = try_dynamic_linker_path(ld_name); - if (result != nullptr) { - return result; - } - } - } - return target_dynamic_linker(&g->zig_target); -} - static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; lj->args.append("-error-limit=0"); - if (g->libc_link_lib != nullptr) { - find_libc_lib_path(g); - } - if (g->linker_script) { lj->args.append("-T"); lj->args.append(g->linker_script); @@ -235,14 +185,14 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append("--gc-sections"); lj->args.append("-m"); - lj->args.append(getLDMOption(&g->zig_target)); + lj->args.append(getLDMOption(g->zig_target)); bool is_lib = g->out_type == OutTypeLib; bool shared = !g->is_static && is_lib; Buf *soname = nullptr; if (g->is_static) { - if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb || - g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb) + if (g->zig_target->arch.arch == ZigLLVM_arm || g->zig_target->arch.arch == ZigLLVM_armeb || + g->zig_target->arch.arch == ZigLLVM_thumb || g->zig_target->arch.arch == ZigLLVM_thumbeb) { lj->args.append("-Bstatic"); } else { @@ -264,13 +214,13 @@ static void construct_linker_job_elf(LinkJob *lj) { if (lj->link_in_crt) { const char *crt1o; const char *crtbegino; - if (g->zig_target.os == OsNetBSD) { - crt1o = "crt0.o"; - crtbegino = "crtbegin.o"; - } else if (g->is_static) { + if (g->zig_target->os == OsNetBSD) { + crt1o = "crt0.o"; + crtbegino = "crtbegin.o"; + } else if (g->is_static) { crt1o = "crt1.o"; crtbegino = "crtbeginT.o"; - } else { + } else { crt1o = "Scrt1.o"; crtbegino = "crtbegin.o"; } @@ -311,23 +261,19 @@ static void construct_linker_job_elf(LinkJob *lj) { } if (g->libc_link_lib != nullptr) { + assert(g->libc != nullptr); lj->args.append("-L"); - lj->args.append(buf_ptr(g->libc_lib_dir)); + lj->args.append(buf_ptr(&g->libc->lib_dir)); lj->args.append("-L"); - lj->args.append(buf_ptr(g->libc_static_lib_dir)); - } + lj->args.append(buf_ptr(&g->libc->static_lib_dir)); - if (!g->is_static) { - if (g->dynamic_linker != nullptr) { - assert(buf_len(g->dynamic_linker) != 0); + if (!g->is_static) { + assert(buf_len(&g->libc->dynamic_linker_path) != 0); lj->args.append("-dynamic-linker"); - lj->args.append(buf_ptr(g->dynamic_linker)); - } else { - Buf *resolved_dynamic_linker = get_dynamic_linker_path(g); - lj->args.append("-dynamic-linker"); - lj->args.append(buf_ptr(resolved_dynamic_linker)); + lj->args.append(buf_ptr(&g->libc->dynamic_linker_path)); } + } if (shared) { @@ -397,11 +343,11 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(get_libc_file(g, "crtn.o")); } - if (!g->is_native_target) { + if (!g->zig_target->is_native) { lj->args.append("--allow-shlib-undefined"); } - if (g->zig_target.os == OsZen) { + if (g->zig_target->os == OsZen) { lj->args.append("-e"); lj->args.append("_start"); @@ -429,11 +375,11 @@ static void construct_linker_job_wasm(LinkJob *lj) { //} static void coff_append_machine_arg(CodeGen *g, ZigList *list) { - if (g->zig_target.arch.arch == ZigLLVM_x86) { + if (g->zig_target->arch.arch == ZigLLVM_x86) { list->append("-MACHINE:X86"); - } else if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + } else if (g->zig_target->arch.arch == ZigLLVM_x86_64) { list->append("-MACHINE:X64"); - } else if (g->zig_target.arch.arch == ZigLLVM_arm) { + } else if (g->zig_target->arch.arch == ZigLLVM_arm) { list->append("-MACHINE:ARM"); } } @@ -592,10 +538,6 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append("/ERRORLIMIT:0"); - if (g->libc_link_lib != nullptr) { - find_libc_lib_path(g); - } - lj->args.append("/NOLOGO"); if (!g->strip_debug_symbols) { @@ -650,13 +592,11 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path)))); if (g->libc_link_lib != nullptr) { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->msvc_lib_dir)))); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->kernel32_lib_dir)))); + assert(g->libc != nullptr); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir)))); - if (g->libc_static_lib_dir != nullptr) { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir)))); - } + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->msvc_lib_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->kernel32_lib_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->lib_dir)))); } if (is_library && !g->is_static) { @@ -691,7 +631,7 @@ static void construct_linker_job_coff(LinkJob *lj) { continue; } if (link_lib->provided_explicitly) { - if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) { + if (lj->codegen->zig_target->env_type == ZigLLVM_GNU) { Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); lj->args.append(buf_ptr(arg)); } @@ -721,7 +661,8 @@ static void construct_linker_job_coff(LinkJob *lj) { gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path)))); gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path)))); Buf diag = BUF_INIT; - if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) { + ZigLLVM_ObjectFormatType target_ofmt = target_object_format(g->zig_target); + if (!zig_lld_link(target_ofmt, gen_lib_args.items, gen_lib_args.length, &diag)) { fprintf(stderr, "%s\n", buf_ptr(&diag)); exit(1); } @@ -790,7 +731,7 @@ static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { platform->kind = MacOS; } else if (g->mios_version_min) { platform->kind = IPhoneOS; - } else if (g->zig_target.os == OsMacOSX) { + } else if (g->zig_target->os == OsMacOSX) { platform->kind = MacOS; g->mmacosx_version_min = buf_create_from_str("10.10"); } else { @@ -817,8 +758,8 @@ static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { } if (platform->kind == IPhoneOS && - (g->zig_target.arch.arch == ZigLLVM_x86 || - g->zig_target.arch.arch == ZigLLVM_x86_64)) + (g->zig_target->arch.arch == ZigLLVM_x86 || + g->zig_target->arch.arch == ZigLLVM_x86_64)) { platform->kind = IPhoneOSSimulator; } @@ -882,7 +823,7 @@ static void construct_linker_job_macho(LinkJob *lj) { } lj->args.append("-arch"); - lj->args.append(get_darwin_arch_string(&g->zig_target)); + lj->args.append(get_darwin_arch_string(g->zig_target)); DarwinPlatform platform; get_darwin_platform(lj, &platform); @@ -939,7 +880,7 @@ static void construct_linker_job_macho(LinkJob *lj) { } break; case IPhoneOS: - if (g->zig_target.arch.arch == ZigLLVM_aarch64) { + if (g->zig_target->arch.arch == ZigLLVM_aarch64) { // iOS does not need any crt1 files for arm64 } else if (darwin_version_lt(&platform, 3, 1)) { lj->args.append("-lcrt1.o"); @@ -969,7 +910,7 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append(buf_ptr(compiler_rt_o_path)); } - if (g->is_native_target) { + if (g->zig_target->is_native) { for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { LinkLib *link_lib = g->link_libs_list.at(lib_i); if (buf_eql_str(link_lib->name, "c")) { @@ -1010,7 +951,7 @@ static void construct_linker_job_macho(LinkJob *lj) { } static void construct_linker_job(LinkJob *lj) { - switch (lj->codegen->zig_target.oformat) { + switch (target_object_format(lj->codegen->zig_target)) { case ZigLLVM_UnknownObjectFormat: zig_unreachable(); @@ -1050,7 +991,7 @@ void codegen_link(CodeGen *g) { for (size_t i = 0; i < g->link_objects.length; i += 1) { file_names.append((const char *)buf_ptr(g->link_objects.at(i))); } - ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target.os); + ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target->os); codegen_add_time_event(g, "LLVM Link"); if (ZigLLVMWriteArchive(buf_ptr(&g->output_file_path), file_names.items, file_names.length, os_type)) { fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->output_file_path)); @@ -1075,7 +1016,7 @@ void codegen_link(CodeGen *g) { Buf diag = BUF_INIT; codegen_add_time_event(g, "LLVM Link"); - if (g->system_linker_hack && g->zig_target.os == OsMacOSX) { + if (g->system_linker_hack && g->zig_target->os == OsMacOSX) { Termination term; ZigList args = {}; for (size_t i = 1; i < lj.args.length; i += 1) { @@ -1085,7 +1026,7 @@ void codegen_link(CodeGen *g) { if (term.how != TerminationIdClean || term.code != 0) { exit(1); } - } else if (!zig_lld_link(g->zig_target.oformat, lj.args.items, lj.args.length, &diag)) { + } else if (!zig_lld_link(target_object_format(g->zig_target), lj.args.items, lj.args.length, &diag)) { fprintf(stderr, "%s\n", buf_ptr(&diag)); exit(1); } diff --git a/src/main.cpp b/src/main.cpp index dd35cee5a5..681cd6e8ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "error.hpp" #include "os.hpp" #include "target.hpp" +#include "libc_installation.hpp" #include @@ -36,6 +37,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " id print the base64-encoded compiler id\n" " init-exe initialize a `zig build` application in the cwd\n" " init-lib initialize a `zig build` library in the cwd\n" + " libc [paths_file] Display native libc paths file or validate one\n" " run [source] create executable and run immediately\n" " translate-c [source] convert c code to zig code\n" " targets list available compilation targets\n" @@ -53,7 +55,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --enable-valgrind include valgrind client requests release builds\n" " --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n" " -ftime-report print timing diagnostics\n" - " --libc-include-dir [path] directory where libc stdlib.h resides\n" + " --libc [file] Provide a file which specifies libc paths\n" " --name [name] override output name\n" " --output [file] override destination path\n" " --output-h [file] generate header file\n" @@ -82,10 +84,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { "Link Options:\n" " --dynamic-linker [path] set the path to ld.so\n" " --each-lib-rpath add rpath for each used dynamic library\n" - " --libc-lib-dir [path] directory where libc crt1.o resides\n" - " --libc-static-lib-dir [path] directory where libc crtbegin.o resides\n" - " --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n" - " --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n" " --library [lib] link against lib\n" " --forbid-library [lib] make it an error to link against lib\n" " --library-path [dir] add a directory to the library search path\n" @@ -111,6 +109,26 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { return return_code; } +static int print_libc_usage(const char *arg0, FILE *file, int return_code) { + fprintf(file, + "Usage: %s libc\n" + "\n" + "Detect the native libc installation and print the resulting paths to stdout.\n" + "You can save this into a file and then edit the paths to create a cross\n" + "compilation libc kit. Then you can pass `--libc [file]` for Zig to use it.\n" + "\n" + "When compiling natively and no `--libc` argument provided, Zig automatically\n" + "creates zig-cache/native_libc.txt so that it does not have to detect libc\n" + "on every invocation. You can remove this file to have Zig re-detect the\n" + "native libc.\n" + "\n\n" + "Usage: %s libc [file]\n" + "\n" + "Parse a libc installation text file and validate it.\n" + , arg0, arg0); + return return_code; +} + static const char *ZIG_ZEN = "\n" " * Communicate intent precisely.\n" " * Edge cases matter.\n" @@ -169,6 +187,7 @@ enum Cmd { CmdTranslateC, CmdVersion, CmdZen, + CmdLibC, }; static const char *default_zig_cache_name = "zig-cache"; @@ -359,12 +378,7 @@ int main(int argc, char **argv) { bool verbose_cimport = false; ErrColor color = ErrColorAuto; CacheOpt enable_cache = CacheOptAuto; - const char *libc_lib_dir = nullptr; - const char *libc_static_lib_dir = nullptr; - const char *libc_include_dir = nullptr; - const char *msvc_lib_dir = nullptr; - const char *kernel32_lib_dir = nullptr; - const char *dynamic_linker = nullptr; + const char *libc_txt = nullptr; ZigList clang_argv = {0}; ZigList llvm_argv = {0}; ZigList lib_dirs = {0}; @@ -434,8 +448,10 @@ int main(int argc, char **argv) { Buf *build_runner_path = buf_alloc(); os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path); - CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), - override_std_dir); + ZigTarget target; + get_native_target(&target); + CodeGen *g = codegen_create(build_runner_path, &target, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + override_std_dir, nullptr); g->valgrind_support = valgrind_support; g->enable_time_report = timing_info; buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); @@ -520,10 +536,12 @@ int main(int argc, char **argv) { return (term.how == TerminationIdClean) ? term.code : -1; } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { init_all_targets(); + ZigTarget target; + get_native_target(&target); Buf *fmt_runner_path = buf_alloc(); os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); - CodeGen *g = codegen_create(fmt_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), - nullptr); + CodeGen *g = codegen_create(fmt_runner_path, &target, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), + nullptr, nullptr); g->valgrind_support = valgrind_support; g->is_single_threaded = true; codegen_set_out_name(g, buf_create_from_str("fmt")); @@ -557,7 +575,11 @@ int main(int argc, char **argv) { } else if (strcmp(arg, "--release-small") == 0) { build_mode = BuildModeSmallRelease; } else if (strcmp(arg, "--help") == 0) { - return print_full_usage(arg0, stderr, EXIT_FAILURE); + if (cmd == CmdLibC) { + return print_libc_usage(arg0, stderr, EXIT_FAILURE); + } else { + return print_full_usage(arg0, stderr, EXIT_FAILURE); + } } else if (strcmp(arg, "--strip") == 0) { strip = true; } else if (strcmp(arg, "--static") == 0) { @@ -658,18 +680,8 @@ int main(int argc, char **argv) { } } else if (strcmp(arg, "--name") == 0) { out_name = argv[i]; - } else if (strcmp(arg, "--libc-lib-dir") == 0) { - libc_lib_dir = argv[i]; - } else if (strcmp(arg, "--libc-static-lib-dir") == 0) { - libc_static_lib_dir = argv[i]; - } else if (strcmp(arg, "--libc-include-dir") == 0) { - libc_include_dir = argv[i]; - } else if (strcmp(arg, "--msvc-lib-dir") == 0) { - msvc_lib_dir = argv[i]; - } else if (strcmp(arg, "--kernel32-lib-dir") == 0) { - kernel32_lib_dir = argv[i]; - } else if (strcmp(arg, "--dynamic-linker") == 0) { - dynamic_linker = argv[i]; + } else if (strcmp(arg, "--libc") == 0) { + libc_txt = argv[i]; } else if (strcmp(arg, "-isystem") == 0) { clang_argv.append("-isystem"); clang_argv.append(argv[i]); @@ -778,6 +790,8 @@ int main(int argc, char **argv) { cmd = CmdVersion; } else if (strcmp(arg, "zen") == 0) { cmd = CmdZen; + } else if (strcmp(arg, "libc") == 0) { + cmd = CmdLibC; } else if (strcmp(arg, "translate-c") == 0) { cmd = CmdTranslateC; } else if (strcmp(arg, "test") == 0) { @@ -797,6 +811,7 @@ int main(int argc, char **argv) { case CmdRun: case CmdTranslateC: case CmdTest: + case CmdLibC: if (!in_file) { in_file = arg; if (cmd == CmdRun) { @@ -828,27 +843,25 @@ int main(int argc, char **argv) { init_all_targets(); - ZigTarget alloc_target; - ZigTarget *target; + ZigTarget target; if (!target_arch && !target_os && !target_environ) { - target = nullptr; + get_native_target(&target); } else { - target = &alloc_target; - get_unknown_target(target); + get_unknown_target(&target); if (target_arch) { - if (parse_target_arch(target_arch, &target->arch)) { + if (parse_target_arch(target_arch, &target.arch)) { fprintf(stderr, "invalid --target-arch argument\n"); return print_error_usage(arg0); } } if (target_os) { - if (parse_target_os(target_os, &target->os)) { + if (parse_target_os(target_os, &target.os)) { fprintf(stderr, "invalid --target-os argument\n"); return print_error_usage(arg0); } } if (target_environ) { - if (parse_target_environ(target_environ, &target->env_type)) { + if (parse_target_environ(target_environ, &target.env_type)) { fprintf(stderr, "invalid --target-environ argument\n"); return print_error_usage(arg0); } @@ -856,8 +869,22 @@ int main(int argc, char **argv) { } switch (cmd) { + case CmdLibC: { + if (in_file) { + ZigLibCInstallation libc; + if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true))) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + ZigLibCInstallation libc; + if ((err = zig_libc_find_native(&libc, true))) + return EXIT_FAILURE; + zig_libc_render(&libc, stdout); + return EXIT_SUCCESS; + } case CmdBuiltin: { - CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir); + CodeGen *g = codegen_create(nullptr, &target, out_type, build_mode, get_zig_lib_dir(), override_std_dir, + nullptr); g->valgrind_support = valgrind_support; g->is_single_threaded = is_single_threaded; Buf *builtin_source = codegen_generate_builtin_source(g); @@ -917,8 +944,16 @@ int main(int argc, char **argv) { if (cmd == CmdRun && buf_out_name == nullptr) { buf_out_name = buf_create_from_str("run"); } - CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(), - override_std_dir); + ZigLibCInstallation *libc = nullptr; + if (libc_txt != nullptr) { + libc = allocate(1); + if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) { + fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); + return EXIT_FAILURE; + } + } + CodeGen *g = codegen_create(zig_root_source_file, &target, out_type, build_mode, get_zig_lib_dir(), + override_std_dir, libc); g->valgrind_support = valgrind_support; g->subsystem = subsystem; @@ -944,18 +979,6 @@ int main(int argc, char **argv) { codegen_set_llvm_argv(g, llvm_argv.items, llvm_argv.length); codegen_set_strip(g, strip); codegen_set_is_static(g, is_static); - if (libc_lib_dir) - codegen_set_libc_lib_dir(g, buf_create_from_str(libc_lib_dir)); - if (libc_static_lib_dir) - codegen_set_libc_static_lib_dir(g, buf_create_from_str(libc_static_lib_dir)); - if (libc_include_dir) - codegen_set_libc_include_dir(g, buf_create_from_str(libc_include_dir)); - if (msvc_lib_dir) - codegen_set_msvc_lib_dir(g, buf_create_from_str(msvc_lib_dir)); - if (kernel32_lib_dir) - codegen_set_kernel32_lib_dir(g, buf_create_from_str(kernel32_lib_dir)); - if (dynamic_linker) - codegen_set_dynamic_linker(g, buf_create_from_str(dynamic_linker)); g->verbose_tokenize = verbose_tokenize; g->verbose_ast = verbose_ast; g->verbose_link = verbose_link; @@ -1086,7 +1109,7 @@ int main(int argc, char **argv) { } } - if (!target_can_exec(&native, target)) { + if (!target_can_exec(&native, &target)) { fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", buf_ptr(test_exe_path)); return 0; diff --git a/src/os.cpp b/src/os.cpp index c11840b1cf..95febca9bc 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1550,7 +1550,7 @@ void os_stderr_set_color(TermColor color) { #endif } -int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { +Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); @@ -1571,7 +1571,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch buf_init_from_buf(tmp_buf, output_buf); buf_append_str(tmp_buf, "ucrt.lib"); if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; + return ErrorNone; } else { buf_resize(output_buf, 0); @@ -1582,12 +1582,12 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch #endif } -int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { +Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; + return ErrorNone; } else { buf_resize(output_buf, 0); @@ -1598,7 +1598,7 @@ int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { #endif } -int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { +Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) { buf_resize(output_buf, 0); @@ -1620,7 +1620,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy buf_init_from_buf(tmp_buf, output_buf); buf_append_str(tmp_buf, "kernel32.lib"); if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; + return ErrorNone; } } { @@ -1643,7 +1643,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy buf_init_from_buf(tmp_buf, output_buf); buf_append_str(tmp_buf, "kernel32.lib"); if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return 0; + return ErrorNone; } } return ErrorFileNotFound; diff --git a/src/os.hpp b/src/os.hpp index dab7b84896..db357c22a2 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -135,9 +135,9 @@ Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); -int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); -int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); -int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); +Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); +Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); +Error ATTRIBUTE_MUST_USE os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList &paths); diff --git a/src/target.cpp b/src/target.cpp index 3e733da0e7..01e8b90cea 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -214,7 +214,7 @@ size_t target_oformat_count(void) { return array_length(oformat_list); } -const ZigLLVM_ObjectFormatType get_target_oformat(size_t index) { +ZigLLVM_ObjectFormatType get_target_oformat(size_t index) { return oformat_list[index]; } @@ -443,14 +443,16 @@ ZigLLVM_EnvironmentType get_target_environ(size_t index) { void get_native_target(ZigTarget *target) { ZigLLVM_OSType os_type; + ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os ZigLLVMGetNativeTarget( &target->arch.arch, &target->arch.sub_arch, &target->vendor, &os_type, &target->env_type, - &target->oformat); + &oformat); target->os = get_zig_os_type(os_type); + target->is_native = true; } void get_unknown_target(ZigTarget *target) { @@ -459,7 +461,7 @@ void get_unknown_target(ZigTarget *target) { target->vendor = ZigLLVM_UnknownVendor; target->os = OsFreestanding; target->env_type = ZigLLVM_UnknownEnvironment; - target->oformat = ZigLLVM_UnknownObjectFormat; + target->is_native = false; } static void get_arch_name_raw(char *out_str, ZigLLVM_ArchType arch, ZigLLVM_SubArchType sub_arch) { @@ -554,85 +556,18 @@ bool target_is_darwin(const ZigTarget *target) { } } -void resolve_target_object_format(ZigTarget *target) { - if (target->oformat != ZigLLVM_UnknownObjectFormat) { - return; +ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target) { + if (target->os == OsUefi || target->os == OsWindows) { + return ZigLLVM_COFF; + } else if (target_is_darwin(target)) { + return ZigLLVM_MachO; } - - switch (target->arch.arch) { - case ZigLLVM_UnknownArch: - case ZigLLVM_aarch64: - case ZigLLVM_arm: - case ZigLLVM_thumb: - case ZigLLVM_x86: - case ZigLLVM_x86_64: - if (target_is_darwin(target)) { - target->oformat = ZigLLVM_MachO; - } else if (target->os == OsWindows) { - target->oformat = ZigLLVM_COFF; - } else { - target->oformat = ZigLLVM_ELF; - } - return; - - case ZigLLVM_aarch64_be: - case ZigLLVM_amdgcn: - case ZigLLVM_amdil: - case ZigLLVM_amdil64: - case ZigLLVM_armeb: - case ZigLLVM_arc: - case ZigLLVM_avr: - case ZigLLVM_bpfeb: - case ZigLLVM_bpfel: - case ZigLLVM_hexagon: - case ZigLLVM_lanai: - case ZigLLVM_hsail: - case ZigLLVM_hsail64: - case ZigLLVM_kalimba: - case ZigLLVM_le32: - case ZigLLVM_le64: - case ZigLLVM_mips: - case ZigLLVM_mips64: - case ZigLLVM_mips64el: - case ZigLLVM_mipsel: - case ZigLLVM_msp430: - case ZigLLVM_nios2: - case ZigLLVM_nvptx: - case ZigLLVM_nvptx64: - case ZigLLVM_ppc64le: - case ZigLLVM_r600: - case ZigLLVM_renderscript32: - case ZigLLVM_renderscript64: - case ZigLLVM_riscv32: - case ZigLLVM_riscv64: - case ZigLLVM_shave: - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - case ZigLLVM_sparcv9: - case ZigLLVM_spir: - case ZigLLVM_spir64: - case ZigLLVM_systemz: - case ZigLLVM_tce: - case ZigLLVM_tcele: - case ZigLLVM_thumbeb: - case ZigLLVM_xcore: - target->oformat= ZigLLVM_ELF; - return; - - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: - target->oformat = ZigLLVM_Wasm; - return; - - case ZigLLVM_ppc: - case ZigLLVM_ppc64: - if (target_is_darwin(target)) { - target->oformat = ZigLLVM_MachO; - } else { - target->oformat= ZigLLVM_ELF; - } - return; + if (target->arch.arch == ZigLLVM_wasm32 || + target->arch.arch == ZigLLVM_wasm64) + { + return ZigLLVM_Wasm; } + return ZigLLVM_ELF; } // See lib/Support/Triple.cpp in LLVM for the source of this data. @@ -812,7 +747,7 @@ bool target_allows_addr_zero(const ZigTarget *target) { return target->os == OsFreestanding; } -const char *target_o_file_ext(ZigTarget *target) { +const char *target_o_file_ext(const ZigTarget *target) { if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) { return ".obj"; } else { @@ -820,15 +755,15 @@ const char *target_o_file_ext(ZigTarget *target) { } } -const char *target_asm_file_ext(ZigTarget *target) { +const char *target_asm_file_ext(const ZigTarget *target) { return ".s"; } -const char *target_llvm_ir_file_ext(ZigTarget *target) { +const char *target_llvm_ir_file_ext(const ZigTarget *target) { return ".ll"; } -const char *target_exe_file_ext(ZigTarget *target) { +const char *target_exe_file_ext(const ZigTarget *target) { if (target->os == OsWindows) { return ".exe"; } else if (target->os == OsUefi) { @@ -838,7 +773,9 @@ const char *target_exe_file_ext(ZigTarget *target) { } } -const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch) { +const char *target_lib_file_ext(const ZigTarget *target, bool is_static, + size_t version_major, size_t version_minor, size_t version_patch) +{ if (target->os == OsWindows || target->os == OsUefi) { if (is_static) { return ".lib"; @@ -860,7 +797,7 @@ enum FloatAbi { FloatAbiSoftFp, }; -static FloatAbi get_float_abi(ZigTarget *target) { +static FloatAbi get_float_abi(const ZigTarget *target) { const ZigLLVM_EnvironmentType env = target->env_type; if (env == ZigLLVM_GNUEABIHF || env == ZigLLVM_EABIHF || @@ -876,7 +813,14 @@ static bool is_64_bit(ZigLLVM_ArchType arch) { return get_arch_pointer_bit_width(arch) == 64; } -Buf *target_dynamic_linker(ZigTarget *target) { +Buf *target_dynamic_linker(const ZigTarget *target) { + if (target->os == OsFreeBSD) { + return buf_create_from_str("/libexec/ld-elf.so.1"); + } + if (target->os == OsNetBSD) { + return buf_create_from_str("/libexec/ld.elf_so"); + } + const ZigLLVM_ArchType arch = target->arch.arch; const ZigLLVM_EnvironmentType env = target->env_type; @@ -1098,3 +1042,10 @@ bool target_has_valgrind_support(const ZigTarget *target) { } zig_unreachable(); } + +bool target_requires_libc(const ZigTarget *target) { + // On Darwin, we always link libSystem which contains libc. + // Similarly on FreeBSD and NetBSD we always link system libc + // since this is the stable syscall interface. + return (target_is_darwin(target) || target->os == OsFreeBSD || target->os == OsNetBSD); +} diff --git a/src/target.hpp b/src/target.hpp index 292ea8f94f..620c9b2664 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -71,7 +71,7 @@ struct ZigTarget { ZigLLVM_VendorType vendor; Os os; ZigLLVM_EnvironmentType env_type; - ZigLLVM_ObjectFormatType oformat; + bool is_native; }; enum CIntType { @@ -105,8 +105,9 @@ ZigLLVM_EnvironmentType get_target_environ(size_t index); size_t target_oformat_count(void); -const ZigLLVM_ObjectFormatType get_target_oformat(size_t index); +ZigLLVM_ObjectFormatType get_target_oformat(size_t index); const char *get_target_oformat_name(ZigLLVM_ObjectFormatType oformat); +ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target); void get_native_target(ZigTarget *target); void get_unknown_target(ZigTarget *target); @@ -123,13 +124,14 @@ void resolve_target_object_format(ZigTarget *target); uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id); -const char *target_o_file_ext(ZigTarget *target); -const char *target_asm_file_ext(ZigTarget *target); -const char *target_llvm_ir_file_ext(ZigTarget *target); -const char *target_exe_file_ext(ZigTarget *target); -const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch); +const char *target_o_file_ext(const ZigTarget *target); +const char *target_asm_file_ext(const ZigTarget *target); +const char *target_llvm_ir_file_ext(const ZigTarget *target); +const char *target_exe_file_ext(const ZigTarget *target); +const char *target_lib_file_ext(const ZigTarget *target, bool is_static, + size_t version_major, size_t version_minor, size_t version_patch); -Buf *target_dynamic_linker(ZigTarget *target); +Buf *target_dynamic_linker(const ZigTarget *target); bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); ZigLLVM_OSType get_llvm_os_type(Os os_type); @@ -138,5 +140,6 @@ bool target_is_arm(const ZigTarget *target); bool target_allows_addr_zero(const ZigTarget *target); bool target_has_valgrind_support(const ZigTarget *target); bool target_is_darwin(const ZigTarget *target); +bool target_requires_libc(const ZigTarget *target); #endif diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 1600fa9bf7..3fcdf139f6 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4776,7 +4776,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-x"); clang_argv.append("c"); - if (c->codegen->is_native_target) { + if (c->codegen->zig_target->is_native) { char *ZIG_PARSEC_CFLAGS = getenv("ZIG_NATIVE_PARSEC_CFLAGS"); if (ZIG_PARSEC_CFLAGS) { Buf tmp_buf = BUF_INIT; @@ -4798,9 +4798,9 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); - if (codegen->libc_include_dir != nullptr) { + if (codegen->libc != nullptr) { clang_argv.append("-isystem"); - clang_argv.append(buf_ptr(codegen->libc_include_dir)); + clang_argv.append(buf_ptr(&codegen->libc->include_dir)); } // windows c runtime requires -D_DEBUG if using debug libraries @@ -4820,7 +4820,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-Xclang"); clang_argv.append("-detailed-preprocessing-record"); - if (!c->codegen->is_native_target) { + if (!c->codegen->zig_target->is_native) { clang_argv.append("-target"); clang_argv.append(buf_ptr(&c->codegen->triple_str)); } From 00d8f4a1bb634ec5b0569e6ff6c2679ade2bd6b0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 23 Feb 2019 13:19:06 -0500 Subject: [PATCH 098/134] introduce std.debug.captureStackTrace and fix the implementation of writeStackTrace it was printing the first frame in the wrong place --- std/debug/index.zig | 74 ++++++++++++++++++++++++++++++++------- std/special/bootstrap.zig | 11 +++--- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/std/debug/index.zig b/std/debug/index.zig index 1d93e0227e..538fb5ef28 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -90,9 +90,60 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { }; } +/// Returns a slice with the same pointer as addresses, with a potentially smaller len. +/// On Windows, when first_address is not null, we ask for at least 32 stack frames, +/// and then try to find the first address. If addresses.len is more than 32, we +/// capture that many stack frames exactly, and then look for the first address, +/// chopping off the irrelevant frames and shifting so that the returned addresses pointer +/// equals the passed in addresses pointer. +pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { + switch (builtin.os) { + builtin.Os.windows => { + const addrs = stack_trace.instruction_addresses; + const u32_addrs_len = @intCast(u32, addrs.len); + const first_addr = first_address orelse { + stack_trace.index = windows.RtlCaptureStackBackTrace( + 0, + u32_addrs_len, + @ptrCast(**c_void, addrs.ptr), + null, + ); + return; + }; + var addr_buf_stack: [32]usize = undefined; + const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; + const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); + const first_index = for (addr_buf[0..n]) |addr, i| { + if (addr == first_addr) { + break i; + } + } else { + stack_trace.index = 0; + return; + }; + const slice = addr_buf[first_index..n]; + // We use a for loop here because slice and addrs may alias. + for (slice) |addr, i| { + addrs[i] = addr; + } + stack_trace.index = slice.len; + }, + else => { + var it = StackIterator.init(first_address); + for (stack_trace.instruction_addresses) |*addr, i| { + addr.* = it.next() orelse { + stack_trace.index = i; + return; + }; + } + stack_trace.index = stack_trace.instruction_addresses.len; + }, + } +} + /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. /// TODO multithreaded awareness -pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void { +pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { const stderr = getStderrStream() catch return; const debug_info = getSelfDebugInfo() catch |err| { stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; @@ -141,7 +192,7 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c const stderr = getStderrStream() catch os.abort(); stderr.print(format ++ "\n", args) catch os.abort(); if (trace) |t| { - dumpStackTrace(t); + dumpStackTrace(t.*); } dumpCurrentStackTrace(first_trace_addr); @@ -155,16 +206,15 @@ const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; -pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool) !void { - var frame_index: usize = undefined; - var frames_left: usize = undefined; - if (stack_trace.index < stack_trace.instruction_addresses.len) { - frame_index = 0; - frames_left = stack_trace.index; - } else { - frame_index = (stack_trace.index + 1) % stack_trace.instruction_addresses.len; - frames_left = stack_trace.instruction_addresses.len; - } +pub fn writeStackTrace( + stack_trace: builtin.StackTrace, + out_stream: var, + allocator: *mem.Allocator, + debug_info: *DebugInfo, + tty_color: bool, +) !void { + var frame_index: usize = 0; + var frames_left: usize = stack_trace.index; while (frames_left != 0) : ({ frames_left -= 1; diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 6dcc90b372..1192d7e46f 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -23,8 +23,8 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> [*]usize) - ); + : [argc] "=r" (-> [*]usize) + ); }, builtin.Arch.i386 => { argc_ptr = asm ("lea (%%esp), %[argc]" @@ -123,7 +123,7 @@ inline fn callMain() u8 { std.debug.warn("error: {}\n", @errorName(err)); if (builtin.os != builtin.Os.zen) { if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace); + std.debug.dumpStackTrace(trace.*); } } return 1; @@ -142,7 +142,10 @@ fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: var phdr_addr = at_phdr; var n = at_phnum; var base: usize = 0; - while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) { + while (n != 0) : ({ + n -= 1; + phdr_addr += at_phent; + }) { const phdr = @intToPtr(*std.elf.Phdr, phdr_addr); // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917 switch (phdr.p_type) { From 98869edb8b44f164bb46851cff88e2338fe6f399 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 23 Feb 2019 20:25:33 -0500 Subject: [PATCH 099/134] fix `zig fmt` arg0 handled incorrectly --- src/main.cpp | 20 +++++++++++--------- std/special/fmt_runner.zig | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 681cd6e8ac..35746319f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -549,18 +549,20 @@ int main(int argc, char **argv) { codegen_build_and_link(g); - ZigList args = {0}; - for (int i = 2; i < argc; i += 1) { - args.append(argv[i]); - } - args.append(nullptr); + // TODO standardize os.cpp so that the args are supposed to have the exe + ZigList args_with_exe = {0}; + ZigList args_without_exe = {0}; const char *exec_path = buf_ptr(&g->output_file_path); + args_with_exe.append(exec_path); + for (int i = 2; i < argc; i += 1) { + args_with_exe.append(argv[i]); + args_without_exe.append(argv[i]); + } + args_with_exe.append(nullptr); + os_execv(exec_path, args_with_exe.items); - os_execv(exec_path, args.items); - - args.pop(); Termination term; - os_spawn_process(exec_path, args, &term); + os_spawn_process(exec_path, args_without_exe, &term); return term.code; } diff --git a/std/special/fmt_runner.zig b/std/special/fmt_runner.zig index 46ced0e136..f0ed6704ed 100644 --- a/std/special/fmt_runner.zig +++ b/std/special/fmt_runner.zig @@ -37,7 +37,7 @@ pub fn main() !void { stderr = &stderr_out_stream.stream; const args = try std.os.argsAlloc(allocator); - var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args); + var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args[1..]); defer flags.deinit(); if (flags.present("help")) { From a77a17e2c2dc8c2add8a3ed47708c90799425c70 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 24 Feb 2019 12:53:28 -0500 Subject: [PATCH 100/134] add `zig cc` command to act like a C compiler closes #490 --- CMakeLists.txt | 3 + cmake/Findclang.cmake | 22 ++ src/main.cpp | 13 + src/zig_clang_cc1_main.cpp | 237 +++++++++++++++ src/zig_clang_cc1as_main.cpp | 573 +++++++++++++++++++++++++++++++++++ src/zig_clang_driver.cpp | 512 +++++++++++++++++++++++++++++++ 6 files changed, 1360 insertions(+) create mode 100644 src/zig_clang_cc1_main.cpp create mode 100644 src/zig_clang_cc1as_main.cpp create mode 100644 src/zig_clang_driver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eb35d2281..58fa045301 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,6 +431,9 @@ set(BLAKE_SOURCES set(ZIG_CPP_SOURCES "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp" + "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp" "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp" ) diff --git a/cmake/Findclang.cmake b/cmake/Findclang.cmake index 9268f19730..7f5f2f6ed3 100644 --- a/cmake/Findclang.cmake +++ b/cmake/Findclang.cmake @@ -11,17 +11,28 @@ if(MSVC) find_package(CLANG REQUIRED CONFIG) set(CLANG_LIBRARIES + clangFrontendTool + clangCodeGen clangFrontend clangDriver clangSerialization clangSema + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore clangAnalysis + clangASTMatchers clangAST clangParse clangSema clangBasic clangEdit clangLex + clangARCMigrate + clangRewriteFrontend + clangRewrite + clangCrossTU + clangIndex ) else() @@ -50,17 +61,28 @@ else() endif() endmacro(FIND_AND_ADD_CLANG_LIB) + FIND_AND_ADD_CLANG_LIB(clangFrontendTool) + FIND_AND_ADD_CLANG_LIB(clangCodeGen) FIND_AND_ADD_CLANG_LIB(clangFrontend) FIND_AND_ADD_CLANG_LIB(clangDriver) FIND_AND_ADD_CLANG_LIB(clangSerialization) FIND_AND_ADD_CLANG_LIB(clangSema) + FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend) + FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers) + FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore) FIND_AND_ADD_CLANG_LIB(clangAnalysis) + FIND_AND_ADD_CLANG_LIB(clangASTMatchers) FIND_AND_ADD_CLANG_LIB(clangAST) FIND_AND_ADD_CLANG_LIB(clangParse) FIND_AND_ADD_CLANG_LIB(clangSema) FIND_AND_ADD_CLANG_LIB(clangBasic) FIND_AND_ADD_CLANG_LIB(clangEdit) FIND_AND_ADD_CLANG_LIB(clangLex) + FIND_AND_ADD_CLANG_LIB(clangARCMigrate) + FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend) + FIND_AND_ADD_CLANG_LIB(clangRewrite) + FIND_AND_ADD_CLANG_LIB(clangCrossTU) + FIND_AND_ADD_CLANG_LIB(clangIndex) endif() include(FindPackageHandleStandardArgs) diff --git a/src/main.cpp b/src/main.cpp index 681cd6e8ac..bcdf072945 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " build-lib [source] create library from source or object files\n" " build-obj [source] create object from source or assembly\n" " builtin show the source code of that @import(\"builtin\")\n" + " cc C compiler\n" " fmt parse files and render in canonical zig format\n" " help show this usage information\n" " id print the base64-encoded compiler id\n" @@ -240,6 +241,8 @@ static bool get_cache_opt(CacheOpt opt, bool default_value) { zig_unreachable(); } +extern "C" int ZigClang_main(int argc, char **argv); + int main(int argc, char **argv) { char *arg0 = argv[0]; Error err; @@ -257,6 +260,16 @@ int main(int argc, char **argv) { return 0; } + if (argc >= 2 && (strcmp(argv[1], "cc") == 0)) { + return ZigClang_main(argc - 1, argv + 1); + } + if (argc >= 2 && strcmp(argv[1], "-cc1") == 0) { + return ZigClang_main(argc, argv); + } + if (argc >= 2 && strcmp(argv[1], "-cc1as") == 0) { + return ZigClang_main(argc, argv); + } + // Must be before all os.hpp function calls. os_init(); diff --git a/src/zig_clang_cc1_main.cpp b/src/zig_clang_cc1_main.cpp new file mode 100644 index 0000000000..8d1c1802bd --- /dev/null +++ b/src/zig_clang_cc1_main.cpp @@ -0,0 +1,237 @@ +//===-- cc1_main.cpp - Clang CC1 Compiler Frontend ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1 functionality, which implements the +// core compiler functionality along with a number of additional tools for +// demonstration and testing purposes. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Option/Arg.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Config/config.h" +#include "clang/Basic/Stack.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/FrontendTool/Utils.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/LinkAllPasses.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include + +#ifdef CLANG_HAVE_RLIMITS +#include +#endif + +using namespace clang; +using namespace llvm::opt; + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +static void LLVMErrorHandler(void *UserData, const std::string &Message, + bool GenCrashDiag) { + DiagnosticsEngine &Diags = *static_cast(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // Run the interrupt handlers to make sure any special cleanups get done, in + // particular that we remove files registered with RemoveFileOnSignal. + llvm::sys::RunInterruptHandlers(); + + // We cannot recover from llvm errors. When reporting a fatal error, exit + // with status 70 to generate crash diagnostics. For BSD systems this is + // defined as an internal software error. Otherwise, exit with status 1. + exit(GenCrashDiag ? 70 : 1); +} + +#ifdef LINK_POLLY_INTO_TOOLS +namespace polly { +void initializePollyPasses(llvm::PassRegistry &Registry); +} +#endif + +#ifdef CLANG_HAVE_RLIMITS +#if defined(__linux__) && defined(__PIE__) +static size_t getCurrentStackAllocation() { + // If we can't compute the current stack usage, allow for 512K of command + // line arguments and environment. + size_t Usage = 512 * 1024; + if (FILE *StatFile = fopen("/proc/self/stat", "r")) { + // We assume that the stack extends from its current address to the end of + // the environment space. In reality, there is another string literal (the + // program name) after the environment, but this is close enough (we only + // need to be within 100K or so). + unsigned long StackPtr, EnvEnd; + // Disable silly GCC -Wformat warning that complains about length + // modifiers on ignored format specifiers. We want to retain these + // for documentation purposes even though they have no effect. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#endif + if (fscanf(StatFile, + "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu " + "%*lu %*ld %*ld %*ld %*ld %*ld %*ld %*llu %*lu %*ld %*lu %*lu " + "%*lu %*lu %lu %*lu %*lu %*lu %*lu %*lu %*llu %*lu %*lu %*d %*d " + "%*u %*u %*llu %*lu %*ld %*lu %*lu %*lu %*lu %*lu %*lu %lu %*d", + &StackPtr, &EnvEnd) == 2) { +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + Usage = StackPtr < EnvEnd ? EnvEnd - StackPtr : StackPtr - EnvEnd; + } + fclose(StatFile); + } + return Usage; +} + +#include + +LLVM_ATTRIBUTE_NOINLINE +static void ensureStackAddressSpace() { + // Linux kernels prior to 4.1 will sometimes locate the heap of a PIE binary + // relatively close to the stack (they are only guaranteed to be 128MiB + // apart). This results in crashes if we happen to heap-allocate more than + // 128MiB before we reach our stack high-water mark. + // + // To avoid these crashes, ensure that we have sufficient virtual memory + // pages allocated before we start running. + size_t Curr = getCurrentStackAllocation(); + const int kTargetStack = DesiredStackSize - 256 * 1024; + if (Curr < kTargetStack) { + volatile char *volatile Alloc = + static_cast(alloca(kTargetStack - Curr)); + Alloc[0] = 0; + Alloc[kTargetStack - Curr - 1] = 0; + } +} +#else +static void ensureStackAddressSpace() {} +#endif + +/// Attempt to ensure that we have at least 8MiB of usable stack space. +static void ensureSufficientStack() { + struct rlimit rlim; + if (getrlimit(RLIMIT_STACK, &rlim) != 0) + return; + + // Increase the soft stack limit to our desired level, if necessary and + // possible. + if (rlim.rlim_cur != RLIM_INFINITY && + rlim.rlim_cur < rlim_t(DesiredStackSize)) { + // Try to allocate sufficient stack. + if (rlim.rlim_max == RLIM_INFINITY || + rlim.rlim_max >= rlim_t(DesiredStackSize)) + rlim.rlim_cur = DesiredStackSize; + else if (rlim.rlim_cur == rlim.rlim_max) + return; + else + rlim.rlim_cur = rlim.rlim_max; + + if (setrlimit(RLIMIT_STACK, &rlim) != 0 || + rlim.rlim_cur != DesiredStackSize) + return; + } + + // We should now have a stack of size at least DesiredStackSize. Ensure + // that we can actually use that much, if necessary. + ensureStackAddressSpace(); +} +#else +static void ensureSufficientStack() {} +#endif + +int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { + ensureSufficientStack(); + + std::unique_ptr Clang(new CompilerInstance()); + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + + // Register the support for object-file-wrapped Clang modules. + auto PCHOps = Clang->getPCHContainerOperations(); + PCHOps->registerWriter(llvm::make_unique()); + PCHOps->registerReader(llvm::make_unique()); + + // Initialize targets first, so that --version shows registered targets. + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); + +#ifdef LINK_POLLY_INTO_TOOLS + llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry(); + polly::initializePollyPasses(Registry); +#endif + + // Buffer diagnostics from argument parsing so that we can output them using a + // well formed diagnostic object. + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + bool Success = CompilerInvocation::CreateFromArgs( + Clang->getInvocation(), Argv.begin(), Argv.end(), Diags); + + // Infer the builtin include path if unspecified. + if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && + Clang->getHeaderSearchOpts().ResourceDir.empty()) + Clang->getHeaderSearchOpts().ResourceDir = + CompilerInvocation::GetResourcesPath(Argv0, MainAddr); + + // Create the actual diagnostics engine. + Clang->createDiagnostics(); + if (!Clang->hasDiagnostics()) + return 1; + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + llvm::install_fatal_error_handler(LLVMErrorHandler, + static_cast(&Clang->getDiagnostics())); + + DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); + if (!Success) + return 1; + + // Execute the frontend actions. + Success = ExecuteCompilerInvocation(Clang.get()); + + // If any timers were active but haven't been destroyed yet, print their + // results now. This happens in -disable-free mode. + llvm::TimerGroup::printAll(llvm::errs()); + + // Our error handler depends on the Diagnostics object, which we're + // potentially about to delete. Uninstall the handler now so that any + // later errors use the default handling behavior instead. + llvm::remove_fatal_error_handler(); + + // When running with -disable-free, don't do any destruction or shutdown. + if (Clang->getFrontendOpts().DisableFree) { + BuryPointer(std::move(Clang)); + return !Success; + } + + return !Success; +} + diff --git a/src/zig_clang_cc1as_main.cpp b/src/zig_clang_cc1as_main.cpp new file mode 100644 index 0000000000..7ae9752027 --- /dev/null +++ b/src/zig_clang_cc1as_main.cpp @@ -0,0 +1,573 @@ +//===-- cc1as_main.cpp - Clang Assembler ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1as functionality, which implements +// the direct interface to the LLVM MC based assembler. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +using namespace clang; +using namespace clang::driver; +using namespace clang::driver::options; +using namespace llvm; +using namespace llvm::opt; + +namespace { + +/// Helper class for representing a single invocation of the assembler. +struct AssemblerInvocation { + /// @name Target Options + /// @{ + + /// The name of the target triple to assemble for. + std::string Triple; + + /// If given, the name of the target CPU to determine which instructions + /// are legal. + std::string CPU; + + /// The list of target specific features to enable or disable -- this should + /// be a list of strings starting with '+' or '-'. + std::vector Features; + + /// The list of symbol definitions. + std::vector SymbolDefs; + + /// @} + /// @name Language Options + /// @{ + + std::vector IncludePaths; + unsigned NoInitialTextSection : 1; + unsigned SaveTemporaryLabels : 1; + unsigned GenDwarfForAssembly : 1; + unsigned RelaxELFRelocations : 1; + unsigned DwarfVersion; + std::string DwarfDebugFlags; + std::string DwarfDebugProducer; + std::string DebugCompilationDir; + std::map DebugPrefixMap; + llvm::DebugCompressionType CompressDebugSections = + llvm::DebugCompressionType::None; + std::string MainFileName; + std::string SplitDwarfFile; + + /// @} + /// @name Frontend Options + /// @{ + + std::string InputFile; + std::vector LLVMArgs; + std::string OutputPath; + enum FileType { + FT_Asm, ///< Assembly (.s) output, transliterate mode. + FT_Null, ///< No output, for timing purposes. + FT_Obj ///< Object file output. + }; + FileType OutputType; + unsigned ShowHelp : 1; + unsigned ShowVersion : 1; + + /// @} + /// @name Transliterate Options + /// @{ + + unsigned OutputAsmVariant; + unsigned ShowEncoding : 1; + unsigned ShowInst : 1; + + /// @} + /// @name Assembler Options + /// @{ + + unsigned RelaxAll : 1; + unsigned NoExecStack : 1; + unsigned FatalWarnings : 1; + unsigned IncrementalLinkerCompatible : 1; + + /// The name of the relocation model to use. + std::string RelocationModel; + + /// @} + +public: + AssemblerInvocation() { + Triple = ""; + NoInitialTextSection = 0; + InputFile = "-"; + OutputPath = "-"; + OutputType = FT_Asm; + OutputAsmVariant = 0; + ShowInst = 0; + ShowEncoding = 0; + RelaxAll = 0; + NoExecStack = 0; + FatalWarnings = 0; + IncrementalLinkerCompatible = 0; + DwarfVersion = 0; + } + + static bool CreateFromArgs(AssemblerInvocation &Res, + ArrayRef Argv, + DiagnosticsEngine &Diags); +}; + +} + +bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, + ArrayRef Argv, + DiagnosticsEngine &Diags) { + bool Success = true; + + // Parse the arguments. + std::unique_ptr OptTbl(createDriverOptTable()); + + const unsigned IncludedFlagsBitmask = options::CC1AsOption; + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = OptTbl->ParseArgs(Argv, MissingArgIndex, MissingArgCount, + IncludedFlagsBitmask); + + // Check for missing argument error. + if (MissingArgCount) { + Diags.Report(diag::err_drv_missing_argument) + << Args.getArgString(MissingArgIndex) << MissingArgCount; + Success = false; + } + + // Issue errors on unknown arguments. + for (const Arg *A : Args.filtered(OPT_UNKNOWN)) { + auto ArgString = A->getAsString(Args); + std::string Nearest; + if (OptTbl->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1) + Diags.Report(diag::err_drv_unknown_argument) << ArgString; + else + Diags.Report(diag::err_drv_unknown_argument_with_suggestion) + << ArgString << Nearest; + Success = false; + } + + // Construct the invocation. + + // Target Options + Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); + Opts.CPU = Args.getLastArgValue(OPT_target_cpu); + Opts.Features = Args.getAllArgValues(OPT_target_feature); + + // Use the default target triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getDefaultTargetTriple(); + + // Language Options + Opts.IncludePaths = Args.getAllArgValues(OPT_I); + Opts.NoInitialTextSection = Args.hasArg(OPT_n); + Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels); + // Any DebugInfoKind implies GenDwarfForAssembly. + Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ); + + if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections, + OPT_compress_debug_sections_EQ)) { + if (A->getOption().getID() == OPT_compress_debug_sections) { + // TODO: be more clever about the compression type auto-detection + Opts.CompressDebugSections = llvm::DebugCompressionType::GNU; + } else { + Opts.CompressDebugSections = + llvm::StringSwitch(A->getValue()) + .Case("none", llvm::DebugCompressionType::None) + .Case("zlib", llvm::DebugCompressionType::Z) + .Case("zlib-gnu", llvm::DebugCompressionType::GNU) + .Default(llvm::DebugCompressionType::None); + } + } + + Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations); + Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags); + Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); + Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer); + Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); + + for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) + Opts.DebugPrefixMap.insert(StringRef(Arg).split('=')); + + // Frontend Options + if (Args.hasArg(OPT_INPUT)) { + bool First = true; + for (const Arg *A : Args.filtered(OPT_INPUT)) { + if (First) { + Opts.InputFile = A->getValue(); + First = false; + } else { + Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args); + Success = false; + } + } + } + Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); + Opts.OutputPath = Args.getLastArgValue(OPT_o); + Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); + if (Arg *A = Args.getLastArg(OPT_filetype)) { + StringRef Name = A->getValue(); + unsigned OutputType = StringSwitch(Name) + .Case("asm", FT_Asm) + .Case("null", FT_Null) + .Case("obj", FT_Obj) + .Default(~0U); + if (OutputType == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else + Opts.OutputType = FileType(OutputType); + } + Opts.ShowHelp = Args.hasArg(OPT_help); + Opts.ShowVersion = Args.hasArg(OPT_version); + + // Transliterate Options + Opts.OutputAsmVariant = + getLastArgIntValue(Args, OPT_output_asm_variant, 0, Diags); + Opts.ShowEncoding = Args.hasArg(OPT_show_encoding); + Opts.ShowInst = Args.hasArg(OPT_show_inst); + + // Assemble Options + Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); + Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); + Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); + Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); + Opts.IncrementalLinkerCompatible = + Args.hasArg(OPT_mincremental_linker_compatible); + Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym); + + return Success; +} + +static std::unique_ptr +getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) { + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT. + if (Path != "-") + sys::RemoveFileOnSignal(Path); + + std::error_code EC; + auto Out = llvm::make_unique( + Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text)); + if (EC) { + Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message(); + return nullptr; + } + + return Out; +} + +static bool ExecuteAssembler(AssemblerInvocation &Opts, + DiagnosticsEngine &Diags) { + // Get the target specific parser. + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error); + if (!TheTarget) + return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; + + ErrorOr> Buffer = + MemoryBuffer::getFileOrSTDIN(Opts.InputFile); + + if (std::error_code EC = Buffer.getError()) { + Error = EC.message(); + return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile; + } + + SourceMgr SrcMgr; + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc()); + + // Record the location of the include directories so that the lexer can find + // it later. + SrcMgr.setIncludeDirs(Opts.IncludePaths); + + std::unique_ptr MRI(TheTarget->createMCRegInfo(Opts.Triple)); + assert(MRI && "Unable to create target register info!"); + + std::unique_ptr MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple)); + assert(MAI && "Unable to create target asm info!"); + + // Ensure MCAsmInfo initialization occurs before any use, otherwise sections + // may be created with a combination of default and explicit settings. + MAI->setCompressDebugSections(Opts.CompressDebugSections); + + MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations); + + bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; + if (Opts.OutputPath.empty()) + Opts.OutputPath = "-"; + std::unique_ptr FDOS = + getOutputStream(Opts.OutputPath, Diags, IsBinary); + if (!FDOS) + return true; + std::unique_ptr DwoOS; + if (!Opts.SplitDwarfFile.empty()) + DwoOS = getOutputStream(Opts.SplitDwarfFile, Diags, IsBinary); + + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and + // MCObjectFileInfo needs a MCContext reference in order to initialize itself. + std::unique_ptr MOFI(new MCObjectFileInfo()); + + MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr); + + bool PIC = false; + if (Opts.RelocationModel == "static") { + PIC = false; + } else if (Opts.RelocationModel == "pic") { + PIC = true; + } else { + assert(Opts.RelocationModel == "dynamic-no-pic" && + "Invalid PIC model!"); + PIC = false; + } + + MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx); + if (Opts.SaveTemporaryLabels) + Ctx.setAllowTemporaryLabels(false); + if (Opts.GenDwarfForAssembly) + Ctx.setGenDwarfForAssembly(true); + if (!Opts.DwarfDebugFlags.empty()) + Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags)); + if (!Opts.DwarfDebugProducer.empty()) + Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer)); + if (!Opts.DebugCompilationDir.empty()) + Ctx.setCompilationDir(Opts.DebugCompilationDir); + if (!Opts.DebugPrefixMap.empty()) + for (const auto &KV : Opts.DebugPrefixMap) + Ctx.addDebugPrefixMapEntry(KV.first, KV.second); + if (!Opts.MainFileName.empty()) + Ctx.setMainFileName(StringRef(Opts.MainFileName)); + Ctx.setDwarfVersion(Opts.DwarfVersion); + + // Build up the feature string from the target feature list. + std::string FS; + if (!Opts.Features.empty()) { + FS = Opts.Features[0]; + for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i) + FS += "," + Opts.Features[i]; + } + + std::unique_ptr Str; + + std::unique_ptr MCII(TheTarget->createMCInstrInfo()); + std::unique_ptr STI( + TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS)); + + raw_pwrite_stream *Out = FDOS.get(); + std::unique_ptr BOS; + + // FIXME: There is a bit of code duplication with addPassesToEmitFile. + if (Opts.OutputType == AssemblerInvocation::FT_Asm) { + MCInstPrinter *IP = TheTarget->createMCInstPrinter( + llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI); + + std::unique_ptr CE; + if (Opts.ShowEncoding) + CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + MCTargetOptions MCOptions; + std::unique_ptr MAB( + TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); + + auto FOut = llvm::make_unique(*Out); + Str.reset(TheTarget->createAsmStreamer( + Ctx, std::move(FOut), /*asmverbose*/ true, + /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB), + Opts.ShowInst)); + } else if (Opts.OutputType == AssemblerInvocation::FT_Null) { + Str.reset(createNullStreamer(Ctx)); + } else { + assert(Opts.OutputType == AssemblerInvocation::FT_Obj && + "Invalid file type!"); + if (!FDOS->supportsSeeking()) { + BOS = make_unique(*FDOS); + Out = BOS.get(); + } + + std::unique_ptr CE( + TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + MCTargetOptions MCOptions; + std::unique_ptr MAB( + TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); + std::unique_ptr OW = + DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS) + : MAB->createObjectWriter(*Out); + + Triple T(Opts.Triple); + Str.reset(TheTarget->createMCObjectStreamer( + T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI, + Opts.RelaxAll, Opts.IncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ true)); + Str.get()->InitSections(Opts.NoExecStack); + } + + // Assembly to object compilation should leverage assembly info. + Str->setUseAssemblerInfoForParsing(true); + + bool Failed = false; + + std::unique_ptr Parser( + createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); + + // FIXME: init MCTargetOptions from sanitizer flags here. + MCTargetOptions Options; + std::unique_ptr TAP( + TheTarget->createMCAsmParser(*STI, *Parser, *MCII, Options)); + if (!TAP) + Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; + + // Set values for symbols, if any. + for (auto &S : Opts.SymbolDefs) { + auto Pair = StringRef(S).split('='); + auto Sym = Pair.first; + auto Val = Pair.second; + int64_t Value; + // We have already error checked this in the driver. + Val.getAsInteger(0, Value); + Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value); + } + + if (!Failed) { + Parser->setTargetParser(*TAP.get()); + Failed = Parser->Run(Opts.NoInitialTextSection); + } + + // Close Streamer first. + // It might have a reference to the output stream. + Str.reset(); + // Close the output stream early. + BOS.reset(); + FDOS.reset(); + + // Delete output file if there were errors. + if (Failed) { + if (Opts.OutputPath != "-") + sys::fs::remove(Opts.OutputPath); + if (!Opts.SplitDwarfFile.empty() && Opts.SplitDwarfFile != "-") + sys::fs::remove(Opts.SplitDwarfFile); + } + + return Failed; +} + +static void LLVMErrorHandler(void *UserData, const std::string &Message, + bool GenCrashDiag) { + DiagnosticsEngine &Diags = *static_cast(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + exit(1); +} + +int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { + // Initialize targets and assembly printers/parsers. + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + + // Construct our diagnostic client. + IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter *DiagClient + = new TextDiagnosticPrinter(errs(), &*DiagOpts); + DiagClient->setPrefix("clang -cc1as"); + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + ScopedFatalErrorHandler FatalErrorHandler + (LLVMErrorHandler, static_cast(&Diags)); + + // Parse the arguments. + AssemblerInvocation Asm; + if (!AssemblerInvocation::CreateFromArgs(Asm, Argv, Diags)) + return 1; + + if (Asm.ShowHelp) { + std::unique_ptr Opts(driver::createDriverOptTable()); + Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler", + /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0, + /*ShowAllAliases=*/false); + return 0; + } + + // Honor -version. + // + // FIXME: Use a better -version message? + if (Asm.ShowVersion) { + llvm::cl::PrintVersionMessage(); + return 0; + } + + // Honor -mllvm. + // + // FIXME: Remove this, one day. + if (!Asm.LLVMArgs.empty()) { + unsigned NumArgs = Asm.LLVMArgs.size(); + auto Args = llvm::make_unique(NumArgs + 2); + Args[0] = "clang (LLVM option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = Asm.LLVMArgs[i].c_str(); + Args[NumArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); + } + + // Execute the invocation, unless there were parsing errors. + bool Failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags); + + // If any timers were active but haven't been destroyed yet, print their + // results now. + TimerGroup::printAll(errs()); + + return !!Failed; +} + diff --git a/src/zig_clang_driver.cpp b/src/zig_clang_driver.cpp new file mode 100644 index 0000000000..21e88cbe65 --- /dev/null +++ b/src/zig_clang_driver.cpp @@ -0,0 +1,512 @@ +//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang driver; it is a thin wrapper +// for functionality in the Driver clang library. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Driver.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/ToolChain.h" +#include "clang/Frontend/ChainedDiagnosticConsumer.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +using namespace clang; +using namespace clang::driver; +using namespace llvm::opt; + +std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { + if (!CanonicalPrefixes) { + SmallString<128> ExecutablePath(Argv0); + // Do a PATH lookup if Argv0 isn't a valid path. + if (!llvm::sys::fs::exists(ExecutablePath)) + if (llvm::ErrorOr P = + llvm::sys::findProgramByName(ExecutablePath)) + ExecutablePath = *P; + return ExecutablePath.str(); + } + + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *P = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::fs::getMainExecutable(Argv0, P); +} + +static const char *GetStableCStr(std::set &SavedStrings, + StringRef S) { + return SavedStrings.insert(S).first->c_str(); +} + +/// ApplyQAOverride - Apply a list of edits to the input argument lists. +/// +/// The input string is a space separate list of edits to perform, +/// they are applied in order to the input argument lists. Edits +/// should be one of the following forms: +/// +/// '#': Silence information about the changes to the command line arguments. +/// +/// '^': Add FOO as a new argument at the beginning of the command line. +/// +/// '+': Add FOO as a new argument at the end of the command line. +/// +/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command +/// line. +/// +/// 'xOPTION': Removes all instances of the literal argument OPTION. +/// +/// 'XOPTION': Removes all instances of the literal argument OPTION, +/// and the following argument. +/// +/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox' +/// at the end of the command line. +/// +/// \param OS - The stream to write edit information to. +/// \param Args - The vector of command line arguments. +/// \param Edit - The override command to perform. +/// \param SavedStrings - Set to use for storing string representations. +static void ApplyOneQAOverride(raw_ostream &OS, + SmallVectorImpl &Args, + StringRef Edit, + std::set &SavedStrings) { + // This does not need to be efficient. + + if (Edit[0] == '^') { + const char *Str = + GetStableCStr(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at beginning\n"; + Args.insert(Args.begin() + 1, Str); + } else if (Edit[0] == '+') { + const char *Str = + GetStableCStr(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at end\n"; + Args.push_back(Str); + } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") && + Edit.slice(2, Edit.size()-1).find('/') != StringRef::npos) { + StringRef MatchPattern = Edit.substr(2).split('/').first; + StringRef ReplPattern = Edit.substr(2).split('/').second; + ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1); + + for (unsigned i = 1, e = Args.size(); i != e; ++i) { + // Ignore end-of-line response file markers + if (Args[i] == nullptr) + continue; + std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]); + + if (Repl != Args[i]) { + OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n"; + Args[i] = GetStableCStr(SavedStrings, Repl); + } + } + } else if (Edit[0] == 'x' || Edit[0] == 'X') { + auto Option = Edit.substr(1); + for (unsigned i = 1; i < Args.size();) { + if (Option == Args[i]) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + if (Edit[0] == 'X') { + if (i < Args.size()) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + OS << "### Invalid X edit, end of command line!\n"; + } + } else + ++i; + } + } else if (Edit[0] == 'O') { + for (unsigned i = 1; i < Args.size();) { + const char *A = Args[i]; + // Ignore end-of-line response file markers + if (A == nullptr) + continue; + if (A[0] == '-' && A[1] == 'O' && + (A[2] == '\0' || + (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || + ('0' <= A[2] && A[2] <= '9'))))) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + ++i; + } + OS << "### Adding argument " << Edit << " at end\n"; + Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str())); + } else { + OS << "### Unrecognized edit: " << Edit << "\n"; + } +} + +/// ApplyQAOverride - Apply a comma separate list of edits to the +/// input argument lists. See ApplyOneQAOverride. +static void ApplyQAOverride(SmallVectorImpl &Args, + const char *OverrideStr, + std::set &SavedStrings) { + raw_ostream *OS = &llvm::errs(); + + if (OverrideStr[0] == '#') { + ++OverrideStr; + OS = &llvm::nulls(); + } + + *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n"; + + // This does not need to be efficient. + + const char *S = OverrideStr; + while (*S) { + const char *End = ::strchr(S, ' '); + if (!End) + End = S + strlen(S); + if (End != S) + ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings); + S = End; + if (*S != '\0') + ++S; + } +} + +extern int cc1_main(ArrayRef Argv, const char *Argv0, + void *MainAddr); +extern int cc1as_main(ArrayRef Argv, const char *Argv0, + void *MainAddr); + +static void insertTargetAndModeArgs(const ParsedClangName &NameParts, + SmallVectorImpl &ArgVector, + std::set &SavedStrings) { + // Put target and mode arguments at the start of argument list so that + // arguments specified in command line could override them. Avoid putting + // them at index 0, as an option like '-cc1' must remain the first. + int InsertionPoint = 0; + if (ArgVector.size() > 0) + ++InsertionPoint; + + if (NameParts.DriverMode) { + // Add the mode flag to the arguments. + ArgVector.insert(ArgVector.begin() + InsertionPoint, + GetStableCStr(SavedStrings, NameParts.DriverMode)); + } + + if (NameParts.TargetIsValid) { + const char *arr[] = {"-target", GetStableCStr(SavedStrings, + NameParts.TargetPrefix)}; + ArgVector.insert(ArgVector.begin() + InsertionPoint, + std::begin(arr), std::end(arr)); + } +} + +static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver, + SmallVectorImpl &Opts) { + llvm::cl::TokenizeWindowsCommandLine(EnvValue, Saver, Opts); + // The first instance of '#' should be replaced with '=' in each option. + for (const char *Opt : Opts) + if (char *NumberSignPtr = const_cast(::strchr(Opt, '#'))) + *NumberSignPtr = '='; +} + +static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { + // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. + TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); + if (TheDriver.CCPrintOptions) + TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE"); + + // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE. + TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS"); + if (TheDriver.CCPrintHeaders) + TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE"); + + // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE. + TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); + if (TheDriver.CCLogDiagnostics) + TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); +} + +static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, + const std::string &Path) { + // If the clang binary happens to be named cl.exe for compatibility reasons, + // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC. + StringRef ExeBasename(llvm::sys::path::filename(Path)); + if (ExeBasename.equals_lower("cl.exe")) + ExeBasename = "clang-cl.exe"; + DiagClient->setPrefix(ExeBasename); +} + +// This lets us create the DiagnosticsEngine with a properly-filled-out +// DiagnosticOptions instance. +static DiagnosticOptions * +CreateAndPopulateDiagOpts(ArrayRef argv) { + auto *DiagOpts = new DiagnosticOptions; + std::unique_ptr Opts(createDriverOptTable()); + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = + Opts->ParseArgs(argv.slice(1), MissingArgIndex, MissingArgCount); + // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. + // Any errors that would be diagnosed here will also be diagnosed later, + // when the DiagnosticsEngine actually exists. + (void)ParseDiagnosticArgs(*DiagOpts, Args); + return DiagOpts; +} + +static void SetInstallDir(SmallVectorImpl &argv, + Driver &TheDriver, bool CanonicalPrefixes) { + // Attempt to find the original path used to invoke the driver, to determine + // the installed path. We do this manually, because we want to support that + // path being a symlink. + SmallString<128> InstalledPath(argv[0]); + + // Do a PATH lookup, if there are no directory components. + if (llvm::sys::path::filename(InstalledPath) == InstalledPath) + if (llvm::ErrorOr Tmp = llvm::sys::findProgramByName( + llvm::sys::path::filename(InstalledPath.str()))) + InstalledPath = *Tmp; + + // FIXME: We don't actually canonicalize this, we just make it absolute. + if (CanonicalPrefixes) + llvm::sys::fs::make_absolute(InstalledPath); + + StringRef InstalledPathParent(llvm::sys::path::parent_path(InstalledPath)); + if (llvm::sys::fs::exists(InstalledPathParent)) + TheDriver.setInstalledDir(InstalledPathParent); +} + +static int ExecuteCC1Tool(ArrayRef argv, StringRef Tool) { + void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; + if (Tool == "") + return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); + if (Tool == "as") + return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); + + // Reject unknown tools. + llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " + << "Valid tools include '-cc1' and '-cc1as'.\n"; + return 1; +} + +extern "C" int ZigClang_main(int argc_, const char **argv_); +int ZigClang_main(int argc_, const char **argv_) { + llvm::InitLLVM X(argc_, argv_); + SmallVector argv(argv_, argv_ + argc_); + + if (llvm::sys::Process::FixupStandardFileDescriptors()) + return 1; + + llvm::InitializeAllTargets(); + auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]); + + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver(A); + + // Parse response files using the GNU syntax, unless we're in CL mode. There + // are two ways to put clang in CL compatibility mode: argv[0] is either + // clang-cl or cl, or --driver-mode=cl is on the command line. The normal + // command line parsing can't happen until after response file parsing, so we + // have to manually search for a --driver-mode=cl argument the hard way. + // Finally, our -cc1 tools don't care which tokenization mode we use because + // response files written by clang will tokenize the same way in either mode. + bool ClangCLMode = false; + if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") || + std::find_if(argv.begin(), argv.end(), [](const char *F) { + return F && strcmp(F, "--driver-mode=cl") == 0; + }) != argv.end()) { + ClangCLMode = true; + } + enum { Default, POSIX, Windows } RSPQuoting = Default; + for (const char *F : argv) { + if (strcmp(F, "--rsp-quoting=posix") == 0) + RSPQuoting = POSIX; + else if (strcmp(F, "--rsp-quoting=windows") == 0) + RSPQuoting = Windows; + } + + // Determines whether we want nullptr markers in argv to indicate response + // files end-of-lines. We only use this for the /LINK driver argument with + // clang-cl.exe on Windows. + bool MarkEOLs = ClangCLMode; + + llvm::cl::TokenizerCallback Tokenizer; + if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode)) + Tokenizer = &llvm::cl::TokenizeWindowsCommandLine; + else + Tokenizer = &llvm::cl::TokenizeGNUCommandLine; + + if (MarkEOLs && argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) + MarkEOLs = false; + llvm::cl::ExpandResponseFiles(Saver, Tokenizer, argv, MarkEOLs); + + // Handle -cc1 integrated tools, even if -cc1 was expanded from a response + // file. + auto FirstArg = std::find_if(argv.begin() + 1, argv.end(), + [](const char *A) { return A != nullptr; }); + if (FirstArg != argv.end() && StringRef(*FirstArg).startswith("-cc1")) { + // If -cc1 came from a response file, remove the EOL sentinels. + if (MarkEOLs) { + auto newEnd = std::remove(argv.begin(), argv.end(), nullptr); + argv.resize(newEnd - argv.begin()); + } + return ExecuteCC1Tool(argv, argv[1] + 4); + } + + bool CanonicalPrefixes = true; + for (int i = 1, size = argv.size(); i < size; ++i) { + // Skip end-of-line response file markers + if (argv[i] == nullptr) + continue; + if (StringRef(argv[i]) == "-no-canonical-prefixes") { + CanonicalPrefixes = false; + break; + } + } + + // Handle CL and _CL_ which permits additional command line options to be + // prepended or appended. + if (ClangCLMode) { + // Arguments in "CL" are prepended. + llvm::Optional OptCL = llvm::sys::Process::GetEnv("CL"); + if (OptCL.hasValue()) { + SmallVector PrependedOpts; + getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts); + + // Insert right after the program name to prepend to the argument list. + argv.insert(argv.begin() + 1, PrependedOpts.begin(), PrependedOpts.end()); + } + // Arguments in "_CL_" are appended. + llvm::Optional Opt_CL_ = llvm::sys::Process::GetEnv("_CL_"); + if (Opt_CL_.hasValue()) { + SmallVector AppendedOpts; + getCLEnvVarOptions(Opt_CL_.getValue(), Saver, AppendedOpts); + + // Insert at the end of the argument list to append. + argv.append(AppendedOpts.begin(), AppendedOpts.end()); + } + } + + std::set SavedStrings; + // Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the + // scenes. + if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) { + // FIXME: Driver shouldn't take extra initial argument. + ApplyQAOverride(argv, OverrideStr, SavedStrings); + } + + std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes); + + IntrusiveRefCntPtr DiagOpts = + CreateAndPopulateDiagOpts(argv); + + TextDiagnosticPrinter *DiagClient + = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + FixupDiagPrefixExeName(DiagClient, Path); + + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); + + if (!DiagOpts->DiagnosticSerializationFile.empty()) { + auto SerializedConsumer = + clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile, + &*DiagOpts, /*MergeChildRecords=*/true); + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.takeClient(), std::move(SerializedConsumer))); + } + + ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); + + Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags); + SetInstallDir(argv, TheDriver, CanonicalPrefixes); + TheDriver.setTargetAndMode(TargetAndMode); + + insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings); + + SetBackdoorDriverOutputsFromEnvVars(TheDriver); + + std::unique_ptr C(TheDriver.BuildCompilation(argv)); + int Res = 1; + if (C && !C->containsError()) { + SmallVector, 4> FailingCommands; + Res = TheDriver.ExecuteCompilation(*C, FailingCommands); + + // Force a crash to test the diagnostics. + if (TheDriver.GenReproducer) { + Diags.Report(diag::err_drv_force_crash) + << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"); + + // Pretend that every command failed. + FailingCommands.clear(); + for (const auto &J : C->getJobs()) + if (const Command *C = dyn_cast(&J)) + FailingCommands.push_back(std::make_pair(-1, C)); + } + + for (const auto &P : FailingCommands) { + int CommandRes = P.first; + const Command *FailingCommand = P.second; + if (!Res) + Res = CommandRes; + + // If result status is < 0, then the driver command signalled an error. + // If result status is 70, then the driver command reported a fatal error. + // On Windows, abort will return an exit code of 3. In these cases, + // generate additional diagnostic information if possible. + bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70; +#ifdef _WIN32 + DiagnoseCrash |= CommandRes == 3; +#endif + if (DiagnoseCrash) { + TheDriver.generateCompilationDiagnostics(*C, *FailingCommand); + break; + } + } + } + + Diags.getClient()->finish(); + + // If any timers were active but haven't been destroyed yet, print their + // results now. This happens in -disable-free mode. + llvm::TimerGroup::printAll(llvm::errs()); + +#ifdef _WIN32 + // Exit status should not be negative on Win32, unless abnormal termination. + // Once abnormal termiation was caught, negative status should not be + // propagated. + if (Res < 0) + Res = 1; +#endif + + // If we have multiple failing commands, we return the result of the first + // failing command. + return Res; +} + From bcca76b3c4c6dcb24435db7a3a10d2319ef4c3aa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 24 Feb 2019 13:37:41 -0500 Subject: [PATCH 101/134] zig cc: remove "polly" which was an undefined symbol on macos --- src/zig_clang_cc1_main.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/zig_clang_cc1_main.cpp b/src/zig_clang_cc1_main.cpp index 8d1c1802bd..ee0f212ff3 100644 --- a/src/zig_clang_cc1_main.cpp +++ b/src/zig_clang_cc1_main.cpp @@ -67,12 +67,6 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message, exit(GenCrashDiag ? 70 : 1); } -#ifdef LINK_POLLY_INTO_TOOLS -namespace polly { -void initializePollyPasses(llvm::PassRegistry &Registry); -} -#endif - #ifdef CLANG_HAVE_RLIMITS #if defined(__linux__) && defined(__PIE__) static size_t getCurrentStackAllocation() { @@ -181,11 +175,6 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); -#ifdef LINK_POLLY_INTO_TOOLS - llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry(); - polly::initializePollyPasses(Registry); -#endif - // Buffer diagnostics from argument parsing so that we can output them using a // well formed diagnostic object. IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); From 8c2c6368f9645def45374c2fb9027bf72b15ab2e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 24 Feb 2019 15:30:30 -0500 Subject: [PATCH 102/134] zig cc: work around clang calling GetCommandLine on Windows --- src/main.cpp | 10 +++------- src/zig_clang_driver.cpp | 3 ++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bcdf072945..94f282242a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -260,13 +260,9 @@ int main(int argc, char **argv) { return 0; } - if (argc >= 2 && (strcmp(argv[1], "cc") == 0)) { - return ZigClang_main(argc - 1, argv + 1); - } - if (argc >= 2 && strcmp(argv[1], "-cc1") == 0) { - return ZigClang_main(argc, argv); - } - if (argc >= 2 && strcmp(argv[1], "-cc1as") == 0) { + if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || + strcmp(argv[1], "-cc1") == 0 || strcmp(argv[1], "-cc1as") == 0)) + { return ZigClang_main(argc, argv); } diff --git a/src/zig_clang_driver.cpp b/src/zig_clang_driver.cpp index 21e88cbe65..3a3942636a 100644 --- a/src/zig_clang_driver.cpp +++ b/src/zig_clang_driver.cpp @@ -318,7 +318,8 @@ static int ExecuteCC1Tool(ArrayRef argv, StringRef Tool) { extern "C" int ZigClang_main(int argc_, const char **argv_); int ZigClang_main(int argc_, const char **argv_) { llvm::InitLLVM X(argc_, argv_); - SmallVector argv(argv_, argv_ + argc_); + size_t argv_offset = (strcmp(argv_[1], "-cc1") == 0 || strcmp(argv_[1], "-cc1as") == 0) ? 0 : 1; + SmallVector argv(argv_ + argv_offset, argv_ + argc_); if (llvm::sys::Process::FixupStandardFileDescriptors()) return 1; From e76ce2c1d0d3988359267fd3030a81a52ec99f3f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 11:37:54 -0500 Subject: [PATCH 103/134] first class support for compiling C code New CLI parameter: --c-source [options] [file] It even works with `--cache on` when there are transitive dependencies. Instead of `builder.addCExecutable`, use `builder.addExecutable` and pass `null` for the root source file. Then use `builder.addCSourceFile`, which takes the path to the C code, and a list of C compiler args. Be sure to linkSystemLibrary("c") if you want libc headers to be available. Merge TestStep into LibExeObjStep. That was long overdue. --- example/mix_o_files/build.zig | 6 +- example/shared_library/build.zig | 6 +- src/all_types.hpp | 7 + src/codegen.cpp | 195 +++++++- src/main.cpp | 85 +++- std/buf_set.zig | 4 + std/build.zig | 794 +++++-------------------------- test/stage1/c_abi/build.zig | 5 +- 8 files changed, 379 insertions(+), 723 deletions(-) diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index a4e7fbbf8f..623ec63de9 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -3,10 +3,10 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const obj = b.addObject("base64", "base64.zig"); - const exe = b.addCExecutable("test"); - exe.addCompileFlags([][]const u8{"-std=c99"}); - exe.addSourceFile("test.c"); + const exe = b.addExecutable("test", null); + exe.addCSourceFile("test.c",[][]const u8{"-std=c99"}); exe.addObject(obj); + exe.linkSystemLibrary("c"); b.default_step.dependOn(&exe.step); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 05648cf9eb..5eaa4f4402 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -3,10 +3,10 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); - const exe = b.addCExecutable("test"); - exe.addCompileFlags([][]const u8{"-std=c99"}); - exe.addSourceFile("test.c"); + const exe = b.addExecutable("test", null); + exe.addCSourceFile("test.c", [][]const u8{"-std=c99"}); exe.linkLibrary(lib); + exe.linkSystemLibrary("c"); b.default_step.dependOn(&exe.step); diff --git a/src/all_types.hpp b/src/all_types.hpp index f29411476e..baa0267238 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1611,6 +1611,11 @@ enum ValgrindSupport { ValgrindSupportEnabled, }; +struct CFile { + ZigList args; + const char *source_path; +}; + // When adding fields, check if they should be added to the hash computation in build_with_cache struct CodeGen { //////////////////////////// Runtime State @@ -1788,6 +1793,7 @@ struct CodeGen { bool verbose_ir; bool verbose_llvm_ir; bool verbose_cimport; + bool verbose_cc; bool error_during_imports; bool generate_error_name_table; bool enable_cache; @@ -1805,6 +1811,7 @@ struct CodeGen { ZigList forbidden_libs; ZigList link_objects; ZigList assembly_files; + ZigList c_source_files; ZigList lib_dirs; ZigLibCInstallation *libc; diff --git a/src/codegen.cpp b/src/codegen.cpp index 54da63e9f9..f14749bb28 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7885,8 +7885,8 @@ static void detect_libc(CodeGen *g) { fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); exit(1); } - if (rename(buf_ptr(native_libc_tmp), buf_ptr(native_libc_txt)) == -1) { - fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(native_libc_txt), strerror(errno)); + if ((err = os_rename(native_libc_tmp, native_libc_txt))) { + fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(native_libc_txt), err_str(err)); exit(1); } } @@ -8123,6 +8123,162 @@ static void gen_global_asm(CodeGen *g) { } } +static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { + Error err; + + Buf *c_source_file = buf_create_from_str(c_file->source_path); + Buf *c_source_basename = buf_alloc(); + os_path_split(c_source_file, nullptr, c_source_basename); + Buf *out_obj_name = buf_sprintf("%s%s", buf_ptr(c_source_basename), target_o_file_ext(g->zig_target)); + Buf *out_obj_path = buf_alloc(); + os_path_join(&g->cache_dir, out_obj_name, out_obj_path); + Buf *out_dep_name = buf_sprintf("%s.d", buf_ptr(c_source_file)); + Buf *out_dep_path = buf_alloc(); + os_path_join(&g->cache_dir, out_dep_name, out_dep_path); + + Termination term; + ZigList args = {}; + args.append("cc"); + + if (g->enable_cache) { + args.append("-MD"); + args.append("-MF"); + args.append(buf_ptr(out_dep_path)); + } + + args.append("-isystem"); + args.append(buf_ptr(g->zig_c_headers_dir)); + + if (g->libc != nullptr) { + args.append("-isystem"); + args.append(buf_ptr(&g->libc->include_dir)); + } + + if (g->zig_target->is_native) { + args.append("-march=native"); + } else { + args.append("-target"); + args.append(buf_ptr(&g->triple_str)); + } + + if (!g->strip_debug_symbols) { + args.append("-g"); + } + switch (g->build_mode) { + case BuildModeDebug: + if (g->libc_link_lib != nullptr) { + args.append("-fstack-protector-strong"); + args.append("--param"); + args.append("ssp-buffer-size=4"); + } else { + args.append("-fno-stack-protector"); + } + break; + case BuildModeSafeRelease: + args.append("-O2"); + if (g->libc_link_lib != nullptr) { + args.append("-D_FORTIFY_SOURCE=2"); + args.append("-fstack-protector-strong"); + args.append("--param"); + args.append("ssp-buffer-size=4"); + } else { + args.append("-fno-stack-protector"); + } + break; + case BuildModeFastRelease: + args.append("-O2"); + args.append("-fno-stack-protector"); + break; + case BuildModeSmallRelease: + args.append("-Os"); + args.append("-fno-stack-protector"); + break; + } + + args.append("-o"); + args.append(buf_ptr(out_obj_path)); + + args.append("-c"); + args.append(buf_ptr(c_source_file)); + + if (!g->disable_pic) { + args.append("-fPIC"); + } + + for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { + args.append(g->clang_argv[arg_i]); + } + + for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) { + args.append(c_file->args.at(arg_i)); + } + + if (g->verbose_cc) { + for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { + fprintf(stderr, "%s ", args.at(arg_i)); + } + fprintf(stderr, "\n"); + } + + os_spawn_process(buf_ptr(self_exe_path), args, &term); + if (term.how != TerminationIdClean || term.code != 0) { + fprintf(stderr, "`zig cc` failed\n"); + exit(1); + } + + g->link_objects.append(out_obj_path); + + // add the files depended on to the cache system + if (g->enable_cache) { + Buf *contents = buf_alloc(); + if ((err = os_fetch_file_path(out_dep_path, contents, false))) { + fprintf(stderr, "unable to read .d file: %s\n", err_str(err)); + exit(1); + } + if ((err = cache_add_file(&g->cache_hash, c_source_file))) { + fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(c_source_file), err_str(err)); + exit(1); + } + SplitIterator it = memSplit(buf_to_slice(contents), str("\n")); + // skip first line + SplitIterator_next(&it); + for (;;) { + Optional> opt_line = SplitIterator_next(&it); + if (!opt_line.is_some) + break; + if (opt_line.value.len == 0) + continue; + SplitIterator line_it = memSplit(opt_line.value, str(" \t")); + Slice filename; + if (!SplitIterator_next(&line_it).unwrap(&filename)) + continue; + Buf *filename_buf = buf_create_from_slice(filename); + if ((err = cache_add_file(&g->cache_hash, filename_buf))) { + fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(c_source_file), err_str(err)); + exit(1); + } + } + } +} + +static void gen_c_objects(CodeGen *g) { + Error err; + + if (g->c_source_files.length == 0) + return; + + Buf *self_exe_path = buf_alloc(); + if ((err = os_self_exe_path(self_exe_path))) { + fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err)); + exit(1); + } + + for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { + CFile *c_file = g->c_source_files.at(c_file_i); + gen_c_object(g, self_exe_path, c_file); + } +} + void codegen_add_object(CodeGen *g, Buf *object_path) { g->link_objects.append(object_path); } @@ -8637,6 +8793,13 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_buf(ch, g->forbidden_libs.items, g->forbidden_libs.length); cache_list_of_file(ch, g->link_objects.items, g->link_objects.length); cache_list_of_file(ch, g->assembly_files.items, g->assembly_files.length); + for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { + CFile *c_file = g->c_source_files.at(c_file_i); + cache_file(ch, buf_create_from_str(c_file->source_path)); + for (size_t opt_i = 0; opt_i < c_file->args.length; opt_i += 1) { + cache_buf(ch, buf_create_from_str(c_file->args.at(opt_i))); + } + } cache_int(ch, g->emit_file_type); cache_int(ch, g->build_mode); cache_int(ch, g->out_type); @@ -8788,6 +8951,7 @@ void codegen_build_and_link(CodeGen *g) { gen_global_asm(g); gen_root_source(g); + gen_c_objects(g); if (g->enable_cache) { if ((err = cache_final(&g->cache_hash, &digest))) { @@ -8805,16 +8969,25 @@ void codegen_build_and_link(CodeGen *g) { resolve_out_paths(g); codegen_add_time_event(g, "Code Generation"); - do_code_gen(g); - codegen_add_time_event(g, "LLVM Emit Output"); - zig_llvm_emit_output(g); + if (g->out_type == OutTypeObj && g->c_source_files.length == 1) { + assert(g->link_objects.length == 1); + if ((err = os_rename(g->link_objects.pop(), &g->o_file_output_path))) { + fprintf(stderr, "unable to move object to '%s': %s\n", + buf_ptr(&g->o_file_output_path), err_str(err)); + exit(1); + } + } else { + do_code_gen(g); + codegen_add_time_event(g, "LLVM Emit Output"); + zig_llvm_emit_output(g); - if (g->out_h_path != nullptr) { - codegen_add_time_event(g, "Generate .h"); - gen_h_file(g); - } - if (g->out_type != OutTypeObj && g->emit_file_type == EmitFileTypeBinary) { - codegen_link(g); + if (g->out_h_path != nullptr) { + codegen_add_time_event(g, "Generate .h"); + gen_h_file(g); + } + if (g->out_type != OutTypeObj && g->emit_file_type == EmitFileTypeBinary) { + codegen_link(g); + } } } diff --git a/src/main.cpp b/src/main.cpp index 14a885f08a..a7f6e0b95d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ #include static int print_error_usage(const char *arg0) { - fprintf(stderr, "See `%s help` for detailed usage information\n", arg0); + fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0); return EXIT_FAILURE; } @@ -34,7 +34,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " builtin show the source code of that @import(\"builtin\")\n" " cc C compiler\n" " fmt parse files and render in canonical zig format\n" - " help show this usage information\n" " id print the base64-encoded compiler id\n" " init-exe initialize a `zig build` application in the cwd\n" " init-lib initialize a `zig build` library in the cwd\n" @@ -48,6 +47,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { "\n" "Compile Options:\n" " --assembly [source] add assembly file to build\n" + " --c-source [options] [file] compile C source code\n" " --cache-dir [path] override the cache directory\n" " --cache [auto|off|on] build in global cache, print out paths to stdout\n" " --color [auto|off|on] enable or disable colored error messages\n" @@ -77,6 +77,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --verbose-ir enable compiler debug output for Zig IR\n" " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" " --verbose-cimport enable compiler debug output for C imports\n" + " --verbose-cc enable compiler debug output for C compilation\n" " -dirafter [dir] same as -isystem but do it last\n" " -isystem [dir] add additional search path for other .h files\n" " -mllvm [arg] forward an arg to LLVM's option processing\n" @@ -181,7 +182,6 @@ enum Cmd { CmdNone, CmdBuild, CmdBuiltin, - CmdHelp, CmdRun, CmdTargets, CmdTest, @@ -385,6 +385,7 @@ int main(int argc, char **argv) { bool verbose_ir = false; bool verbose_llvm_ir = false; bool verbose_cimport = false; + bool verbose_cc = false; ErrColor color = ErrColorAuto; CacheOpt enable_cache = CacheOptAuto; const char *libc_txt = nullptr; @@ -404,6 +405,7 @@ int main(int argc, char **argv) { ZigList rpath_list = {0}; bool each_lib_rpath = false; ZigList objects = {0}; + ZigList c_source_files = {0}; ZigList asm_files = {0}; const char *test_filter = nullptr; const char *test_name_prefix = nullptr; @@ -512,6 +514,7 @@ int main(int argc, char **argv) { " --verbose-ir Enable compiler debug output for Zig IR\n" " --verbose-llvm-ir Enable compiler debug output for LLVM IR\n" " --verbose-cimport Enable compiler debug output for C imports\n" + " --verbose-cc Enable compiler debug output for C compilation\n" "\n" , zig_exe_path); return EXIT_SUCCESS; @@ -521,7 +524,7 @@ int main(int argc, char **argv) { "No 'build.zig' file found.\n" "Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,\n" "or build an executable directly with `zig build-exe $FILENAME.zig`.\n" - "See: `zig build --help` or `zig help` for more options.\n" + "See: `zig build --help` or `zig --help` for more options.\n" ); return EXIT_FAILURE; } @@ -587,9 +590,9 @@ int main(int argc, char **argv) { build_mode = BuildModeSmallRelease; } else if (strcmp(arg, "--help") == 0) { if (cmd == CmdLibC) { - return print_libc_usage(arg0, stderr, EXIT_FAILURE); + return print_libc_usage(arg0, stdout, EXIT_SUCCESS); } else { - return print_full_usage(arg0, stderr, EXIT_FAILURE); + return print_full_usage(arg0, stdout, EXIT_SUCCESS); } } else if (strcmp(arg, "--strip") == 0) { strip = true; @@ -607,6 +610,8 @@ int main(int argc, char **argv) { verbose_llvm_ir = true; } else if (strcmp(arg, "--verbose-cimport") == 0) { verbose_cimport = true; + } else if (strcmp(arg, "--verbose-cc") == 0) { + verbose_cc = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; } else if (strcmp(arg, "--each-lib-rpath") == 0) { @@ -714,6 +719,19 @@ int main(int argc, char **argv) { forbidden_link_libs.append(argv[i]); } else if (strcmp(arg, "--object") == 0) { objects.append(argv[i]); + } else if (strcmp(arg, "--c-source") == 0) { + CFile *c_file = allocate(1); + for (;;) { + if (argv[i][0] == '-') { + c_file->args.append(argv[i]); + i += 1; + continue; + } else { + c_file->source_path = argv[i]; + c_source_files.append(c_file); + break; + } + } } else if (strcmp(arg, "--assembly") == 0) { asm_files.append(argv[i]); } else if (strcmp(arg, "--cache-dir") == 0) { @@ -792,8 +810,6 @@ int main(int argc, char **argv) { } else if (strcmp(arg, "build-lib") == 0) { cmd = CmdBuild; out_type = OutTypeLib; - } else if (strcmp(arg, "help") == 0) { - cmd = CmdHelp; } else if (strcmp(arg, "run") == 0) { cmd = CmdRun; out_type = OutTypeExe; @@ -835,7 +851,6 @@ int main(int argc, char **argv) { } break; case CmdBuiltin: - case CmdHelp: case CmdVersion: case CmdZen: case CmdTargets: @@ -910,15 +925,43 @@ int main(int argc, char **argv) { case CmdTranslateC: case CmdTest: { - if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) { - fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n"); + if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0 && + c_source_files.length == 0) + { + fprintf(stderr, + "Expected at least one of these things:\n" + " * Zig root source file argument\n" + " * --object argument\n" + " * --assembly argument\n" + " * --c-source argument\n"); return print_error_usage(arg0); } else if ((cmd == CmdTranslateC || cmd == CmdTest || cmd == CmdRun) && !in_file) { fprintf(stderr, "Expected source file argument.\n"); return print_error_usage(arg0); - } else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) { - fprintf(stderr, "When building an object file, --object arguments are invalid.\n"); - return print_error_usage(arg0); + } else if (cmd == CmdBuild && out_type == OutTypeObj) { + if (objects.length != 0) { + fprintf(stderr, + "When building an object file, --object arguments are invalid.\n" + "Consider building a static library instead.\n"); + return print_error_usage(arg0); + } + size_t zig_root_src_count = in_file ? 1 : 0; + if (zig_root_src_count + c_source_files.length > 1) { + fprintf(stderr, + "When building an object file, only one of these allowed:\n" + " * Zig root source file argument\n" + " * --c-source argument\n" + "Consider building a static library instead.\n"); + return print_error_usage(arg0); + } + if (c_source_files.length != 0 && asm_files.length != 0) { + fprintf(stderr, + "When building an object file, only one of these allowed:\n" + " * --assembly argument\n" + " * --c-source argument\n" + "Consider building a static library instead.\n"); + return print_error_usage(arg0); + } } assert(cmd != CmdBuild || out_type != OutTypeUnknown); @@ -945,6 +988,13 @@ int main(int argc, char **argv) { } } + if (need_name && buf_out_name == nullptr && c_source_files.length == 1) { + Buf basename = BUF_INIT; + os_path_split(buf_create_from_str(c_source_files.at(0)->source_path), nullptr, &basename); + buf_out_name = buf_alloc(); + os_path_extname(&basename, buf_out_name, nullptr); + } + if (need_name && buf_out_name == nullptr) { fprintf(stderr, "--name [name] not provided and unable to infer\n\n"); return print_error_usage(arg0); @@ -996,6 +1046,7 @@ int main(int argc, char **argv) { g->verbose_ir = verbose_ir; g->verbose_llvm_ir = verbose_llvm_ir; g->verbose_cimport = verbose_cimport; + g->verbose_cc = verbose_cc; codegen_set_errmsg_color(g, color); g->system_linker_hack = system_linker_hack; @@ -1048,6 +1099,7 @@ int main(int argc, char **argv) { add_package(g, cur_pkg, g->root_package); if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { + g->c_source_files = c_source_files; for (size_t i = 0; i < objects.length; i += 1) { codegen_add_object(g, buf_create_from_str(objects.at(i))); } @@ -1147,8 +1199,6 @@ int main(int argc, char **argv) { zig_unreachable(); } } - case CmdHelp: - return print_full_usage(arg0, stdout, EXIT_SUCCESS); case CmdVersion: printf("%s\n", ZIG_VERSION_STRING); return EXIT_SUCCESS; @@ -1158,7 +1208,6 @@ int main(int argc, char **argv) { case CmdTargets: return print_target_list(stdout); case CmdNone: - fprintf(stderr, "Zig programming language\n"); - return print_error_usage(arg0); + return print_full_usage(arg0, stderr, EXIT_FAILURE); } } diff --git a/std/buf_set.zig b/std/buf_set.zig index 7ccd94c179..484b4953b9 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -32,6 +32,10 @@ pub const BufSet = struct { } } + pub fn exists(self: BufSet, key: []const u8) bool { + return self.hash_map.get(key) != null; + } + pub fn delete(self: *BufSet, key: []const u8) void { const entry = self.hash_map.remove(key) orelse return; self.free(entry.key); diff --git a/std/build.zig b/std/build.zig index 946a447a11..4386b730cd 100644 --- a/std/build.zig +++ b/std/build.zig @@ -31,6 +31,7 @@ pub const Builder = struct { verbose_tokenize: bool, verbose_ast: bool, verbose_link: bool, + verbose_cc: bool, verbose_ir: bool, verbose_llvm_ir: bool, verbose_cimport: bool, @@ -99,6 +100,7 @@ pub const Builder = struct { .verbose_tokenize = false, .verbose_ast = false, .verbose_link = false, + .verbose_cc = false, .verbose_ir = false, .verbose_llvm_ir = false, .verbose_cimport = false, @@ -157,7 +159,7 @@ pub const Builder = struct { return LibExeObjStep.createExecutable(self, name, root_src, true); } - pub fn addObject(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { + pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { return LibExeObjStep.createObject(self, name, root_src); } @@ -169,10 +171,8 @@ pub const Builder = struct { return LibExeObjStep.createStaticLibrary(self, name, root_src); } - pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { - const test_step = self.allocator.create(TestStep) catch unreachable; - test_step.* = TestStep.init(self, root_src); - return test_step; + pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { + return LibExeObjStep.createTest(self, "test", root_src); } pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { @@ -181,22 +181,6 @@ pub const Builder = struct { return obj_step; } - pub fn addCStaticLibrary(self: *Builder, name: []const u8) *LibExeObjStep { - return LibExeObjStep.createCStaticLibrary(self, name); - } - - pub fn addCSharedLibrary(self: *Builder, name: []const u8, ver: Version) *LibExeObjStep { - return LibExeObjStep.createCSharedLibrary(self, name, ver); - } - - pub fn addCExecutable(self: *Builder, name: []const u8) *LibExeObjStep { - return LibExeObjStep.createCExecutable(self, name); - } - - pub fn addCObject(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - return LibExeObjStep.createCObject(self, name, src); - } - /// ::argv is copied. pub fn addCommand(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { return CommandStep.create(self, cwd, env_map, argv); @@ -663,14 +647,6 @@ pub const Builder = struct { return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; } - fn getCCExe(self: *Builder) []const u8 { - if (builtin.environ == builtin.Environ.msvc) { - return "cl.exe"; - } else { - return os.getEnvVarOwned(self.allocator, "CC") catch |err| if (err == error.EnvironmentVariableNotFound) ([]const u8)("cc") else debug.panic("Unable to get environment variable: {}", err); - } - } - pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations const exe_extension = (Target{ .Native = {} }).exeFileExt(); @@ -825,6 +801,11 @@ const Pkg = struct { path: []const u8, }; +const CSourceFile = struct { + source_path: []const u8, + args: []const []const u8, +}; + pub const LibExeObjStep = struct { step: Step, builder: *Builder, @@ -844,16 +825,17 @@ pub const LibExeObjStep = struct { strip: bool, full_path_libs: ArrayList([]const u8), need_flat_namespace_hack: bool, - is_zig: bool, - cflags: ArrayList([]const u8), include_dirs: ArrayList([]const u8), lib_paths: ArrayList([]const u8), - disable_libc: bool, frameworks: BufSet, verbose_link: bool, + verbose_cc: bool, c_std: Builder.CStd, + override_std_dir: ?[]const u8, + exec_cmd_args: ?[]const ?[]const u8, + name_prefix: []const u8, + filter: ?[]const u8, - // zig only stuff root_src: ?[]const u8, output_h_path: ?[]const u8, out_h_filename: []const u8, @@ -862,14 +844,14 @@ pub const LibExeObjStep = struct { build_options_contents: std.Buffer, system_linker_hack: bool, - // C only stuff - source_files: ArrayList([]const u8), + c_source_files: ArrayList(*CSourceFile), object_src: []const u8, const Kind = enum { Exe, Lib, Obj, + Test, }; pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { @@ -878,46 +860,27 @@ pub const LibExeObjStep = struct { return self; } - pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: Version) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Lib, version, false); - return self; - } - pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } - pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); - return self; - } - - pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { + pub fn createObject(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } - pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); - self.object_src = src; - return self; - } - pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; self.* = initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0)); return self; } - pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { + pub fn createTest(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); + self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); return self; } @@ -926,6 +889,7 @@ pub const LibExeObjStep = struct { .strip = false, .builder = builder, .verbose_link = false, + .verbose_cc = false, .build_mode = builtin.Mode.Debug, .static = static, .kind = kind, @@ -946,61 +910,19 @@ pub const LibExeObjStep = struct { .object_files = ArrayList([]const u8).init(builder.allocator), .assembly_files = ArrayList([]const u8).init(builder.allocator), .packages = ArrayList(Pkg).init(builder.allocator), - .is_zig = true, .full_path_libs = ArrayList([]const u8).init(builder.allocator), .need_flat_namespace_hack = false, - .cflags = ArrayList([]const u8).init(builder.allocator), - .source_files = undefined, + .c_source_files = ArrayList(*CSourceFile).init(builder.allocator), .include_dirs = ArrayList([]const u8).init(builder.allocator), .lib_paths = ArrayList([]const u8).init(builder.allocator), .object_src = undefined, - .disable_libc = true, .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, .c_std = Builder.CStd.C99, .system_linker_hack = false, - }; - self.computeOutFileNames(); - return self; - } - - fn initC(builder: *Builder, name: []const u8, kind: Kind, version: Version, static: bool) LibExeObjStep { - var self = LibExeObjStep{ - .builder = builder, - .name = name, - .kind = kind, - .version = version, - .static = static, - .target = Target.Native, - .cflags = ArrayList([]const u8).init(builder.allocator), - .source_files = ArrayList([]const u8).init(builder.allocator), - .object_files = ArrayList([]const u8).init(builder.allocator), - .step = Step.init(name, builder.allocator, make), - .link_libs = BufSet.init(builder.allocator), - .frameworks = BufSet.init(builder.allocator), - .full_path_libs = ArrayList([]const u8).init(builder.allocator), - .include_dirs = ArrayList([]const u8).init(builder.allocator), - .lib_paths = ArrayList([]const u8).init(builder.allocator), - .output_path = null, - .out_filename = undefined, - .major_only_filename = undefined, - .name_only_filename = undefined, - .object_src = undefined, - .build_mode = builtin.Mode.Debug, - .strip = false, - .need_flat_namespace_hack = false, - .disable_libc = false, - .is_zig = false, - .linker_script = null, - .c_std = Builder.CStd.C99, - .system_linker_hack = false, - - .root_src = undefined, - .verbose_link = false, - .output_h_path = undefined, - .out_h_filename = undefined, - .assembly_files = undefined, - .packages = undefined, - .build_options_contents = undefined, + .override_std_dir = null, + .exec_cmd_args = null, + .name_prefix = "", + .filter = null, }; self.computeOutFileNames(); return self; @@ -1014,6 +936,9 @@ pub const LibExeObjStep = struct { Kind.Exe => { self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt()); }, + Kind.Test => { + self.out_filename = self.builder.fmt("test{}", self.target.exeFileExt()); + }, Kind.Lib => { if (self.static) { self.out_filename = self.builder.fmt("lib{}.a", self.name); @@ -1081,24 +1006,44 @@ pub const LibExeObjStep = struct { } pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { - assert(self.kind != Kind.Obj); self.link_libs.put(name) catch unreachable; } - pub fn addSourceFile(self: *LibExeObjStep, file: []const u8) void { - assert(self.kind != Kind.Obj); - assert(!self.is_zig); - self.source_files.append(file) catch unreachable; + pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { + assert(self.kind == Kind.Test); + self.name_prefix = text; + } + + pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { + assert(self.kind == Kind.Test); + self.filter = text; + } + + pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void { + const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; + c_source_file.* = CSourceFile{ + .source_path = file, + .args = args, + }; + self.c_source_files.append(c_source_file) catch unreachable; } pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { self.verbose_link = value; } + pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { + self.verbose_cc = value; + } + pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void { self.build_mode = mode; } + pub fn overrideStdDir(self: *LibExeObjStep, dir_path: []const u8) void { + self.override_std_dir = dir_path; + } + pub fn setOutputPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_path = file_path; @@ -1149,17 +1094,15 @@ pub const LibExeObjStep = struct { self.object_files.append(obj.getOutputPath()) catch unreachable; - // TODO make this lazy instead of stateful - if (!obj.disable_libc) { - self.disable_libc = false; - } - // TODO should be some kind of isolated directory that only has this header in it self.include_dirs.append(self.builder.cache_root) catch unreachable; + + if (obj.link_libs.exists("c")) { + self.link_libs.put("c") catch unreachable; + } } pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { - assert(self.is_zig); const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; out.print("pub const {} = {};\n", name, value) catch unreachable; } @@ -1173,23 +1116,15 @@ pub const LibExeObjStep = struct { } pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { - assert(self.is_zig); - self.packages.append(Pkg{ .name = name, .path = pkg_index_path, }) catch unreachable; } - pub fn addCompileFlags(self: *LibExeObjStep, flags: []const []const u8) void { - for (flags) |flag| { - self.cflags.append(flag) catch unreachable; - } - } - - pub fn setNoStdLib(self: *LibExeObjStep, disable: bool) void { - assert(!self.is_zig); - self.disable_libc = disable; + pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { + assert(self.kind == Kind.Test); + self.exec_cmd_args = args; } pub fn enableSystemLinkerHack(self: *LibExeObjStep) void { @@ -1198,15 +1133,11 @@ pub const LibExeObjStep = struct { fn make(step: *Step) !void { const self = @fieldParentPtr(LibExeObjStep, "step", step); - return if (self.is_zig) self.makeZig() else self.makeC(); - } - - fn makeZig(self: *LibExeObjStep) !void { const builder = self.builder; - assert(self.is_zig); - - if (self.root_src == null and self.object_files.len == 0 and self.assembly_files.len == 0) { + if (self.root_src == null and self.object_files.len == 0 and + self.assembly_files.len == 0 and self.c_source_files.len == 0) + { warn("{}: linker needs 1 or more objects to link\n", self.step.name); return error.NeedAnObject; } @@ -1220,6 +1151,7 @@ pub const LibExeObjStep = struct { Kind.Lib => "build-lib", Kind.Exe => "build-exe", Kind.Obj => "build-obj", + Kind.Test => "test", }; zig_args.append(cmd) catch unreachable; @@ -1227,6 +1159,14 @@ pub const LibExeObjStep = struct { zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; } + for (self.c_source_files.toSliceConst()) |c_source_file| { + try zig_args.append("--c-source"); + for (c_source_file.args) |arg| { + try zig_args.append(arg); + } + try zig_args.append(self.builder.pathFromRoot(c_source_file.source_path)); + } + if (self.build_options_contents.len() > 0) { const build_options_file = try os.path.join( builder.allocator, @@ -1239,6 +1179,16 @@ pub const LibExeObjStep = struct { try zig_args.append("--pkg-end"); } + if (self.filter) |filter| { + try zig_args.append("--test-filter"); + try zig_args.append(filter); + } + + if (self.name_prefix.len != 0) { + try zig_args.append("--test-name-prefix"); + try zig_args.append(self.name_prefix); + } + for (self.object_files.toSliceConst()) |object_file| { zig_args.append("--object") catch unreachable; zig_args.append(builder.pathFromRoot(object_file)) catch unreachable; @@ -1255,6 +1205,7 @@ pub const LibExeObjStep = struct { if (builder.verbose_ir) zig_args.append("--verbose-ir") catch unreachable; if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; + if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; if (self.strip) { zig_args.append("--strip") catch unreachable; @@ -1293,7 +1244,7 @@ pub const LibExeObjStep = struct { zig_args.append("--ver-patch") catch unreachable; zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; } - if (self.kind == Kind.Exe and self.static) { + if ((self.kind == Kind.Exe or self.kind == Kind.Test) and self.static) { zig_args.append("--static") catch unreachable; } @@ -1325,11 +1276,16 @@ pub const LibExeObjStep = struct { } } - if (!self.disable_libc) { - zig_args.append("--library") catch unreachable; - zig_args.append("c") catch unreachable; + if (self.exec_cmd_args) |exec_cmd_args| { + for (exec_cmd_args) |cmd_arg| { + if (cmd_arg) |arg| { + try zig_args.append("--test-cmd"); + try zig_args.append(arg); + } else { + try zig_args.append("--test-cmd-bin"); + } + } } - for (self.packages.toSliceConst()) |pkg| { zig_args.append("--pkg-begin") catch unreachable; zig_args.append(pkg.name) catch unreachable; @@ -1363,8 +1319,14 @@ pub const LibExeObjStep = struct { } for (self.full_path_libs.toSliceConst()) |full_path_lib| { - zig_args.append("--library") catch unreachable; - zig_args.append(builder.pathFromRoot(full_path_lib)) catch unreachable; + try zig_args.append("--library"); + try zig_args.append(builder.pathFromRoot(full_path_lib)); + + const full_path_lib_abs = builder.pathFromRoot(full_path_lib); + if (os.path.dirname(full_path_lib_abs)) |dirname| { + try zig_args.append("-rpath"); + try zig_args.append(dirname); + } } if (self.target.isDarwin()) { @@ -1379,558 +1341,16 @@ pub const LibExeObjStep = struct { try zig_args.append("--system-linker-hack"); } - try builder.spawnChild(zig_args.toSliceConst()); - - if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); - } - } - - fn appendCompileFlags(self: *LibExeObjStep, args: *ArrayList([]const u8)) void { - if (!self.strip) { - args.append("-g") catch unreachable; - } - switch (self.build_mode) { - builtin.Mode.Debug => { - if (self.disable_libc) { - args.append("-fno-stack-protector") catch unreachable; - } else { - args.append("-fstack-protector-strong") catch unreachable; - args.append("--param") catch unreachable; - args.append("ssp-buffer-size=4") catch unreachable; - } - }, - builtin.Mode.ReleaseSafe => { - args.append("-O2") catch unreachable; - if (self.disable_libc) { - args.append("-fno-stack-protector") catch unreachable; - } else { - args.append("-D_FORTIFY_SOURCE=2") catch unreachable; - args.append("-fstack-protector-strong") catch unreachable; - args.append("--param") catch unreachable; - args.append("ssp-buffer-size=4") catch unreachable; - } - }, - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => { - args.append("-O2") catch unreachable; - args.append("-fno-stack-protector") catch unreachable; - }, - } - - for (self.include_dirs.toSliceConst()) |dir| { - args.append("-I") catch unreachable; - args.append(self.builder.pathFromRoot(dir)) catch unreachable; - } - - for (self.cflags.toSliceConst()) |cflag| { - args.append(cflag) catch unreachable; - } - - if (self.disable_libc) { - args.append("-nostdlib") catch unreachable; - } - } - - fn makeC(self: *LibExeObjStep) !void { - const builder = self.builder; - - const cc = builder.getCCExe(); - - assert(!self.is_zig); - - var cc_args = ArrayList([]const u8).init(builder.allocator); - defer cc_args.deinit(); - - cc_args.append(cc) catch unreachable; - - const is_darwin = self.target.isDarwin(); - - const c_std_arg = switch (self.c_std) { - Builder.CStd.C89 => "-std=c89", - Builder.CStd.C99 => "-std=c99", - Builder.CStd.C11 => "-std=c11", - }; - try cc_args.append(c_std_arg); - - switch (self.kind) { - Kind.Obj => { - cc_args.append("-c") catch unreachable; - cc_args.append(builder.pathFromRoot(self.object_src)) catch unreachable; - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append("-o") catch unreachable; - cc_args.append(output_path) catch unreachable; - - self.appendCompileFlags(&cc_args); - - try builder.spawnChild(cc_args.toSliceConst()); - }, - Kind.Lib => { - for (self.source_files.toSliceConst()) |source_file| { - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - if (!self.static) { - cc_args.append("-fPIC") catch unreachable; - } - - const abs_source_file = builder.pathFromRoot(source_file); - cc_args.append("-c") catch unreachable; - cc_args.append(abs_source_file) catch unreachable; - - const cache_o_src = os.path.join( - builder.allocator, - [][]const u8{ builder.cache_root, source_file }, - ) catch unreachable; - if (os.path.dirname(cache_o_src)) |cache_o_dir| { - try builder.makePath(cache_o_dir); - } - const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt()); - cc_args.append("-o") catch unreachable; - cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable; - - self.appendCompileFlags(&cc_args); - - try builder.spawnChild(cc_args.toSliceConst()); - - self.object_files.append(cache_o_file) catch unreachable; - } - - if (self.static) { - // ar - cc_args.resize(0) catch unreachable; - cc_args.append("ar") catch unreachable; - - cc_args.append("qc") catch unreachable; - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append(output_path) catch unreachable; - - for (self.object_files.toSliceConst()) |object_file| { - cc_args.append(builder.pathFromRoot(object_file)) catch unreachable; - } - - try builder.spawnChild(cc_args.toSliceConst()); - - // ranlib - cc_args.resize(0) catch unreachable; - cc_args.append("ranlib") catch unreachable; - cc_args.append(output_path) catch unreachable; - - try builder.spawnChild(cc_args.toSliceConst()); - } else { - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - if (is_darwin) { - cc_args.append("-dynamiclib") catch unreachable; - - cc_args.append("-Wl,-headerpad_max_install_names") catch unreachable; - - cc_args.append("-compatibility_version") catch unreachable; - cc_args.append(builder.fmt("{}.0.0", self.version.major)) catch unreachable; - - cc_args.append("-current_version") catch unreachable; - cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable; - - const install_name = builder.pathFromRoot(os.path.join( - builder.allocator, - [][]const u8{ builder.cache_root, self.major_only_filename }, - ) catch unreachable); - cc_args.append("-install_name") catch unreachable; - cc_args.append(install_name) catch unreachable; - } else { - cc_args.append("-fPIC") catch unreachable; - cc_args.append("-shared") catch unreachable; - - const soname_arg = builder.fmt("-Wl,-soname,lib{}.so.{d}", self.name, self.version.major); - defer builder.allocator.free(soname_arg); - cc_args.append(soname_arg) catch unreachable; - } - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append("-o") catch unreachable; - cc_args.append(output_path) catch unreachable; - - for (self.object_files.toSliceConst()) |object_file| { - cc_args.append(builder.pathFromRoot(object_file)) catch unreachable; - } - - if (!is_darwin) { - const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc( - builder.allocator, - builder.pathFromRoot(builder.cache_root), - )); - defer builder.allocator.free(rpath_arg); - try cc_args.append(rpath_arg); - - try cc_args.append("-rdynamic"); - } - - for (self.full_path_libs.toSliceConst()) |full_path_lib| { - cc_args.append(builder.pathFromRoot(full_path_lib)) catch unreachable; - } - - { - var it = self.link_libs.iterator(); - while (it.next()) |entry| { - cc_args.append(builder.fmt("-l{}", entry.key)) catch unreachable; - } - } - - if (is_darwin and !self.static) { - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - cc_args.append("-framework") catch unreachable; - cc_args.append(entry.key) catch unreachable; - } - } - - try builder.spawnChild(cc_args.toSliceConst()); - - if (self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); - } - } - }, - Kind.Exe => { - for (self.source_files.toSliceConst()) |source_file| { - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - const abs_source_file = builder.pathFromRoot(source_file); - cc_args.append("-c") catch unreachable; - cc_args.append(abs_source_file) catch unreachable; - - const cache_o_src = os.path.join( - builder.allocator, - [][]const u8{ builder.cache_root, source_file }, - ) catch unreachable; - if (os.path.dirname(cache_o_src)) |cache_o_dir| { - try builder.makePath(cache_o_dir); - } - const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt()); - cc_args.append("-o") catch unreachable; - cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable; - - for (self.cflags.toSliceConst()) |cflag| { - cc_args.append(cflag) catch unreachable; - } - - for (self.include_dirs.toSliceConst()) |dir| { - cc_args.append("-I") catch unreachable; - cc_args.append(builder.pathFromRoot(dir)) catch unreachable; - } - - try builder.spawnChild(cc_args.toSliceConst()); - - self.object_files.append(cache_o_file) catch unreachable; - } - - cc_args.resize(0) catch unreachable; - cc_args.append(cc) catch unreachable; - - for (self.object_files.toSliceConst()) |object_file| { - cc_args.append(builder.pathFromRoot(object_file)) catch unreachable; - } - - const output_path = builder.pathFromRoot(self.getOutputPath()); - cc_args.append("-o") catch unreachable; - cc_args.append(output_path) catch unreachable; - - const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc( - builder.allocator, - builder.pathFromRoot(builder.cache_root), - )); - defer builder.allocator.free(rpath_arg); - try cc_args.append(rpath_arg); - - try cc_args.append("-rdynamic"); - - { - var it = self.link_libs.iterator(); - while (it.next()) |entry| { - cc_args.append(builder.fmt("-l{}", entry.key)) catch unreachable; - } - } - - if (is_darwin) { - if (self.need_flat_namespace_hack) { - cc_args.append("-Wl,-flat_namespace") catch unreachable; - } - cc_args.append("-Wl,-search_paths_first") catch unreachable; - } - - for (self.full_path_libs.toSliceConst()) |full_path_lib| { - cc_args.append(builder.pathFromRoot(full_path_lib)) catch unreachable; - } - - if (is_darwin) { - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - cc_args.append("-framework") catch unreachable; - cc_args.append(entry.key) catch unreachable; - } - } - - try builder.spawnChild(cc_args.toSliceConst()); - }, - } - } -}; - -pub const TestStep = struct { - step: Step, - builder: *Builder, - root_src: []const u8, - build_mode: builtin.Mode, - verbose: bool, - link_libs: BufSet, - name_prefix: []const u8, - filter: ?[]const u8, - target: Target, - exec_cmd_args: ?[]const ?[]const u8, - include_dirs: ArrayList([]const u8), - lib_paths: ArrayList([]const u8), - packages: ArrayList(Pkg), - object_files: ArrayList([]const u8), - output_path: ?[]const u8, - system_linker_hack: bool, - override_std_dir: ?[]const u8, - - pub fn init(builder: *Builder, root_src: []const u8) TestStep { - const step_name = builder.fmt("test {}", root_src); - return TestStep{ - .step = Step.init(step_name, builder.allocator, make), - .builder = builder, - .root_src = root_src, - .build_mode = builtin.Mode.Debug, - .verbose = false, - .name_prefix = "", - .filter = null, - .link_libs = BufSet.init(builder.allocator), - .target = Target{ .Native = {} }, - .exec_cmd_args = null, - .include_dirs = ArrayList([]const u8).init(builder.allocator), - .lib_paths = ArrayList([]const u8).init(builder.allocator), - .packages = ArrayList(Pkg).init(builder.allocator), - .object_files = ArrayList([]const u8).init(builder.allocator), - .output_path = null, - .system_linker_hack = false, - .override_std_dir = null, - }; - } - - pub fn addLibPath(self: *TestStep, path: []const u8) void { - self.lib_paths.append(path) catch unreachable; - } - - pub fn addPackagePath(self: *TestStep, name: []const u8, pkg_index_path: []const u8) void { - self.packages.append(Pkg{ - .name = name, - .path = pkg_index_path, - }) catch unreachable; - } - - pub fn setVerbose(self: *TestStep, value: bool) void { - self.verbose = value; - } - - pub fn addIncludeDir(self: *TestStep, path: []const u8) void { - self.include_dirs.append(path) catch unreachable; - } - - pub fn setBuildMode(self: *TestStep, mode: builtin.Mode) void { - self.build_mode = mode; - } - - pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void { - self.override_std_dir = dir_path; - } - - pub fn setOutputPath(self: *TestStep, file_path: []const u8) void { - self.output_path = file_path; - - // catch a common mistake - if (mem.eql(u8, self.builder.pathFromRoot(file_path), self.builder.pathFromRoot("."))) { - debug.panic("setOutputPath wants a file path, not a directory\n"); - } - } - - pub fn getOutputPath(self: *TestStep) []const u8 { - if (self.output_path) |output_path| { - return output_path; - } else { - const basename = self.builder.fmt("test{}", self.target.exeFileExt()); - return os.path.join( - self.builder.allocator, - [][]const u8{ self.builder.cache_root, basename }, - ) catch unreachable; - } - } - - pub fn linkSystemLibrary(self: *TestStep, name: []const u8) void { - self.link_libs.put(name) catch unreachable; - } - - pub fn setNamePrefix(self: *TestStep, text: []const u8) void { - self.name_prefix = text; - } - - pub fn setFilter(self: *TestStep, text: ?[]const u8) void { - self.filter = text; - } - - pub fn addObject(self: *TestStep, obj: *LibExeObjStep) void { - assert(obj.kind == LibExeObjStep.Kind.Obj); - - self.step.dependOn(&obj.step); - - self.object_files.append(obj.getOutputPath()) catch unreachable; - - // TODO should be some kind of isolated directory that only has this header in it - self.include_dirs.append(self.builder.cache_root) catch unreachable; - } - - pub fn addObjectFile(self: *TestStep, path: []const u8) void { - self.object_files.append(path) catch unreachable; - } - - pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { - self.target = Target{ - .Cross = CrossTarget{ - .arch = target_arch, - .os = target_os, - .environ = target_environ, - }, - }; - } - - pub fn setExecCmd(self: *TestStep, args: []const ?[]const u8) void { - self.exec_cmd_args = args; - } - - pub fn enableSystemLinkerHack(self: *TestStep) void { - self.system_linker_hack = true; - } - - fn make(step: *Step) !void { - const self = @fieldParentPtr(TestStep, "step", step); - const builder = self.builder; - - var zig_args = ArrayList([]const u8).init(builder.allocator); - defer zig_args.deinit(); - - try zig_args.append(builder.zig_exe); - - try zig_args.append("test"); - try zig_args.append(builder.pathFromRoot(self.root_src)); - - if (self.verbose) { - try zig_args.append("--verbose"); - } - - switch (self.build_mode) { - builtin.Mode.Debug => {}, - builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"), - builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"), - builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"), - } - - const output_path = builder.pathFromRoot(self.getOutputPath()); - try zig_args.append("--output"); - try zig_args.append(output_path); - - switch (self.target) { - Target.Native => {}, - Target.Cross => |cross_target| { - try zig_args.append("--target-arch"); - try zig_args.append(@tagName(cross_target.arch)); - - try zig_args.append("--target-os"); - try zig_args.append(@tagName(cross_target.os)); - - try zig_args.append("--target-environ"); - try zig_args.append(@tagName(cross_target.environ)); - }, - } - - if (self.filter) |filter| { - try zig_args.append("--test-filter"); - try zig_args.append(filter); - } - - if (self.name_prefix.len != 0) { - try zig_args.append("--test-name-prefix"); - try zig_args.append(self.name_prefix); - } - - for (self.object_files.toSliceConst()) |object_file| { - try zig_args.append("--object"); - try zig_args.append(builder.pathFromRoot(object_file)); - } - - { - var it = self.link_libs.iterator(); - while (true) { - const entry = it.next() orelse break; - try zig_args.append("--library"); - try zig_args.append(entry.key); - } - } - - if (self.exec_cmd_args) |exec_cmd_args| { - for (exec_cmd_args) |cmd_arg| { - if (cmd_arg) |arg| { - try zig_args.append("--test-cmd"); - try zig_args.append(arg); - } else { - try zig_args.append("--test-cmd-bin"); - } - } - } - - for (self.include_dirs.toSliceConst()) |include_path| { - try zig_args.append("-isystem"); - try zig_args.append(builder.pathFromRoot(include_path)); - } - - for (builder.include_paths.toSliceConst()) |include_path| { - try zig_args.append("-isystem"); - try zig_args.append(builder.pathFromRoot(include_path)); - } - - for (builder.rpaths.toSliceConst()) |rpath| { - try zig_args.append("-rpath"); - try zig_args.append(rpath); - } - - for (self.lib_paths.toSliceConst()) |lib_path| { - try zig_args.append("--library-path"); - try zig_args.append(lib_path); - } - - for (builder.lib_paths.toSliceConst()) |lib_path| { - try zig_args.append("--library-path"); - try zig_args.append(lib_path); - } - - for (self.packages.toSliceConst()) |pkg| { - zig_args.append("--pkg-begin") catch unreachable; - zig_args.append(pkg.name) catch unreachable; - zig_args.append(builder.pathFromRoot(pkg.path)) catch unreachable; - zig_args.append("--pkg-end") catch unreachable; - } - - if (self.system_linker_hack) { - try zig_args.append("--system-linker-hack"); - } if (self.override_std_dir) |dir| { try zig_args.append("--override-std-dir"); try zig_args.append(builder.pathFromRoot(dir)); } try builder.spawnChild(zig_args.toSliceConst()); + + if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { + try doAtomicSymLinks(builder.allocator, output_path, self.major_only_filename, self.name_only_filename); + } } }; @@ -1976,6 +1396,7 @@ const InstallArtifactStep = struct { pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { const dest_dir = switch (artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, + LibExeObjStep.Kind.Test => unreachable, LibExeObjStep.Kind.Exe => builder.exe_dir, LibExeObjStep.Kind.Lib => builder.lib_dir, }; @@ -2012,6 +1433,7 @@ const InstallArtifactStep = struct { builtin.Os.windows => {}, else => switch (self.artifact.kind) { LibExeObjStep.Kind.Obj => unreachable, + LibExeObjStep.Kind.Test => unreachable, LibExeObjStep.Kind.Exe => u32(0o755), LibExeObjStep.Kind.Lib => if (self.artifact.static) u32(0o666) else u32(0o755), }, diff --git a/test/stage1/c_abi/build.zig b/test/stage1/c_abi/build.zig index e0a108aeb5..f3d5c64c00 100644 --- a/test/stage1/c_abi/build.zig +++ b/test/stage1/c_abi/build.zig @@ -3,9 +3,10 @@ const Builder = @import("std").build.Builder; pub fn build(b: *Builder) void { const rel_opts = b.standardReleaseOptions(); - const c_obj = b.addCObject("cfuncs", "cfuncs.c"); + const c_obj = b.addObject("cfuncs", null); + c_obj.addCSourceFile("cfuncs.c", [][]const u8{"-std=c99"}); c_obj.setBuildMode(rel_opts); - c_obj.setNoStdLib(true); + c_obj.linkSystemLibrary("c"); const main = b.addTest("main.zig"); main.setBuildMode(rel_opts); From 525c2eaf5d49f537d4ccd48ab0c5bc1f52cc3204 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 13:34:25 -0500 Subject: [PATCH 104/134] building DLLs on Windows works better --- src/all_types.hpp | 1 + src/codegen.cpp | 6 +++++- src/codegen.hpp | 1 + src/link.cpp | 1 + src/main.cpp | 6 ++++++ src/target.cpp | 6 ++++++ src/target.hpp | 1 + std/build.zig | 45 +++++++++++++++++++++++++++++++++++++++-- test/build_examples.zig | 8 ++------ 9 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index baa0267238..8099f99f48 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1743,6 +1743,7 @@ struct CodeGen { Buf triple_str; Buf global_asm; Buf *out_h_path; + Buf *out_lib_path; Buf artifact_dir; Buf output_file_path; Buf o_file_output_path; diff --git a/src/codegen.cpp b/src/codegen.cpp index f14749bb28..3edff93be8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -175,6 +175,10 @@ void codegen_set_output_h_path(CodeGen *g, Buf *h_path) { g->out_h_path = h_path; } +void codegen_set_output_lib_path(CodeGen *g, Buf *lib_path) { + g->out_lib_path = lib_path; +} + void codegen_set_output_path(CodeGen *g, Buf *path) { g->wanted_output_file_path = path; } @@ -8201,7 +8205,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { args.append("-c"); args.append(buf_ptr(c_source_file)); - if (!g->disable_pic) { + if (!g->disable_pic && target_supports_fpic(g->zig_target)) { args.append("-fPIC"); } diff --git a/src/codegen.hpp b/src/codegen.hpp index 035b759ec5..4f62cc4cbc 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -41,6 +41,7 @@ void codegen_set_test_filter(CodeGen *g, Buf *filter); void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch); void codegen_set_output_h_path(CodeGen *g, Buf *h_path); +void codegen_set_output_lib_path(CodeGen *g, Buf *lib_path); void codegen_set_output_path(CodeGen *g, Buf *path); void codegen_add_time_event(CodeGen *g, const char *name); void codegen_print_timing_report(CodeGen *g, FILE *f); diff --git a/src/link.cpp b/src/link.cpp index 2068b8efb0..5f611ae5a6 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -554,6 +554,7 @@ static void construct_linker_job_coff(LinkJob *lj) { bool is_library = g->out_type == OutTypeLib; switch (g->subsystem) { case TargetSubsystemAuto: + add_nt_link_args(lj, is_library); break; case TargetSubsystemConsole: lj->args.append("/SUBSYSTEM:console"); diff --git a/src/main.cpp b/src/main.cpp index a7f6e0b95d..1c154250ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,6 +60,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --name [name] override output name\n" " --output [file] override destination path\n" " --output-h [file] generate header file\n" + " --output-lib [file] override import library path\n" " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" " --pkg-end pop current pkg\n" " --release-fast build with optimizations on and safety off\n" @@ -375,6 +376,7 @@ int main(int argc, char **argv) { const char *in_file = nullptr; const char *out_file = nullptr; const char *out_file_h = nullptr; + const char *out_file_lib = nullptr; bool strip = false; bool is_static = false; OutType out_type = OutTypeUnknown; @@ -661,6 +663,8 @@ int main(int argc, char **argv) { out_file = argv[i]; } else if (strcmp(arg, "--output-h") == 0) { out_file_h = argv[i]; + } else if (strcmp(arg, "--output-lib") == 0) { + out_file_lib = argv[i]; } else if (strcmp(arg, "--color") == 0) { if (strcmp(argv[i], "auto") == 0) { color = ErrColorAuto; @@ -1094,6 +1098,8 @@ int main(int argc, char **argv) { codegen_set_output_path(g, buf_create_from_str(out_file)); if (out_file_h != nullptr && (out_type == OutTypeObj || out_type == OutTypeLib)) codegen_set_output_h_path(g, buf_create_from_str(out_file_h)); + if (out_file_lib != nullptr && out_type == OutTypeLib && !is_static) + codegen_set_output_lib_path(g, buf_create_from_str(out_file_lib)); add_package(g, cur_pkg, g->root_package); diff --git a/src/target.cpp b/src/target.cpp index 01e8b90cea..49a1742945 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1049,3 +1049,9 @@ bool target_requires_libc(const ZigTarget *target) { // since this is the stable syscall interface. return (target_is_darwin(target) || target->os == OsFreeBSD || target->os == OsNetBSD); } + +bool target_supports_fpic(const ZigTarget *target) { + // This is not whether the target supports Position Independent Code, but whether the -fPIC + // C compiler argument is valid. + return target->os != OsWindows; +} diff --git a/src/target.hpp b/src/target.hpp index 620c9b2664..dceeee2eca 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -141,5 +141,6 @@ bool target_allows_addr_zero(const ZigTarget *target); bool target_has_valgrind_support(const ZigTarget *target); bool target_is_darwin(const ZigTarget *target); bool target_requires_libc(const ZigTarget *target); +bool target_supports_fpic(const ZigTarget *target); #endif diff --git a/std/build.zig b/std/build.zig index 4386b730cd..0d03b0325f 100644 --- a/std/build.zig +++ b/std/build.zig @@ -815,6 +815,7 @@ pub const LibExeObjStep = struct { linker_script: ?[]const u8, out_filename: []const u8, output_path: ?[]const u8, + output_lib_path: ?[]const u8, static: bool, version: Version, object_files: ArrayList([]const u8), @@ -839,6 +840,7 @@ pub const LibExeObjStep = struct { root_src: ?[]const u8, output_h_path: ?[]const u8, out_h_filename: []const u8, + out_lib_filename: []const u8, assembly_files: ArrayList([]const u8), packages: ArrayList(Pkg), build_options_contents: std.Buffer, @@ -901,10 +903,12 @@ pub const LibExeObjStep = struct { .frameworks = BufSet.init(builder.allocator), .step = Step.init(name, builder.allocator, make), .output_path = null, + .output_lib_path = null, .output_h_path = null, .version = ver, .out_filename = undefined, .out_h_filename = builder.fmt("{}.h", name), + .out_lib_filename = undefined, .major_only_filename = undefined, .name_only_filename = undefined, .object_files = ArrayList([]const u8).init(builder.allocator), @@ -941,21 +945,32 @@ pub const LibExeObjStep = struct { }, Kind.Lib => { if (self.static) { - self.out_filename = self.builder.fmt("lib{}.a", self.name); + switch (self.target.getOs()) { + builtin.Os.windows => { + self.out_filename = self.builder.fmt("{}.lib", self.name); + }, + else => { + self.out_filename = self.builder.fmt("lib{}.a", self.name); + }, + } + self.out_lib_filename = self.out_filename; } else { switch (self.target.getOs()) { builtin.Os.ios, builtin.Os.macosx => { self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); + self.out_lib_filename = self.out_filename; }, builtin.Os.windows => { self.out_filename = self.builder.fmt("{}.dll", self.name); + self.out_lib_filename = self.builder.fmt("{}.lib", self.name); }, else => { self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", self.name, self.version.major, self.version.minor, self.version.patch); self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); self.name_only_filename = self.builder.fmt("lib{}.so", self.name); + self.out_lib_filename = self.out_filename; }, } } @@ -990,7 +1005,11 @@ pub const LibExeObjStep = struct { self.step.dependOn(&lib.step); - self.full_path_libs.append(lib.getOutputPath()) catch unreachable; + if (lib.static or self.target.isWindows()) { + self.object_files.append(lib.getOutputLibPath()) catch unreachable; + } else { + self.full_path_libs.append(lib.getOutputPath()) catch unreachable; + } // TODO should be some kind of isolated directory that only has this header in it self.include_dirs.append(self.builder.cache_root) catch unreachable; @@ -1060,6 +1079,22 @@ pub const LibExeObjStep = struct { ) catch unreachable; } + pub fn setOutputLibPath(self: *LibExeObjStep, file_path: []const u8) void { + assert(self.kind == Kind.Lib); + if (self.static) + return self.setOutputPath(file_path); + + self.output_lib_path = file_path; + } + + pub fn getOutputLibPath(self: *LibExeObjStep) []const u8 { + assert(self.kind == Kind.Lib); + return if (self.output_lib_path) |output_lib_path| output_lib_path else os.path.join( + self.builder.allocator, + [][]const u8{ self.builder.cache_root, self.out_lib_filename }, + ) catch unreachable; + } + pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void { self.output_h_path = file_path; @@ -1225,6 +1260,12 @@ pub const LibExeObjStep = struct { zig_args.append("--output") catch unreachable; zig_args.append(output_path) catch unreachable; + if (self.kind == Kind.Lib and !self.static) { + const output_lib_path = builder.pathFromRoot(self.getOutputLibPath()); + zig_args.append("--output-lib") catch unreachable; + zig_args.append(output_lib_path) catch unreachable; + } + if (self.kind != Kind.Exe) { const output_h_path = self.getOutputHPath(); zig_args.append("--output-h") catch unreachable; diff --git a/test/build_examples.zig b/test/build_examples.zig index 66a6ecda17..c8a47bb093 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -7,12 +7,8 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { cases.addC("example/hello_world/hello_libc.zig"); cases.add("example/cat/main.zig"); cases.add("example/guess_number/main.zig"); - if (!is_windows) { - // TODO get this test passing on windows - // See https://github.com/ziglang/zig/issues/538 - cases.addBuildFile("example/shared_library/build.zig"); - cases.addBuildFile("example/mix_o_files/build.zig"); - } + cases.addBuildFile("example/shared_library/build.zig"); + cases.addBuildFile("example/mix_o_files/build.zig"); if (builtin.os != builtin.Os.macosx) { // TODO https://github.com/ziglang/zig/issues/1126 cases.addBuildFile("test/standalone/issue_339/build.zig"); From 0d4db8828a9efc05b5c3622098a8337de0b62d1e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 14:03:36 -0500 Subject: [PATCH 105/134] `@cImport` works with `--cache on` We pass -MD -MF args to clang when doing `@cImport`, which gives us a complete list of files that the C code read from. Then we add these to the cache. So even when using `@cImport` Zig's caching system remains perfect. This is a proof of concept for the mechanism that the self-hosted compiler will use to watch and rebuild files. --- src/cache_hash.cpp | 34 ++++++++++++++++++++++++++++++++++ src/cache_hash.hpp | 2 ++ src/codegen.cpp | 31 ++++++------------------------- src/error.cpp | 1 + src/error.hpp | 1 + src/ir.cpp | 6 ------ src/os.cpp | 12 ++++++++++++ src/os.hpp | 1 + src/translate_c.cpp | 20 ++++++++++++++++++++ 9 files changed, 77 insertions(+), 31 deletions(-) diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp index 4526a83c27..85bad3dd2d 100644 --- a/src/cache_hash.cpp +++ b/src/cache_hash.cpp @@ -414,6 +414,39 @@ Error cache_add_file(CacheHash *ch, Buf *path) { return cache_add_file_fetch(ch, resolved_path, nullptr); } +Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) { + Error err; + Buf *contents = buf_alloc(); + if ((err = os_fetch_file_path(dep_file_path, contents, false))) { + if (verbose) { + fprintf(stderr, "unable to read .d file: %s\n", err_str(err)); + } + return ErrorReadingDepFile; + } + SplitIterator it = memSplit(buf_to_slice(contents), str("\n")); + // skip first line + SplitIterator_next(&it); + for (;;) { + Optional> opt_line = SplitIterator_next(&it); + if (!opt_line.is_some) + break; + if (opt_line.value.len == 0) + continue; + SplitIterator line_it = memSplit(opt_line.value, str(" \t")); + Slice filename; + if (!SplitIterator_next(&line_it).unwrap(&filename)) + continue; + Buf *filename_buf = buf_create_from_slice(filename); + if ((err = cache_add_file(ch, filename_buf))) { + if (verbose) { + fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(filename_buf), err_str(err)); + } + return err; + } + } + return ErrorNone; +} + static Error write_manifest_file(CacheHash *ch) { Error err; Buf contents = BUF_INIT; @@ -464,3 +497,4 @@ void cache_release(CacheHash *ch) { os_file_close(ch->manifest_file); } + diff --git a/src/cache_hash.hpp b/src/cache_hash.hpp index db1c42ec03..d74c8623ce 100644 --- a/src/cache_hash.hpp +++ b/src/cache_hash.hpp @@ -56,6 +56,8 @@ Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest); // If you did not get a cache hit, call this function for every file // that is depended on, and then finish with cache_final. Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path); +// This opens a file created by -MD -MF args to Clang +Error ATTRIBUTE_MUST_USE cache_add_dep_file(CacheHash *ch, Buf *path, bool verbose); // This variant of cache_add_file returns the file contents. // Also the file path argument must be already resolved. diff --git a/src/codegen.cpp b/src/codegen.cpp index 3edff93be8..4a8b376e2b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8218,8 +8218,9 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { } if (g->verbose_cc) { + fprintf(stderr, "zig"); for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { - fprintf(stderr, "%s ", args.at(arg_i)); + fprintf(stderr, " %s", args.at(arg_i)); } fprintf(stderr, "\n"); } @@ -8232,35 +8233,15 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { g->link_objects.append(out_obj_path); - // add the files depended on to the cache system if (g->enable_cache) { - Buf *contents = buf_alloc(); - if ((err = os_fetch_file_path(out_dep_path, contents, false))) { - fprintf(stderr, "unable to read .d file: %s\n", err_str(err)); - exit(1); - } + // add the files depended on to the cache system if ((err = cache_add_file(&g->cache_hash, c_source_file))) { fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(c_source_file), err_str(err)); exit(1); } - SplitIterator it = memSplit(buf_to_slice(contents), str("\n")); - // skip first line - SplitIterator_next(&it); - for (;;) { - Optional> opt_line = SplitIterator_next(&it); - if (!opt_line.is_some) - break; - if (opt_line.value.len == 0) - continue; - SplitIterator line_it = memSplit(opt_line.value, str(" \t")); - Slice filename; - if (!SplitIterator_next(&line_it).unwrap(&filename)) - continue; - Buf *filename_buf = buf_create_from_slice(filename); - if ((err = cache_add_file(&g->cache_hash, filename_buf))) { - fprintf(stderr, "unable to add %s to cache: %s\n", buf_ptr(c_source_file), err_str(err)); - exit(1); - } + if ((err = cache_add_dep_file(&g->cache_hash, out_dep_path, true))) { + fprintf(stderr, "failed to add C source dependencies to cache: %s\n", err_str(err)); + exit(1); } } } diff --git a/src/error.cpp b/src/error.cpp index 9e01a86d8c..c81cfd1683 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -36,6 +36,7 @@ const char *err_str(Error err) { case ErrorCacheUnavailable: return "cache unavailable"; case ErrorPathTooLong: return "path too long"; case ErrorCCompilerCannotFindFile: return "C compiler cannot find file"; + case ErrorReadingDepFile: return "failed to read .d file"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 0e14f37a69..a580da547f 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -38,6 +38,7 @@ enum Error { ErrorCacheUnavailable, ErrorPathTooLong, ErrorCCompilerCannotFindFile, + ErrorReadingDepFile, }; const char *err_str(Error err); diff --git a/src/ir.cpp b/src/ir.cpp index ea2fcb289f..929f195586 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18670,12 +18670,6 @@ static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstruc } static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { - if (ira->codegen->enable_cache) { - ir_add_error(ira, &instruction->base, - buf_sprintf("TODO @cImport is incompatible with --cache on. The cache system currently is unable to detect subsequent changes in .h files.")); - return ira->codegen->invalid_instruction; - } - AstNode *node = instruction->base.source_node; assert(node->type == NodeTypeFnCallExpr); AstNode *block_node = node->data.fn_call_expr.params.at(0); diff --git a/src/os.cpp b/src/os.cpp index 95febca9bc..f52325af00 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1232,6 +1232,18 @@ static Error os_buf_to_tmp_file_posix(Buf *contents, Buf *suffix, Buf *out_tmp_p } #endif +Buf *os_tmp_filename(Buf *prefix, Buf *suffix) { + Buf *result = buf_create_from_buf(prefix); + + const char base64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; + assert(array_length(base64) == 64 + 1); + for (size_t i = 0; i < 12; i += 1) { + buf_append_char(result, base64[rand() % 64]); + } + buf_append_buf(result, suffix); + return result; +} + #if defined(ZIG_OS_WINDOWS) static Error os_buf_to_tmp_file_windows(Buf *contents, Buf *suffix, Buf *out_tmp_path) { char tmp_dir[MAX_PATH + 1]; diff --git a/src/os.hpp b/src/os.hpp index db357c22a2..c4fae1d62b 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -121,6 +121,7 @@ Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd); bool os_stderr_tty(void); void os_stderr_set_color(TermColor color); +Buf *os_tmp_filename(Buf *prefix, Buf *suffix); Error os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); Error os_delete_file(Buf *path); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 3fcdf139f6..c80bc6d726 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4776,6 +4776,15 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-x"); clang_argv.append("c"); + Buf *out_dep_path = nullptr; + if (codegen->enable_cache) { + Buf *prefix = buf_sprintf("%s" OS_SEP, buf_ptr(&codegen->cache_dir)); + out_dep_path = os_tmp_filename(prefix, buf_create_from_str(".d")); + clang_argv.append("-MD"); + clang_argv.append("-MF"); + clang_argv.append(buf_ptr(out_dep_path)); + } + if (c->codegen->zig_target->is_native) { char *ZIG_PARSEC_CFLAGS = getenv("ZIG_NATIVE_PARSEC_CFLAGS"); if (ZIG_PARSEC_CFLAGS) { @@ -4912,6 +4921,17 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const return ErrorCCompileErrors; } + if (codegen->enable_cache) { + Error err; + assert(out_dep_path != nullptr); + if ((err = cache_add_dep_file(&codegen->cache_hash, out_dep_path, codegen->verbose_cimport))) { + if (codegen->verbose_cimport) { + fprintf(stderr, "translate-c: aborting due to failed cache operation: %s\n", err_str(err)); + } + return err; + } + } + c->ctx = ZigClangASTUnit_getASTContext(ast_unit); c->source_manager = ZigClangASTUnit_getSourceManager(ast_unit); c->root = trans_create_node(c, NodeTypeContainerDecl); From 33cbb29def181ee758b1f8bb93d8d81a4e377ce1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 14:27:03 -0500 Subject: [PATCH 106/134] zig build: 2 improvements * `linkLibrary` will make the target depend on libc if the source does * when building C source file(s), don't try to generate .h files --- std/build.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/std/build.zig b/std/build.zig index 0d03b0325f..d88a9b3270 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1011,6 +1011,10 @@ pub const LibExeObjStep = struct { self.full_path_libs.append(lib.getOutputPath()) catch unreachable; } + if (lib.link_libs.exists("c")) { + self.link_libs.put("c") catch unreachable; + } + // TODO should be some kind of isolated directory that only has this header in it self.include_dirs.append(self.builder.cache_root) catch unreachable; self.need_flat_namespace_hack = true; @@ -1266,7 +1270,7 @@ pub const LibExeObjStep = struct { zig_args.append(output_lib_path) catch unreachable; } - if (self.kind != Kind.Exe) { + if (self.kind != Kind.Exe and self.root_src != null) { const output_h_path = self.getOutputHPath(); zig_args.append("--output-h") catch unreachable; zig_args.append(builder.pathFromRoot(output_h_path)) catch unreachable; From 7b8c5578c6c174893c5dd08e89cf3b3492ae065f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 16:28:23 -0500 Subject: [PATCH 107/134] fix infinite recursion in type_has_one_possible_value closes #2006 --- src/all_types.hpp | 14 +++++++++++--- src/analyze.cpp | 14 ++++++++++++-- src/analyze.hpp | 5 ----- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/2006.zig | 12 ++++++++++++ 5 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 test/stage1/behavior/bugs/2006.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 8099f99f48..44c1cc0956 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1240,6 +1240,12 @@ enum ZigTypeId { ZigTypeIdVector, }; +enum OnePossibleValue { + OnePossibleValueInvalid, + OnePossibleValueNo, + OnePossibleValueYes, +}; + struct ZigType { ZigTypeId id; Buf name; @@ -1247,9 +1253,6 @@ struct ZigType { LLVMTypeRef type_ref; ZigLLVMDIType *di_type; - bool zero_bits; // this is denormalized data - bool gen_h_loop_flag; - union { ZigTypePointer pointer; ZigTypeInt integral; @@ -1275,6 +1278,11 @@ struct ZigType { // If we generate a constant name value for this type, we memoize it here. // The type of this is array ConstExprValue *cached_const_name_val; + + OnePossibleValue one_possible_value; + + bool zero_bits; // this is denormalized data + bool gen_h_loop_flag; }; struct PackageTableEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index 0ecff11142..5d9b0df8ee 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5129,6 +5129,10 @@ bool type_has_bits(ZigType *type_entry) { // Whether you can infer the value based solely on the type. OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { assert(type_entry != nullptr); + + if (type_entry->one_possible_value != OnePossibleValueInvalid) + return type_entry->one_possible_value; + Error err; if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) return OnePossibleValueInvalid; @@ -5176,8 +5180,14 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdInt: case ZigTypeIdVector: return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; - case ZigTypeIdPointer: - return type_has_one_possible_value(g, type_entry->data.pointer.child_type); + case ZigTypeIdPointer: { + ZigType *elem_type = type_entry->data.pointer.child_type; + // If the recursive function call asks, then we are not one possible value. + type_entry->one_possible_value = OnePossibleValueNo; + // Now update it to be the value of the recursive call. + type_entry->one_possible_value = type_has_one_possible_value(g, elem_type); + return type_entry->one_possible_value; + } case ZigTypeIdUnion: if (type_entry->data.unionation.src_field_count > 1) return OnePossibleValueNo; diff --git a/src/analyze.hpp b/src/analyze.hpp index de4785fb27..e8838ae57e 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -226,11 +226,6 @@ enum ReqCompTime { }; ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry); -enum OnePossibleValue { - OnePossibleValueInvalid, - OnePossibleValueNo, - OnePossibleValueYes, -}; OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry); Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index f7fc1c42a4..d18f127f92 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -19,6 +19,7 @@ comptime { _ = @import("behavior/bugs/1442.zig"); _ = @import("behavior/bugs/1486.zig"); _ = @import("behavior/bugs/1851.zig"); + _ = @import("behavior/bugs/2006.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/655.zig"); diff --git a/test/stage1/behavior/bugs/2006.zig b/test/stage1/behavior/bugs/2006.zig new file mode 100644 index 0000000000..dad78a9ba0 --- /dev/null +++ b/test/stage1/behavior/bugs/2006.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const expect = std.testing.expect; + +const S = struct { + p: *S, +}; +test "bug 2006" { + var a: S = undefined; + a = S{ .p = undefined }; + expect(@sizeOf(S) != 0); + expect(@sizeOf(*void) == 0); +} From 39605a79656802c1c0986286910b1536ac7a25a9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 16:36:18 -0500 Subject: [PATCH 108/134] delete incorrect TODO comment The code path is tested by behavior tests. Thanks to various contributors, but mostly Marc Tiehuis, bigint.cpp now has full coverage. closes #405 --- src/bigint.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bigint.cpp b/src/bigint.cpp index 7299f2379c..4c103adce3 100644 --- a/src/bigint.cpp +++ b/src/bigint.cpp @@ -1665,7 +1665,6 @@ int64_t bigint_as_signed(const BigInt *bigint) { return 0; } else if (bigint->digit_count == 1) { if (bigint->is_negative) { - // TODO this code path is untested if (bigint->data.digit <= 9223372036854775808ULL) { return (-((int64_t)(bigint->data.digit - 1))) - 1; } else { From aab8e13529cc558e0be48c57fda0e22b67b9be39 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 18:31:37 -0500 Subject: [PATCH 109/134] add docs for zero bit types and pointers to zero bit types closes #1561 --- doc/langref.html.in | 55 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index b0c3a9e914..7c1a1a81d3 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1824,7 +1824,7 @@ fn foo(bytes: []u8) u32 { } {#code_end#} {#header_close#} - {#see_also|C Pointers#} + {#see_also|C Pointers|Pointers to Zero Bit Types#} {#header_close#} {#header_open|Slices#} @@ -4464,9 +4464,20 @@ fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { {#header_close#} {#header_close#} - {#header_open|void#} + {#header_open|Zero Bit Types#} +

    For some types, {#link|@sizeOf#} is 0:

    +
      +
    • {#link|void#}
    • +
    • The {#link|Integers#} {#syntax#}u0{#endsyntax#} and {#syntax#}i0{#endsyntax#}.
    • +
    • {#link|Arrays#} and {#link|Vectors#} with len 0, or with an element type that is a zero bit type.
    • +
    • An {#link|enum#} with only 1 tag.
    • +
    • An {#link|struct#} with all fields being zero bit types.
    • +
    • A {#link|union#} with only 1 field which is a zero bit type.
    • +
    • {#link|Pointers to Zero Bit Types#} are themselves zero bit types.
    • +

    - {#syntax#}void{#endsyntax#} represents a type that has no value. Code that makes use of void values is + These types can only ever have one possible value, and thus + require 0 bits to represent. Code that makes use of these types is not included in the final generated code:

    {#code_begin|syntax#} @@ -4476,8 +4487,8 @@ export fn entry() void { x = y; } {#code_end#} -

    When this turns into LLVM IR, there is no code generated in the body of {#syntax#}entry{#endsyntax#}, - even in debug mode. For example, on x86_64:

    +

    When this turns into machine code, there is no code generated in the + body of {#syntax#}entry{#endsyntax#}, even in {#link|Debug#} mode. For example, on x86_64:

    0000000000000010 <entry>:
       10:	55                   	push   %rbp
       11:	48 89 e5             	mov    %rsp,%rbp
    @@ -4485,6 +4496,8 @@ export fn entry() void {
       15:	c3                   	retq   

    These assembly instructions do not have any code associated with the void values - they only perform the function call prologue and epilog.

    + + {#header_open|void#}

    {#syntax#}void{#endsyntax#} can be useful for instantiating generic types. For example, given a {#syntax#}Map(Key, Value){#endsyntax#}, one can pass {#syntax#}void{#endsyntax#} for the {#syntax#}Value{#endsyntax#} @@ -4556,6 +4569,38 @@ fn foo() i32 { {#code_end#} {#header_close#} + {#header_open|Pointers to Zero Bit Types#} +

    Pointers to zero bit types also have zero bits. They always compare equal to each other:

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +test "pointer to empty struct" { + const Empty = struct {}; + var a = Empty{}; + var b = Empty{}; + var ptr_a = &a; + var ptr_b = &b; + comptime assert(ptr_a == ptr_b); +} + {#code_end#} +

    The type being pointed to can only ever be one value; therefore loads and stores are + never generated. {#link|ptrToInt#} and {#link|intToPtr#} are not allowed:

    + {#code_begin|test_err#} +const Empty = struct {}; + +test "@ptrToInt for pointer to zero bit type" { + var a = Empty{}; + _ = @ptrToInt(&a); +} + +test "@intToPtr for pointer to zero bit type" { + _ = @intToPtr(*Empty, 0x1); +} + {#code_end#} + {#header_close#} + {#header_close#} + {#header_open|comptime#}

    Zig places importance on the concept of whether an expression is known at compile-time. From 0f54728cf0e282ca6578bb8a55df3409541c1a7f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 19:10:48 -0500 Subject: [PATCH 110/134] fix not finding libgcc_s when looking for native libc closes #2011 --- src/codegen.cpp | 1 + src/libc_installation.cpp | 60 +++++++++++++++++++++++++++------------ src/libc_installation.hpp | 1 + src/link.cpp | 19 ++++++++----- 4 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4a8b376e2b..99fffa4494 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8815,6 +8815,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); if (g->libc) { cache_buf(ch, &g->libc->include_dir); + cache_buf(ch, &g->libc->crt_dir); cache_buf(ch, &g->libc->lib_dir); cache_buf(ch, &g->libc->static_lib_dir); cache_buf(ch, &g->libc->msvc_lib_dir); diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 9228ea95b6..04459d78e5 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -10,10 +10,9 @@ #include "windows_sdk.h" #include "target.hpp" -static const size_t zig_libc_keys_len = 6; - static const char *zig_libc_keys[] = { "include_dir", + "crt_dir", "lib_dir", "static_lib_dir", "msvc_lib_dir", @@ -21,6 +20,8 @@ static const char *zig_libc_keys[] = { "dynamic_linker_path", }; +static const size_t zig_libc_keys_len = array_length(zig_libc_keys); + static bool zig_libc_match_key(Slice name, Slice value, bool *found_keys, size_t index, Buf *field_ptr) { @@ -33,6 +34,7 @@ static bool zig_libc_match_key(Slice name, Slice value, bool * static void zig_libc_init_empty(ZigLibCInstallation *libc) { *libc = {}; buf_init_from_str(&libc->include_dir, ""); + buf_init_from_str(&libc->crt_dir, ""); buf_init_from_str(&libc->lib_dir, ""); buf_init_from_str(&libc->static_lib_dir, ""); buf_init_from_str(&libc->msvc_lib_dir, ""); @@ -44,7 +46,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget Error err; zig_libc_init_empty(libc); - bool found_keys[6] = {}; // zig_libc_keys_len + bool found_keys[array_length(zig_libc_keys)] = {}; // zig_libc_keys_len Buf *contents = buf_alloc(); if ((err = os_fetch_file_path(libc_file, contents, false))) { @@ -74,11 +76,12 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget Slice value = SplitIterator_rest(&line_it); bool match = false; match = match || zig_libc_match_key(name, value, found_keys, 0, &libc->include_dir); - match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->static_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->msvc_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->kernel32_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->dynamic_linker_path); + match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->crt_dir); + match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->static_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->msvc_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->kernel32_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 6, &libc->dynamic_linker_path); } for (size_t i = 0; i < zig_libc_keys_len; i += 1) { @@ -97,8 +100,17 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget return ErrorSemanticAnalyzeFail; } - if (buf_len(&libc->lib_dir) == 0) { + if (buf_len(&libc->crt_dir) == 0) { if (!target_is_darwin(target)) { + if (verbose) { + fprintf(stderr, "crt_dir may not be empty for %s\n", get_target_os_name(target->os)); + } + return ErrorSemanticAnalyzeFail; + } + } + + if (buf_len(&libc->lib_dir) == 0) { + if (!target_is_darwin(target) && target->os != OsWindows) { if (verbose) { fprintf(stderr, "lib_dir may not be empty for %s\n", get_target_os_name(target->os)); } @@ -134,7 +146,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget } if (buf_len(&libc->dynamic_linker_path) == 0) { - if (target->os == OsLinux) { + if (!target_is_darwin(target) && target->os != OsWindows) { if (verbose) { fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", get_target_os_name(target->os)); } @@ -156,11 +168,11 @@ static Error zig_libc_find_native_include_dir_windows(ZigLibCInstallation *self, } return ErrorNone; } -static Error zig_libc_find_lib_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target, +static Error zig_libc_find_crt_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target, bool verbose) { Error err; - if ((err = os_get_win32_ucrt_lib_path(sdk, &self->lib_dir, target->arch.arch))) { + if ((err = os_get_win32_ucrt_lib_path(sdk, &self->crt_dir, target->arch.arch))) { if (verbose) { fprintf(stderr, "Unable to determine ucrt path: %s\n", err_str(err)); } @@ -291,8 +303,11 @@ static Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want } return ErrorNone; } +static Error zig_libc_find_native_crt_dir_posix(ZigLibCInstallation *self, bool verbose) { + return zig_libc_cc_print_file_name("crt1.o", &self->crt_dir, true, verbose); +} static Error zig_libc_find_native_lib_dir_posix(ZigLibCInstallation *self, bool verbose) { - return zig_libc_cc_print_file_name("crt1.o", &self->lib_dir, true, verbose); + return zig_libc_cc_print_file_name("libgcc_s.so", &self->lib_dir, true, verbose); } static Error zig_libc_find_native_static_lib_dir_posix(ZigLibCInstallation *self, bool verbose) { @@ -328,16 +343,21 @@ static Error zig_libc_find_native_dynamic_linker_posix(ZigLibCInstallation *self void zig_libc_render(ZigLibCInstallation *self, FILE *file) { fprintf(file, "# The directory that contains `stdlib.h`.\n" - "# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`\n" + "# On POSIX, can be found with: `cc -E -Wp,-v -xc /dev/null`\n" "include_dir=%s\n" "\n" "# The directory that contains `crt1.o`.\n" - "# On Linux, can be found with `cc -print-file-name=crt1.o`.\n" + "# On POSIX, can be found with `cc -print-file-name=crt1.o`.\n" "# Not needed when targeting MacOS.\n" + "crt_dir=%s\n" + "\n" + "# The directory that contains `libgcc_s.so`.\n" + "# On POSIX, can be found with `cc -print-file-name=libgcc_s.so`.\n" + "# Not needed when targeting MacOS or Windows.\n" "lib_dir=%s\n" "\n" "# The directory that contains `crtbegin.o`.\n" - "# On Linux, can be found with `cc -print-file-name=crtbegin.o`.\n" + "# On POSIX, can be found with `cc -print-file-name=crtbegin.o`.\n" "# Not needed when targeting MacOS or Windows.\n" "static_lib_dir=%s\n" "\n" @@ -350,11 +370,12 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) { "kernel32_lib_dir=%s\n" "\n" "# The full path to the dynamic linker, on the target system.\n" - "# Only needed when targeting Linux.\n" + "# Not needed when targeting MacOS or Windows.\n" "dynamic_linker_path=%s\n" "\n" , buf_ptr(&self->include_dir), + buf_ptr(&self->crt_dir), buf_ptr(&self->lib_dir), buf_ptr(&self->static_lib_dir), buf_ptr(&self->msvc_lib_dir), @@ -378,7 +399,7 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) { return err; if ((err = zig_libc_find_native_include_dir_windows(self, sdk, verbose))) return err; - if ((err = zig_libc_find_lib_dir_windows(self, sdk, &native_target, verbose))) + if ((err = zig_libc_find_crt_dir_windows(self, sdk, &native_target, verbose))) return err; return ErrorNone; case ZigFindWindowsSdkErrorOutOfMemory: @@ -393,9 +414,12 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) { if ((err = zig_libc_find_native_include_dir_posix(self, verbose))) return err; #if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) + buf_init_from_str(&self->crt_dir, "/usr/lib"); buf_init_from_str(&self->lib_dir, "/usr/lib"); buf_init_from_str(&self->static_lib_dir, "/usr/lib"); #elif !defined(ZIG_OS_DARWIN) + if ((err = zig_libc_find_native_crt_dir_posix(self, verbose))) + return err; if ((err = zig_libc_find_native_lib_dir_posix(self, verbose))) return err; if ((err = zig_libc_find_native_static_lib_dir_posix(self, verbose))) diff --git a/src/libc_installation.hpp b/src/libc_installation.hpp index 4cb77f1451..7a1fb37d67 100644 --- a/src/libc_installation.hpp +++ b/src/libc_installation.hpp @@ -17,6 +17,7 @@ // Must be synchronized with zig_libc_keys struct ZigLibCInstallation { Buf include_dir; + Buf crt_dir; Buf lib_dir; Buf static_lib_dir; Buf msvc_lib_dir; diff --git a/src/link.cpp b/src/link.cpp index 5f611ae5a6..f52eaa1374 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -17,10 +17,10 @@ struct LinkJob { HashMap rpath_table; }; -static const char *get_libc_file(CodeGen *g, const char *file) { +static const char *get_libc_crt_file(CodeGen *g, const char *file) { assert(g->libc != nullptr); Buf *out_buf = buf_alloc(); - os_path_join(&g->libc->lib_dir, buf_create_from_str(file), out_buf); + os_path_join(&g->libc->crt_dir, buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } @@ -224,8 +224,8 @@ static void construct_linker_job_elf(LinkJob *lj) { crt1o = "Scrt1.o"; crtbegino = "crtbegin.o"; } - lj->args.append(get_libc_file(g, crt1o)); - lj->args.append(get_libc_file(g, "crti.o")); + lj->args.append(get_libc_crt_file(g, crt1o)); + lj->args.append(get_libc_crt_file(g, "crti.o")); lj->args.append(get_libc_static_file(g, crtbegino)); } @@ -263,7 +263,12 @@ static void construct_linker_job_elf(LinkJob *lj) { if (g->libc_link_lib != nullptr) { assert(g->libc != nullptr); lj->args.append("-L"); - lj->args.append(buf_ptr(&g->libc->lib_dir)); + lj->args.append(buf_ptr(&g->libc->crt_dir)); + + if (!buf_eql_buf(&g->libc->crt_dir, &g->libc->lib_dir)) { + lj->args.append("-L"); + lj->args.append(buf_ptr(&g->libc->lib_dir)); + } lj->args.append("-L"); lj->args.append(buf_ptr(&g->libc->static_lib_dir)); @@ -340,7 +345,7 @@ static void construct_linker_job_elf(LinkJob *lj) { // crt end if (lj->link_in_crt) { lj->args.append(get_libc_static_file(g, "crtend.o")); - lj->args.append(get_libc_file(g, "crtn.o")); + lj->args.append(get_libc_crt_file(g, "crtn.o")); } if (!g->zig_target->is_native) { @@ -597,7 +602,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->msvc_lib_dir)))); lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->kernel32_lib_dir)))); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->lib_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->crt_dir)))); } if (is_library && !g->is_static) { From 3ca861c7dd048a8bc15b6776a3b56fdc790750f7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 19:31:30 -0500 Subject: [PATCH 111/134] add a compile error note when C import fails and not linking libc closes #558 --- src/ir.cpp | 4 ++++ test/compile_errors.zig | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 929f195586..d6e40384f8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18705,6 +18705,10 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct if (errors.length > 0) { ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); + if (ira->codegen->libc_link_lib == nullptr) { + add_error_note(ira->codegen, parent_err_msg, node, + buf_sprintf("libc headers not available; compilation does not link against libc")); + } for (size_t i = 0; i < errors.length; i += 1) { ErrorMsg *err_msg = errors.at(i); err_msg_add_note(parent_err_msg, err_msg); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 48eb7cd85d..37b39706b5 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,17 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "libc headers note", + \\const c = @cImport(@cInclude("stdio.h")); + \\export fn entry() void { + \\ c.printf("hello, world!\n"); + \\} + , + ".tmp_source.zig:1:11: error: C import failed", + ".tmp_source.zig:1:11: note: libc headers not available; compilation does not link against libc", + ); + cases.addTest( "comptime vector overflow shows the index", \\comptime { From 152db27146e8b219af118abae8d235831aad2153 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 20:09:18 -0500 Subject: [PATCH 112/134] better error message when forgetting to link against libc closes #1698 --- src/all_types.hpp | 1 + src/ir.cpp | 8 ++++++++ test/compile_errors.zig | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index 44c1cc0956..fcd4f35b3a 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1808,6 +1808,7 @@ struct CodeGen { bool enable_cache; bool enable_time_report; bool system_linker_hack; + bool reported_bad_link_libc_error; //////////////////////////// Participates in Input Parameter Cache Hash /////// Note: there is a separate cache hash for builtin.zig, when adding fields, diff --git a/src/ir.cpp b/src/ir.cpp index d6e40384f8..73ad54a32d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15520,6 +15520,14 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ } static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { + if (buf_eql_str(lib_name, "c") && ira->codegen->libc_link_lib == nullptr && + !ira->codegen->reported_bad_link_libc_error) + { + ir_add_error_node(ira, source_node, + buf_sprintf("dependency on library c must be explicitly specified in the build command")); + ira->codegen->reported_bad_link_libc_error = true; + } + LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); for (size_t i = 0; i < link_lib->symbols.length; i += 1) { Buf *existing_symbol_name = link_lib->symbols.at(i); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 37b39706b5..d11c077164 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( + "implicit dependency on libc", + \\extern "c" fn exit(u8) void; + \\export fn entry() void { + \\ exit(0); + \\} + , + ".tmp_source.zig:3:5: error: dependency on library c must be explicitly specified in the build command", + ); + cases.addTest( "libc headers note", \\const c = @cImport(@cInclude("stdio.h")); From 4b7e285b763046052c154b71a95a0ff05472f41f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 20:18:35 -0500 Subject: [PATCH 113/134] add a regression test for #704 closes #704 --- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/704.zig | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 test/stage1/behavior/bugs/704.zig diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index d18f127f92..8124e27501 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -24,6 +24,7 @@ comptime { _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); + _ = @import("behavior/bugs/704.zig"); _ = @import("behavior/bugs/726.zig"); _ = @import("behavior/bugs/828.zig"); _ = @import("behavior/bugs/920.zig"); diff --git a/test/stage1/behavior/bugs/704.zig b/test/stage1/behavior/bugs/704.zig new file mode 100644 index 0000000000..765336c95f --- /dev/null +++ b/test/stage1/behavior/bugs/704.zig @@ -0,0 +1,7 @@ +const xxx = struct { + pub fn bar(self: *xxx) void {} +}; +test "bug 704" { + var x: xxx = undefined; + x.bar(); +} From 7571db05de3efcbc70a8404de6f479909b465eaa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 20:28:09 -0500 Subject: [PATCH 114/134] fix incorrectly trying to memset at comptime closes #718 --- src/ir.cpp | 6 +++++- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/718.zig | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 test/stage1/behavior/bugs/718.zig diff --git a/src/ir.cpp b/src/ir.cpp index 73ad54a32d..d59acb7015 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19451,10 +19451,12 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio if (type_is_invalid(casted_count->value.type)) return ira->codegen->invalid_instruction; + // TODO test this at comptime with u8 and non-u8 types if (casted_dest_ptr->value.special == ConstValSpecialStatic && casted_byte->value.special == ConstValSpecialStatic && casted_count->value.special == ConstValSpecialStatic && - casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) + casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + casted_dest_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { ConstExprValue *dest_ptr_val = &casted_dest_ptr->value; @@ -19573,6 +19575,8 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio if (type_is_invalid(casted_count->value.type)) return ira->codegen->invalid_instruction; + // TODO test this at comptime with u8 and non-u8 types + // TODO test with dest ptr being a global runtime variable if (casted_dest_ptr->value.special == ConstValSpecialStatic && casted_src_ptr->value.special == ConstValSpecialStatic && casted_count->value.special == ConstValSpecialStatic && diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 8124e27501..07158ae784 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -25,6 +25,7 @@ comptime { _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/704.zig"); + _ = @import("behavior/bugs/718.zig"); _ = @import("behavior/bugs/726.zig"); _ = @import("behavior/bugs/828.zig"); _ = @import("behavior/bugs/920.zig"); diff --git a/test/stage1/behavior/bugs/718.zig b/test/stage1/behavior/bugs/718.zig new file mode 100644 index 0000000000..8dfb511bb4 --- /dev/null +++ b/test/stage1/behavior/bugs/718.zig @@ -0,0 +1,17 @@ +const std = @import("std"); +const mem = std.mem; +const expect = std.testing.expect; +const Keys = struct { + up: bool, + down: bool, + left: bool, + right: bool, +}; +var keys: Keys = undefined; +test "zero keys with @memset" { + @memset(@ptrCast([*]u8, &keys), 0, @sizeOf(@typeOf(keys))); + expect(!keys.up); + expect(!keys.down); + expect(!keys.left); + expect(!keys.right); +} From 82fafca375ebf474e95a7312bb2c96f8df30375e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 25 Feb 2019 21:46:32 -0500 Subject: [PATCH 115/134] fix the libc compile error tests to only run on linux --- test/compile_errors.zig | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d11c077164..58a7190c67 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,26 +1,29 @@ const tests = @import("tests.zig"); +const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { - cases.addTest( - "implicit dependency on libc", - \\extern "c" fn exit(u8) void; - \\export fn entry() void { - \\ exit(0); - \\} - , - ".tmp_source.zig:3:5: error: dependency on library c must be explicitly specified in the build command", - ); + if (builtin.os == builtin.Os.linux) { + cases.addTest( + "implicit dependency on libc", + \\extern "c" fn exit(u8) void; + \\export fn entry() void { + \\ exit(0); + \\} + , + ".tmp_source.zig:3:5: error: dependency on library c must be explicitly specified in the build command", + ); - cases.addTest( - "libc headers note", - \\const c = @cImport(@cInclude("stdio.h")); - \\export fn entry() void { - \\ c.printf("hello, world!\n"); - \\} - , - ".tmp_source.zig:1:11: error: C import failed", - ".tmp_source.zig:1:11: note: libc headers not available; compilation does not link against libc", - ); + cases.addTest( + "libc headers note", + \\const c = @cImport(@cInclude("stdio.h")); + \\export fn entry() void { + \\ _ = c.printf(c"hello, world!\n"); + \\} + , + ".tmp_source.zig:1:11: error: C import failed", + ".tmp_source.zig:1:11: note: libc headers not available; compilation does not link against libc", + ); + } cases.addTest( "comptime vector overflow shows the index", From 3932b6474e890bdc6bc31cf7cad655e4feabaf7d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 08:16:03 -0500 Subject: [PATCH 116/134] use -nobuiltininc when compiling c code --- src/codegen.cpp | 2 ++ src/translate_c.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 99fffa4494..a10f9b4ffa 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8150,6 +8150,8 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { args.append(buf_ptr(out_dep_path)); } + args.append("-nobuiltininc"); + args.append("-isystem"); args.append(buf_ptr(g->zig_c_headers_dir)); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index c80bc6d726..e9909a4706 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4804,6 +4804,8 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const } } + clang_argv.append("-nobuiltininc"); + clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); From 6fd28164316185676ca3337f07c510b409cb6687 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 10:15:48 -0500 Subject: [PATCH 117/134] use -nostdinc++ when compiling C code --- src/codegen.cpp | 1 + src/translate_c.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index a10f9b4ffa..6e4e66e978 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8151,6 +8151,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { } args.append("-nobuiltininc"); + args.append("-nostdinc++"); args.append("-isystem"); args.append(buf_ptr(g->zig_c_headers_dir)); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index e9909a4706..6bd2f7e6dc 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4805,6 +4805,7 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const } clang_argv.append("-nobuiltininc"); + clang_argv.append("-nostdinc++"); clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); From 0eed72d6874b1705405df0d7975c66a2178c7106 Mon Sep 17 00:00:00 2001 From: John Schmidt Date: Mon, 25 Feb 2019 20:25:21 +0100 Subject: [PATCH 118/134] Add priority queue --- CMakeLists.txt | 1 + std/index.zig | 4 +- std/priority_queue.zig | 331 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 std/priority_queue.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 58fa045301..ab69a0d081 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -609,6 +609,7 @@ set(ZIG_STD_FILES "os/windows/util.zig" "os/zen.zig" "pdb.zig" + "priority_queue.zig" "rand/index.zig" "rand/ziggurat.zig" "segmented_list.zig" diff --git a/std/index.zig b/std/index.zig index 9d9b7ee8d6..f8c1b065e9 100644 --- a/std/index.zig +++ b/std/index.zig @@ -9,6 +9,7 @@ pub const DynLib = @import("dynamic_library.zig").DynLib; pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const Mutex = @import("mutex.zig").Mutex; +pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SpinLock = @import("spinlock.zig").SpinLock; @@ -59,7 +60,7 @@ test "std" { _ = @import("statically_initialized_mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); - + _ = @import("base64.zig"); _ = @import("build.zig"); _ = @import("c/index.zig"); @@ -85,6 +86,7 @@ test "std" { _ = @import("net.zig"); _ = @import("os/index.zig"); _ = @import("pdb.zig"); + _ = @import("priority_queue.zig"); _ = @import("rand/index.zig"); _ = @import("sort.zig"); _ = @import("testing.zig"); diff --git a/std/priority_queue.zig b/std/priority_queue.zig new file mode 100644 index 0000000000..33b5a963c6 --- /dev/null +++ b/std/priority_queue.zig @@ -0,0 +1,331 @@ +const std = @import("index.zig"); +const Allocator = std.mem.Allocator; +const debug = std.debug; +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; + +pub fn PriorityQueue(comptime T: type) type { + return struct { + const Self = @This(); + + items: []T, + len: usize, + allocator: *Allocator, + compareFn: fn (a: T, b: T) bool, + + pub fn init(allocator: *Allocator, compareFn: fn (a: T, b: T) bool) Self { + return Self{ + .items = []T{}, + .len = 0, + .allocator = allocator, + .compareFn = compareFn, + }; + } + + pub fn deinit(self: Self) void { + self.allocator.free(self.items); + } + + pub fn add(self: *Self, elem: T) !void { + try ensureCapacity(self, self.len + 1); + + self.items[self.len] = elem; + var child_index = self.len; + while (child_index > 0) { + var parent_index = ((child_index - 1) >> 1); + const child = self.items[child_index]; + const parent = self.items[parent_index]; + + if (!self.compareFn(child, parent)) break; + + self.items[parent_index] = child; + self.items[child_index] = parent; + child_index = parent_index; + } + self.len += 1; + } + + pub fn peek(self: *Self) ?T { + return if (self.len > 0) self.items[0] else null; + } + + pub fn removeOrNull(self: *Self) ?T { + return if (self.len > 0) self.remove() else null; + } + + pub fn remove(self: *Self) T { + const first = self.items[0]; + const last = self.items[self.len - 1]; + self.items[0] = last; + self.len -= 1; + siftDown(self); + return first; + } + + pub fn count(self: Self) usize { + return self.len; + } + + pub fn capacity(self: Self) usize { + return self.items.len; + } + + fn siftDown(self: *Self) void { + var index: usize = 0; + const half = self.len >> 1; + while (true) { + var left_index = (index << 1) + 1; + var right_index = left_index + 1; + var left = if (left_index < self.len) self.items[left_index] else null; + var right = if (right_index < self.len) self.items[right_index] else null; + + var smallest_index = index; + var smallest = self.items[index]; + + if (left) |e| { + if (self.compareFn(e, smallest)) { + smallest_index = left_index; + smallest = e; + } + } + + if (right) |e| { + if (self.compareFn(e, smallest)) { + smallest_index = right_index; + smallest = e; + } + } + + if (smallest_index == index) return; + + self.items[smallest_index] = self.items[index]; + self.items[index] = smallest; + index = smallest_index; + + if (index >= half) return; + } + } + + pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { + var better_capacity = self.capacity(); + if (better_capacity >= new_capacity) return; + while (true) { + better_capacity += better_capacity / 2 + 8; + if (better_capacity >= new_capacity) break; + } + self.items = try self.allocator.realloc(T, self.items, better_capacity); + } + + pub fn resize(self: *Self, new_len: usize) !void { + try self.ensureCapacity(new_len); + self.len = new_len; + } + + pub fn shrink(self: *Self, new_len: usize) void { + assert(new_len <= self.len); + self.len = new_len; + } + + const Iterator = struct { + queue: *PriorityQueue(T), + count: usize, + + fn next(it: *Iterator) ?T { + if (it.count > it.queue.len - 1) return null; + const out = it.count; + it.count += 1; + return it.queue.items[out]; + } + + fn reset(it: *Iterator) void { + it.count = 0; + } + }; + + pub fn iterator(self: *Self) Iterator { + return Iterator{ + .queue = self, + .count = 0, + }; + } + + fn dump(self: *Self) void { + warn("{{ "); + warn("items: "); + for (self.items) |e, i| { + if (i >= self.len) break; + warn("{}, ", e); + } + warn("array: "); + for (self.items) |e, i| { + warn("{}, ", e); + } + warn("len: {} ", self.len); + warn("capacity: {}", self.capacity()); + warn(" }}\n"); + } + }; +} + +fn lessThan(a: u32, b: u32) bool { + return a < b; +} + +fn greaterThan(a: u32, b: u32) bool { + return a > b; +} + +const PQ = PriorityQueue(u32); + +test "std.PriorityQueue: add and remove min heap" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + + try queue.add(54); + try queue.add(12); + try queue.add(7); + try queue.add(23); + try queue.add(25); + try queue.add(13); + expectEqual(u32(7), queue.remove()); + expectEqual(u32(12), queue.remove()); + expectEqual(u32(13), queue.remove()); + expectEqual(u32(23), queue.remove()); + expectEqual(u32(25), queue.remove()); + expectEqual(u32(54), queue.remove()); +} + +test "std.PriorityQueue: add and remove same min heap" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + + try queue.add(1); + try queue.add(1); + try queue.add(2); + try queue.add(2); + try queue.add(1); + try queue.add(1); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(2), queue.remove()); + expectEqual(u32(2), queue.remove()); +} + +test "std.PriorityQueue: removeOrNull on empty" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + + expect(queue.removeOrNull() == null); +} + +test "std.PriorityQueue: edge case 3 elements" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + + try queue.add(9); + try queue.add(3); + try queue.add(2); + expectEqual(u32(2), queue.remove()); + expectEqual(u32(3), queue.remove()); + expectEqual(u32(9), queue.remove()); +} + +test "std.PriorityQueue: peek" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + + expect(queue.peek() == null); + try queue.add(9); + try queue.add(3); + try queue.add(2); + expectEqual(u32(2), queue.peek().?); + expectEqual(u32(2), queue.peek().?); +} + +test "std.PriorityQueue: sift up with odd indices" { + var queue = PQ.init(debug.global_allocator, lessThan); + defer queue.deinit(); + const items = []u32{ 15, 7, 21, 14, 13, 22, 12, 6, 7, 25, 5, 24, 11, 16, 15, 24, 2, 1 }; + for (items) |e| { + try queue.add(e); + } + + expectEqual(u32(1), queue.remove()); + expectEqual(u32(2), queue.remove()); + expectEqual(u32(5), queue.remove()); + expectEqual(u32(6), queue.remove()); + expectEqual(u32(7), queue.remove()); + expectEqual(u32(7), queue.remove()); + expectEqual(u32(11), queue.remove()); + expectEqual(u32(12), queue.remove()); + expectEqual(u32(13), queue.remove()); + expectEqual(u32(14), queue.remove()); + expectEqual(u32(15), queue.remove()); + expectEqual(u32(15), queue.remove()); + expectEqual(u32(16), queue.remove()); + expectEqual(u32(21), queue.remove()); + expectEqual(u32(22), queue.remove()); + expectEqual(u32(24), queue.remove()); + expectEqual(u32(24), queue.remove()); + expectEqual(u32(25), queue.remove()); +} + +test "std.PriorityQueue: add and remove max heap" { + var queue = PQ.init(debug.global_allocator, greaterThan); + defer queue.deinit(); + + try queue.add(54); + try queue.add(12); + try queue.add(7); + try queue.add(23); + try queue.add(25); + try queue.add(13); + expectEqual(u32(54), queue.remove()); + expectEqual(u32(25), queue.remove()); + expectEqual(u32(23), queue.remove()); + expectEqual(u32(13), queue.remove()); + expectEqual(u32(12), queue.remove()); + expectEqual(u32(7), queue.remove()); +} + +test "std.PriorityQueue: add and remove same max heap" { + var queue = PQ.init(debug.global_allocator, greaterThan); + defer queue.deinit(); + + try queue.add(1); + try queue.add(1); + try queue.add(2); + try queue.add(2); + try queue.add(1); + try queue.add(1); + expectEqual(u32(2), queue.remove()); + expectEqual(u32(2), queue.remove()); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(1), queue.remove()); + expectEqual(u32(1), queue.remove()); +} + +test "std.PriorityQueue: iterator" { + var queue = PQ.init(debug.global_allocator, lessThan); + var map = std.AutoHashMap(u32, void).init(debug.global_allocator); + defer { + queue.deinit(); + map.deinit(); + } + + const items = []u32{ 54, 12, 7, 23, 25, 13 }; + for (items) |e| { + _ = try queue.add(e); + _ = try map.put(e, {}); + } + + var it = queue.iterator(); + while (it.next()) |e| { + _ = map.remove(e); + } + + expectEqual(usize(0), map.count()); +} From 1ec1097bd389b9af1b3e40d9edbc4c1c0b65f6a1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 14:50:20 -0500 Subject: [PATCH 119/134] use -nostdinc and sometimes -nolibc when compiling C code --- src/codegen.cpp | 7 +++++++ src/translate_c.cpp | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 6e4e66e978..b7230328c3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8151,8 +8151,15 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { } args.append("-nobuiltininc"); + args.append("-nostdinc"); args.append("-nostdinc++"); + if (g->libc_link_lib == nullptr) { + args.append("-nolibc"); + } + + args.append("-fno-spell-checking"); + args.append("-isystem"); args.append(buf_ptr(g->zig_c_headers_dir)); diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 6bd2f7e6dc..8525d73f56 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4805,7 +4805,11 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const } clang_argv.append("-nobuiltininc"); + clang_argv.append("-nostdinc"); clang_argv.append("-nostdinc++"); + if (codegen->libc_link_lib == nullptr) { + clang_argv.append("-nolibc"); + } clang_argv.append("-isystem"); clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); @@ -4832,7 +4836,9 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append("-Xclang"); clang_argv.append("-detailed-preprocessing-record"); - if (!c->codegen->zig_target->is_native) { + if (c->codegen->zig_target->is_native) { + clang_argv.append("-march=native"); + } else { clang_argv.append("-target"); clang_argv.append(buf_ptr(&c->codegen->triple_str)); } From ade10387a583351fe597b58c4c9ceb97d959adf5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 15:51:32 -0500 Subject: [PATCH 120/134] breaking changes to the way targets work in zig * CLI: `-target [name]` instead of `--target-*` args. This matches clang's API. * `builtin.Environ` renamed to `builtin.Abi` - likewise `builtin.environ` renamed to `builtin.abi` * stop hiding the concept of sub-arch. closes #1526 * `zig targets` only shows available targets. closes #438 * include all targets in readme, even those that don't print with `zig targets` but note they are Tier 4 * refactor target.cpp and make the naming conventions more consistent * introduce the concept of a "default C ABI" for a given OS/Arch combo. As a rule of thumb, if the system compiler is clang or gcc then the default C ABI is the gnu ABI. --- README.md | 57 ++- doc/docgen.zig | 9 +- src-self-hosted/link.zig | 4 +- src-self-hosted/main.zig | 18 +- src-self-hosted/target.zig | 240 ++------- src/all_types.hpp | 3 +- src/analyze.cpp | 2 +- src/buffer.hpp | 4 +- src/codegen.cpp | 120 +++-- src/error.cpp | 5 + src/error.hpp | 5 + src/libc_installation.cpp | 18 +- src/link.cpp | 34 +- src/main.cpp | 83 ++-- src/target.cpp | 795 ++++++++++++++++++++---------- src/target.hpp | 67 ++- src/util.hpp | 11 +- src/zig_llvm.cpp | 2 +- src/zig_llvm.h | 5 +- std/build.zig | 59 ++- std/fmt/index.zig | 2 +- std/os/linux/index.zig | 2 +- std/special/bootstrap.zig | 2 +- std/special/compiler_rt/index.zig | 58 +-- test/tests.zig | 10 +- 25 files changed, 875 insertions(+), 740 deletions(-) diff --git a/README.md b/README.md index c6d6e8895a..5244b49867 100644 --- a/README.md +++ b/README.md @@ -77,39 +77,54 @@ clarity. - what sizes are the C integer types - C ABI calling convention for this target - bootstrap code and default panic handler + * `zig targets` is guaranteed to include this target. #### Tier 4 Support * Support for these targets is entirely experimental. * LLVM may have the target as an experimental target, which means that you need to use Zig-provided binaries for the target to be available, or - build LLVM from source with special configure flags. + build LLVM from source with special configure flags. `zig targets` will + display the target if it is available. * This target may be considered deprecated by an official party, [such as macosx/i386](https://support.apple.com/en-us/HT208436) in which case this target will remain forever stuck in Tier 4. + * This target may only support `--emit asm` and cannot emit object files. #### Support Table -| | freestanding | linux | macosx | windows | freebsd | netbsd | UEFI | other | -|--------|--------------|--------|--------|---------|---------|------- | -------|--------| -|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | -|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|r600 | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|spir | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | Tier 3 | Tier 3 | -|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | -|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | -|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | -|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | +| | freestanding | linux | macosx | windows | freebsd | netbsd | UEFI | other | +|-------------|--------------|--------|--------|---------|---------|------- | -------|--------| +|x86_64 | Tier 2 | Tier 1 | Tier 1 | Tier 1 | Tier 2 | Tier 2 | Tier 2 | Tier 3 | +|i386 | Tier 2 | Tier 2 | Tier 4 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|arm64 | Tier 2 | Tier 2 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | Tier 3 | +|avr | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|bpf | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|hexagon | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|mips | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|powerpc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|amdgcn | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|sparc | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|s390x | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|lanai | Tier 3 | Tier 3 | N/A | N/A | Tier 3 | Tier 3 | N/A | Tier 3 | +|wasm32 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +|wasm64 | Tier 4 | N/A | N/A | N/A | N/A | N/A | N/A | N/A | +|riscv32 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | +|riscv64 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | Tier 4 | Tier 4 | +|xcore | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|nvptx | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|msp430 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|r600 | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|arc | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|tce | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|le | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|amdil | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|hsail | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|spir | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|kalimba | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|shave | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | +|renderscript | Tier 4 | Tier 4 | N/A | N/A | Tier 4 | Tier 4 | N/A | Tier 4 | ## Community diff --git a/doc/docgen.zig b/doc/docgen.zig index 082f308a57..cea456a98d 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1105,14 +1105,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var }, } if (code.target_windows) { - try test_args.appendSlice([][]const u8{ - "--target-os", - "windows", - "--target-arch", - "x86_64", - "--target-environ", - "msvc", - }); + try test_args.appendSlice([][]const u8{ "-target", "x86_64-windows" }); } const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed"); const escaped_stderr = try escapeHtml(allocator, result.stderr); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 2d8c0b92c2..5689ee7925 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -326,7 +326,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { switch (ctx.comp.target.getArch()) { builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"), builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"), - builtin.Arch.aarch64v8 => try ctx.args.append(c"-MACHINE:ARM"), + builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"), else => return error.UnsupportedLinkArchitecture, } @@ -552,7 +552,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { } }, DarwinPlatform.Kind.IPhoneOS => { - if (ctx.comp.target.getArch() == builtin.Arch.aarch64v8) { + if (ctx.comp.target.getArch() == builtin.Arch.aarch64) { // iOS does not need any crt1 files for arm64 } else if (platform.versionLessThan(3, 1)) { try ctx.args.append(c"-lcrt1.o"); diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 42556beaed..4c3edf6d5d 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -154,9 +154,7 @@ const usage_build_generic = \\ release-small optimize for small binary, safety off \\ --static Output will be statically linked \\ --strip Exclude debug symbols - \\ --target-arch [name] Specify target architecture - \\ --target-environ [name] Specify target environment - \\ --target-os [name] Specify target operating system + \\ -target [name] -- see the targets command \\ --verbose-tokenize Turn on compiler debug output for tokenization \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view) \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source) @@ -220,9 +218,7 @@ const args_build_generic = []Flag{ Flag.Bool("--pkg-end"), Flag.Bool("--static"), Flag.Bool("--strip"), - Flag.Arg1("--target-arch"), - Flag.Arg1("--target-environ"), - Flag.Arg1("--target-os"), + Flag.Arg1("-target"), Flag.Bool("--verbose-tokenize"), Flag.Bool("--verbose-ast-tree"), Flag.Bool("--verbose-ast-fmt"), @@ -839,15 +835,15 @@ fn cmdTargets(allocator: *Allocator, args: []const []const u8) !void { } try stdout.write("\n"); - try stdout.write("Environments:\n"); + try stdout.write("C ABIs:\n"); { comptime var i: usize = 0; - inline while (i < @memberCount(builtin.Environ)) : (i += 1) { - comptime const environ_tag = @memberName(builtin.Environ, i); + inline while (i < @memberCount(builtin.Abi)) : (i += 1) { + comptime const abi_tag = @memberName(builtin.Abi, i); // NOTE: Cannot use empty string, see #918. - comptime const native_str = if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n"; + comptime const native_str = if (comptime mem.eql(u8, abi_tag, @tagName(builtin.abi))) " (native)\n" else "\n"; - try stdout.print(" {}{}", environ_tag, native_str); + try stdout.print(" {}{}", abi_tag, native_str); } } } diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 121242b505..5fb7146c40 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -16,7 +16,7 @@ pub const Target = union(enum) { pub const Cross = struct { arch: builtin.Arch, os: builtin.Os, - environ: builtin.Environ, + abi: builtin.Abi, object_format: builtin.ObjectFormat, }; @@ -49,16 +49,16 @@ pub const Target = union(enum) { } pub fn getArch(self: Target) builtin.Arch { - return switch (self) { - Target.Native => builtin.arch, - @TagType(Target).Cross => |t| t.arch, - }; + switch (self) { + Target.Native => return builtin.arch, + @TagType(Target).Cross => |t| return t.arch, + } } - pub fn getEnviron(self: Target) builtin.Environ { + pub fn getAbi(self: Target) builtin.Abi { return switch (self) { - Target.Native => builtin.environ, - @TagType(Target).Cross => |t| t.environ, + Target.Native => builtin.abi, + @TagType(Target).Cross => |t| t.abi, }; } @@ -93,46 +93,10 @@ pub const Target = union(enum) { /// TODO expose the arch and subarch separately pub fn isArmOrThumb(self: Target) bool { return switch (self.getArch()) { - builtin.Arch.armv8_3a, - builtin.Arch.armv8_2a, - builtin.Arch.armv8_1a, - builtin.Arch.armv8, - builtin.Arch.armv8r, - builtin.Arch.armv8m_baseline, - builtin.Arch.armv8m_mainline, - builtin.Arch.armv7, - builtin.Arch.armv7em, - builtin.Arch.armv7m, - builtin.Arch.armv7s, - builtin.Arch.armv7k, - builtin.Arch.armv7ve, - builtin.Arch.armv6, - builtin.Arch.armv6m, - builtin.Arch.armv6k, - builtin.Arch.armv6t2, - builtin.Arch.armv5, - builtin.Arch.armv5te, - builtin.Arch.armv4t, - builtin.Arch.armebv8_3a, - builtin.Arch.armebv8_2a, - builtin.Arch.armebv8_1a, - builtin.Arch.armebv8, - builtin.Arch.armebv8r, - builtin.Arch.armebv8m_baseline, - builtin.Arch.armebv8m_mainline, - builtin.Arch.armebv7, - builtin.Arch.armebv7em, - builtin.Arch.armebv7m, - builtin.Arch.armebv7s, - builtin.Arch.armebv7k, - builtin.Arch.armebv7ve, - builtin.Arch.armebv6, - builtin.Arch.armebv6m, - builtin.Arch.armebv6k, - builtin.Arch.armebv6t2, - builtin.Arch.armebv5, - builtin.Arch.armebv5te, - builtin.Arch.armebv4t, + builtin.Arch.arm, + builtin.Arch.armeb, + builtin.Arch.aarch64, + builtin.Arch.aarch64_be, builtin.Arch.thumb, builtin.Arch.thumbeb, => true, @@ -155,14 +119,14 @@ pub const Target = union(enum) { // LLVM WebAssembly output support requires the target to be activated at // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. // - // LLVM determines the output format based on the environment suffix, + // LLVM determines the output format based on the abi suffix, // defaulting to an object based on the architecture. The default format in // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to // explicitly set this ourself in order for it to work. // // This is fixed in LLVM 7 and you will be able to get wasm output by // using the target triple `wasm32-unknown-unknown-unknown`. - const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron()); + const env_name = if (self.isWasm()) "wasm" else @tagName(self.getAbi()); var out = &std.io.BufferOutStream.init(&result).stream; try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name); @@ -181,46 +145,8 @@ pub const Target = union(enum) { => return 16, builtin.Arch.arc, - builtin.Arch.armv8_3a, - builtin.Arch.armv8_2a, - builtin.Arch.armv8_1a, - builtin.Arch.armv8, - builtin.Arch.armv8r, - builtin.Arch.armv8m_baseline, - builtin.Arch.armv8m_mainline, - builtin.Arch.armv7, - builtin.Arch.armv7em, - builtin.Arch.armv7m, - builtin.Arch.armv7s, - builtin.Arch.armv7k, - builtin.Arch.armv7ve, - builtin.Arch.armv6, - builtin.Arch.armv6m, - builtin.Arch.armv6k, - builtin.Arch.armv6t2, - builtin.Arch.armv5, - builtin.Arch.armv5te, - builtin.Arch.armv4t, - builtin.Arch.armebv8_3a, - builtin.Arch.armebv8_2a, - builtin.Arch.armebv8_1a, - builtin.Arch.armebv8, - builtin.Arch.armebv8r, - builtin.Arch.armebv8m_baseline, - builtin.Arch.armebv8m_mainline, - builtin.Arch.armebv7, - builtin.Arch.armebv7em, - builtin.Arch.armebv7m, - builtin.Arch.armebv7s, - builtin.Arch.armebv7k, - builtin.Arch.armebv7ve, - builtin.Arch.armebv6, - builtin.Arch.armebv6m, - builtin.Arch.armebv6k, - builtin.Arch.armebv6t2, - builtin.Arch.armebv5, - builtin.Arch.armebv5te, - builtin.Arch.armebv4t, + builtin.Arch.arm, + builtin.Arch.armeb, builtin.Arch.hexagon, builtin.Arch.le32, builtin.Arch.mips, @@ -241,29 +167,15 @@ pub const Target = union(enum) { builtin.Arch.amdil, builtin.Arch.hsail, builtin.Arch.spir, - builtin.Arch.kalimbav3, - builtin.Arch.kalimbav4, - builtin.Arch.kalimbav5, + builtin.Arch.kalimba, builtin.Arch.shave, builtin.Arch.lanai, builtin.Arch.wasm32, builtin.Arch.renderscript32, => return 32, - builtin.Arch.aarch64v8_3a, - builtin.Arch.aarch64v8_2a, - builtin.Arch.aarch64v8_1a, - builtin.Arch.aarch64v8, - builtin.Arch.aarch64v8r, - builtin.Arch.aarch64v8m_baseline, - builtin.Arch.aarch64v8m_mainline, - builtin.Arch.aarch64_bev8_3a, - builtin.Arch.aarch64_bev8_2a, - builtin.Arch.aarch64_bev8_1a, - builtin.Arch.aarch64_bev8, - builtin.Arch.aarch64_bev8r, - builtin.Arch.aarch64_bev8m_baseline, - builtin.Arch.aarch64_bev8m_mainline, + builtin.Arch.aarch64, + builtin.Arch.aarch64_be, builtin.Arch.mips64, builtin.Arch.mips64el, builtin.Arch.powerpc64, @@ -287,17 +199,17 @@ pub const Target = union(enum) { } pub fn getFloatAbi(self: Target) FloatAbi { - return switch (self.getEnviron()) { - builtin.Environ.gnueabihf, - builtin.Environ.eabihf, - builtin.Environ.musleabihf, + return switch (self.getAbi()) { + builtin.Abi.gnueabihf, + builtin.Abi.eabihf, + builtin.Abi.musleabihf, => FloatAbi.Hard, else => FloatAbi.Soft, }; } pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { - const env = self.getEnviron(); + const env = self.getAbi(); const arch = self.getArch(); const os = self.getOs(); switch (os) { @@ -306,21 +218,21 @@ pub const Target = union(enum) { }, builtin.Os.linux => { switch (env) { - builtin.Environ.android => { + builtin.Abi.android => { if (self.is64bit()) { return "/system/bin/linker64"; } else { return "/system/bin/linker"; } }, - builtin.Environ.gnux32 => { + builtin.Abi.gnux32 => { if (arch == builtin.Arch.x86_64) { return "/libx32/ld-linux-x32.so.2"; } }, - builtin.Environ.musl, - builtin.Environ.musleabi, - builtin.Environ.musleabihf, + builtin.Abi.musl, + builtin.Abi.musleabi, + builtin.Abi.musleabihf, => { if (arch == builtin.Arch.x86_64) { return "/lib/ld-musl-x86_64.so.1"; @@ -334,70 +246,18 @@ pub const Target = union(enum) { builtin.Arch.sparcel, => return "/lib/ld-linux.so.2", - builtin.Arch.aarch64v8_3a, - builtin.Arch.aarch64v8_2a, - builtin.Arch.aarch64v8_1a, - builtin.Arch.aarch64v8, - builtin.Arch.aarch64v8r, - builtin.Arch.aarch64v8m_baseline, - builtin.Arch.aarch64v8m_mainline, - => return "/lib/ld-linux-aarch64.so.1", + builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1", - builtin.Arch.aarch64_bev8_3a, - builtin.Arch.aarch64_bev8_2a, - builtin.Arch.aarch64_bev8_1a, - builtin.Arch.aarch64_bev8, - builtin.Arch.aarch64_bev8r, - builtin.Arch.aarch64_bev8m_baseline, - builtin.Arch.aarch64_bev8m_mainline, - => return "/lib/ld-linux-aarch64_be.so.1", + builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1", - builtin.Arch.armv8_3a, - builtin.Arch.armv8_2a, - builtin.Arch.armv8_1a, - builtin.Arch.armv8, - builtin.Arch.armv8r, - builtin.Arch.armv8m_baseline, - builtin.Arch.armv8m_mainline, - builtin.Arch.armv7, - builtin.Arch.armv7em, - builtin.Arch.armv7m, - builtin.Arch.armv7s, - builtin.Arch.armv7k, - builtin.Arch.armv7ve, - builtin.Arch.armv6, - builtin.Arch.armv6m, - builtin.Arch.armv6k, - builtin.Arch.armv6t2, - builtin.Arch.armv5, - builtin.Arch.armv5te, - builtin.Arch.armv4t, + builtin.Arch.arm, builtin.Arch.thumb, => return switch (self.getFloatAbi()) { FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", else => return "/lib/ld-linux.so.3", }, - builtin.Arch.armebv8_3a, - builtin.Arch.armebv8_2a, - builtin.Arch.armebv8_1a, - builtin.Arch.armebv8, - builtin.Arch.armebv8r, - builtin.Arch.armebv8m_baseline, - builtin.Arch.armebv8m_mainline, - builtin.Arch.armebv7, - builtin.Arch.armebv7em, - builtin.Arch.armebv7m, - builtin.Arch.armebv7s, - builtin.Arch.armebv7k, - builtin.Arch.armebv7ve, - builtin.Arch.armebv6, - builtin.Arch.armebv6m, - builtin.Arch.armebv6k, - builtin.Arch.armebv6t2, - builtin.Arch.armebv5, - builtin.Arch.armebv5te, - builtin.Arch.armebv4t, + builtin.Arch.armeb, builtin.Arch.thumbeb, => return switch (self.getFloatAbi()) { FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3", @@ -441,9 +301,7 @@ pub const Target = union(enum) { builtin.Arch.hsail64, builtin.Arch.spir, builtin.Arch.spir64, - builtin.Arch.kalimbav3, - builtin.Arch.kalimbav4, - builtin.Arch.kalimbav5, + builtin.Arch.kalimba, builtin.Arch.shave, builtin.Arch.lanai, builtin.Arch.wasm32, @@ -566,35 +424,9 @@ pub const Target = union(enum) { pub fn getDarwinArchString(self: Target) []const u8 { const arch = self.getArch(); switch (arch) { - builtin.Arch.aarch64v8_3a, - builtin.Arch.aarch64v8_2a, - builtin.Arch.aarch64v8_1a, - builtin.Arch.aarch64v8, - builtin.Arch.aarch64v8r, - builtin.Arch.aarch64v8m_baseline, - builtin.Arch.aarch64v8m_mainline, - => return "arm64", + builtin.Arch.aarch64 => return "arm64", builtin.Arch.thumb, - builtin.Arch.armv8_3a, - builtin.Arch.armv8_2a, - builtin.Arch.armv8_1a, - builtin.Arch.armv8, - builtin.Arch.armv8r, - builtin.Arch.armv8m_baseline, - builtin.Arch.armv8m_mainline, - builtin.Arch.armv7, - builtin.Arch.armv7em, - builtin.Arch.armv7m, - builtin.Arch.armv7s, - builtin.Arch.armv7k, - builtin.Arch.armv7ve, - builtin.Arch.armv6, - builtin.Arch.armv6m, - builtin.Arch.armv6k, - builtin.Arch.armv6t2, - builtin.Arch.armv5, - builtin.Arch.armv5te, - builtin.Arch.armv4t, + builtin.Arch.arm, => return "arm", builtin.Arch.powerpc => return "ppc", builtin.Arch.powerpc64 => return "ppc64", diff --git a/src/all_types.hpp b/src/all_types.hpp index fcd4f35b3a..6876aaa3ba 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1784,7 +1784,8 @@ struct CodeGen { unsigned pointer_size_bytes; uint32_t target_os_index; uint32_t target_arch_index; - uint32_t target_environ_index; + uint32_t target_sub_arch_index; + uint32_t target_abi_index; uint32_t target_oformat_index; bool is_big_endian; bool have_pub_main; diff --git a/src/analyze.cpp b/src/analyze.cpp index 5d9b0df8ee..a6c158a780 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1102,7 +1102,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { if (type_is_c_abi_int(g, fn_type_id->return_type)) { return false; } - if (g->zig_target->arch.arch == ZigLLVM_x86_64) { + if (g->zig_target->arch == ZigLLVM_x86_64) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); return abi_class == X64CABIClass_MEMORY; } else if (target_is_arm(g->zig_target)) { diff --git a/src/buffer.hpp b/src/buffer.hpp index afe9fd8a91..e286f7dc63 100644 --- a/src/buffer.hpp +++ b/src/buffer.hpp @@ -132,9 +132,7 @@ void buf_appendf(Buf *buf, const char *format, ...) static inline bool buf_eql_mem(Buf *buf, const char *mem, size_t mem_len) { assert(buf->list.length); - if (buf_len(buf) != mem_len) - return false; - return memcmp(buf_ptr(buf), mem, mem_len) == 0; + return mem_eql_mem(buf_ptr(buf), buf_len(buf), mem, mem_len); } static inline bool buf_eql_str(Buf *buf, const char *str) { diff --git a/src/codegen.cpp b/src/codegen.cpp index b7230328c3..4e8cd070f4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -29,9 +29,9 @@ static void init_darwin_native(CodeGen *g) { // Allow conflicts among OSX and iOS, but choose the default platform. if (osx_target && ios_target) { - if (g->zig_target->arch.arch == ZigLLVM_arm || - g->zig_target->arch.arch == ZigLLVM_aarch64 || - g->zig_target->arch.arch == ZigLLVM_thumb) + if (g->zig_target->arch == ZigLLVM_arm || + g->zig_target->arch == ZigLLVM_aarch64 || + g->zig_target->arch == ZigLLVM_thumb) { osx_target = nullptr; } else { @@ -347,8 +347,8 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { case CallingConventionC: return LLVMCCallConv; case CallingConventionCold: // cold calling convention only works on x86. - if (g->zig_target->arch.arch == ZigLLVM_x86 || - g->zig_target->arch.arch == ZigLLVM_x86_64) + if (g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64) { // cold calling convention is not supported on windows if (g->zig_target->os == OsWindows) { @@ -364,7 +364,7 @@ static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { zig_unreachable(); case CallingConventionStdcall: // stdcall calling convention only works on x86. - if (g->zig_target->arch.arch == ZigLLVM_x86) { + if (g->zig_target->arch == ZigLLVM_x86) { return LLVMX86StdcallCallConv; } else { return LLVMCCallConv; @@ -463,7 +463,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { bool external_linkage = linkage != GlobalLinkageIdInternal; CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; if (cc == CallingConventionStdcall && external_linkage && - g->zig_target->arch.arch == ZigLLVM_x86) + g->zig_target->arch == ZigLLVM_x86) { // prevent llvm name mangling symbol_name = buf_sprintf("\x01_%s", buf_ptr(symbol_name)); @@ -2095,7 +2095,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ return true; } - if (g->zig_target->arch.arch == ZigLLVM_x86_64) { + if (g->zig_target->arch == ZigLLVM_x86_64) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); size_t ty_size = type_size(g, ty); if (abi_class == X64CABIClass_MEMORY) { @@ -3344,9 +3344,9 @@ static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; bool asm_has_side_effects = true; bool asm_is_alignstack = false; - if (g->zig_target->arch.arch == ZigLLVM_x86_64) { + if (g->zig_target->arch == ZigLLVM_x86_64) { if (g->zig_target->os == OsLinux || target_is_darwin(g->zig_target) || g->zig_target->os == OsSolaris || - (g->zig_target->os == OsWindows && g->zig_target->env_type != ZigLLVM_MSVC)) + (g->zig_target->os == OsWindows && g->zig_target->abi != ZigLLVM_MSVC)) { if (g->cur_fn->valgrind_client_request_array == nullptr) { LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); @@ -3586,7 +3586,7 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { LLVMValueRef write_register_fn_val = get_write_register_fn_val(g); if (g->sp_md_node == nullptr) { - Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(&g->zig_target->arch)); + Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(g->zig_target->arch)); LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1); g->sp_md_node = LLVMMDNode(&str_node, 1); } @@ -7284,8 +7284,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const Os = enum {\n"); uint32_t field_count = (uint32_t)target_os_count(); for (uint32_t i = 0; i < field_count; i += 1) { - Os os_type = get_target_os(i); - const char *name = get_target_os_name(os_type); + Os os_type = target_os_enum(i); + const char *name = target_os_name(os_type); buf_appendf(contents, " %s,\n", name); if (os_type == g->zig_target->os) { @@ -7299,53 +7299,77 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { const char *cur_arch = nullptr; { - buf_appendf(contents, "pub const Arch = enum {\n"); + buf_appendf(contents, "pub const Arch = union(enum) {\n"); uint32_t field_count = (uint32_t)target_arch_count(); - for (uint32_t i = 0; i < field_count; i += 1) { - const ArchType *arch_type = get_target_arch(i); - Buf *arch_name = buf_alloc(); - buf_resize(arch_name, 50); - get_arch_name(buf_ptr(arch_name), arch_type); - buf_resize(arch_name, strlen(buf_ptr(arch_name))); - - buf_appendf(contents, " %s,\n", buf_ptr(arch_name)); - - if (arch_type->arch == g->zig_target->arch.arch && - arch_type->sub_arch == g->zig_target->arch.sub_arch) - { - g->target_arch_index = i; - cur_arch = buf_ptr(arch_name); + for (uint32_t arch_i = 0; arch_i < field_count; arch_i += 1) { + ZigLLVM_ArchType arch = target_arch_enum(arch_i); + const char *arch_name = target_arch_name(arch); + SubArchList sub_arch_list = target_subarch_list(arch); + if (sub_arch_list == SubArchListNone) { + buf_appendf(contents, " %s,\n", arch_name); + if (arch == g->zig_target->arch) { + g->target_arch_index = arch_i; + cur_arch = buf_ptr(buf_sprintf("Arch.%s", arch_name)); + } + } else { + const char *sub_arch_list_name = target_subarch_list_name(sub_arch_list); + buf_appendf(contents, " %s: %s,\n", arch_name, sub_arch_list_name); + if (arch == g->zig_target->arch) { + size_t sub_count = target_subarch_count(sub_arch_list); + for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) { + ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i); + if (sub == g->zig_target->sub_arch) { + g->target_sub_arch_index = sub_i; + cur_arch = buf_ptr(buf_sprintf("Arch{ .%s = Arch.%s.%s }", + arch_name, sub_arch_list_name, target_subarch_name(sub))); + } + } + } } } + + uint32_t list_count = target_subarch_list_count(); + // start at index 1 to skip None + for (uint32_t list_i = 1; list_i < list_count; list_i += 1) { + SubArchList sub_arch_list = target_subarch_list_enum(list_i); + const char *subarch_list_name = target_subarch_list_name(sub_arch_list); + buf_appendf(contents, " pub const %s = enum {\n", subarch_list_name); + size_t sub_count = target_subarch_count(sub_arch_list); + for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) { + ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i); + buf_appendf(contents, " %s,\n", target_subarch_name(sub)); + } + buf_appendf(contents, " };\n"); + } buf_appendf(contents, "};\n\n"); } assert(cur_arch != nullptr); - const char *cur_environ = nullptr; + const char *cur_abi = nullptr; { - buf_appendf(contents, "pub const Environ = enum {\n"); - uint32_t field_count = (uint32_t)target_environ_count(); + buf_appendf(contents, "pub const Abi = enum {\n"); + uint32_t field_count = (uint32_t)target_abi_count(); for (uint32_t i = 0; i < field_count; i += 1) { - ZigLLVM_EnvironmentType environ_type = get_target_environ(i); - const char *name = ZigLLVMGetEnvironmentTypeName(environ_type); + ZigLLVM_EnvironmentType abi = target_abi_enum(i); + const char *name = target_abi_name(abi); buf_appendf(contents, " %s,\n", name); - if (environ_type == g->zig_target->env_type) { - g->target_environ_index = i; - cur_environ = name; + if (abi == g->zig_target->abi) { + g->target_abi_index = i; + cur_abi = name; } } buf_appendf(contents, "};\n\n"); } - assert(cur_environ != nullptr); + assert(cur_abi != nullptr); const char *cur_obj_fmt = nullptr; { buf_appendf(contents, "pub const ObjectFormat = enum {\n"); uint32_t field_count = (uint32_t)target_oformat_count(); for (uint32_t i = 0; i < field_count; i += 1) { - ZigLLVM_ObjectFormatType oformat = get_target_oformat(i); - const char *name = get_target_oformat_name(oformat); + ZigLLVM_ObjectFormatType oformat = target_oformat_enum(i); + const char *name = target_oformat_name(oformat); buf_appendf(contents, " %s,\n", name); ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target); @@ -7633,8 +7657,8 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); - buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); - buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ); + buf_appendf(contents, "pub const arch = %s;\n", cur_arch); + buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); @@ -7669,11 +7693,11 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_bool(&cache_hash, g->is_test_build); cache_bool(&cache_hash, g->is_single_threaded); cache_int(&cache_hash, g->zig_target->is_native); - cache_int(&cache_hash, g->zig_target->arch.arch); - cache_int(&cache_hash, g->zig_target->arch.sub_arch); + cache_int(&cache_hash, g->zig_target->arch); + cache_int(&cache_hash, g->zig_target->sub_arch); cache_int(&cache_hash, g->zig_target->vendor); cache_int(&cache_hash, g->zig_target->os); - cache_int(&cache_hash, g->zig_target->env_type); + cache_int(&cache_hash, g->zig_target->abi); cache_bool(&cache_hash, g->have_err_ret_tracing); cache_bool(&cache_hash, g->libc_link_lib != nullptr); cache_bool(&cache_hash, g->valgrind_support); @@ -7901,7 +7925,7 @@ static void detect_libc(CodeGen *g) { // without a cross compiling libc kit. fprintf(stderr, "Cannot link against libc for non-native OS '%s' without providing a libc installation file.\n" - "See `zig libc --help` for more details.\n", get_target_os_name(g->zig_target->os)); + "See `zig libc --help` for more details.\n", target_os_name(g->zig_target->os)); exit(1); } } @@ -8799,11 +8823,11 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->build_mode); cache_int(ch, g->out_type); cache_bool(ch, g->zig_target->is_native); - cache_int(ch, g->zig_target->arch.arch); - cache_int(ch, g->zig_target->arch.sub_arch); + cache_int(ch, g->zig_target->arch); + cache_int(ch, g->zig_target->sub_arch); cache_int(ch, g->zig_target->vendor); cache_int(ch, g->zig_target->os); - cache_int(ch, g->zig_target->env_type); + cache_int(ch, g->zig_target->abi); cache_int(ch, g->subsystem); cache_bool(ch, g->is_static); cache_bool(ch, g->strip_debug_symbols); diff --git a/src/error.cpp b/src/error.cpp index c81cfd1683..80f7963538 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -37,6 +37,11 @@ const char *err_str(Error err) { case ErrorPathTooLong: return "path too long"; case ErrorCCompilerCannotFindFile: return "C compiler cannot find file"; case ErrorReadingDepFile: return "failed to read .d file"; + case ErrorMissingArchitecture: return "missing architecture"; + case ErrorMissingOperatingSystem: return "missing operating system"; + case ErrorUnknownArchitecture: return "unrecognized architecture"; + case ErrorUnknownOperatingSystem: return "unrecognized operating system"; + case ErrorUnknownABI: return "unrecognized C ABI"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index a580da547f..50dfeecc32 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -39,6 +39,11 @@ enum Error { ErrorPathTooLong, ErrorCCompilerCannotFindFile, ErrorReadingDepFile, + ErrorMissingArchitecture, + ErrorMissingOperatingSystem, + ErrorUnknownArchitecture, + ErrorUnknownOperatingSystem, + ErrorUnknownABI, }; const char *err_str(Error err); diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 04459d78e5..6e410ad5ac 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -103,7 +103,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget if (buf_len(&libc->crt_dir) == 0) { if (!target_is_darwin(target)) { if (verbose) { - fprintf(stderr, "crt_dir may not be empty for %s\n", get_target_os_name(target->os)); + fprintf(stderr, "crt_dir may not be empty for %s\n", target_os_name(target->os)); } return ErrorSemanticAnalyzeFail; } @@ -112,7 +112,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget if (buf_len(&libc->lib_dir) == 0) { if (!target_is_darwin(target) && target->os != OsWindows) { if (verbose) { - fprintf(stderr, "lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + fprintf(stderr, "lib_dir may not be empty for %s\n", target_os_name(target->os)); } return ErrorSemanticAnalyzeFail; } @@ -121,7 +121,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget if (buf_len(&libc->static_lib_dir) == 0) { if (!target_is_darwin(target) && target->os != OsWindows) { if (verbose) { - fprintf(stderr, "static_lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + fprintf(stderr, "static_lib_dir may not be empty for %s\n", target_os_name(target->os)); } return ErrorSemanticAnalyzeFail; } @@ -130,7 +130,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget if (buf_len(&libc->msvc_lib_dir) == 0) { if (target->os == OsWindows) { if (verbose) { - fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", target_os_name(target->os)); } return ErrorSemanticAnalyzeFail; } @@ -139,7 +139,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget if (buf_len(&libc->kernel32_lib_dir) == 0) { if (target->os == OsWindows) { if (verbose) { - fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", get_target_os_name(target->os)); + fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", target_os_name(target->os)); } return ErrorSemanticAnalyzeFail; } @@ -148,7 +148,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget if (buf_len(&libc->dynamic_linker_path) == 0) { if (!target_is_darwin(target) && target->os != OsWindows) { if (verbose) { - fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", get_target_os_name(target->os)); + fprintf(stderr, "dynamic_linker_path may not be empty for %s\n", target_os_name(target->os)); } return ErrorSemanticAnalyzeFail; } @@ -334,8 +334,10 @@ static Error zig_libc_find_native_dynamic_linker_posix(ZigLibCInstallation *self #endif ZigTarget native_target; get_native_target(&native_target); - Buf *dynamic_linker_path = target_dynamic_linker(&native_target); - buf_init_from_buf(&self->dynamic_linker_path, dynamic_linker_path); + const char *dynamic_linker_path = target_dynamic_linker(&native_target); + if (dynamic_linker_path != nullptr) { + buf_init_from_str(&self->dynamic_linker_path, dynamic_linker_path); + } return ErrorNone; } #endif diff --git a/src/link.cpp b/src/link.cpp index f52eaa1374..df2eb37f0a 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -96,7 +96,7 @@ static Buf *build_compiler_rt(CodeGen *parent_gen) { } static const char *get_darwin_arch_string(const ZigTarget *t) { - switch (t->arch.arch) { + switch (t->arch) { case ZigLLVM_aarch64: return "arm64"; case ZigLLVM_thumb: @@ -109,13 +109,13 @@ static const char *get_darwin_arch_string(const ZigTarget *t) { case ZigLLVM_ppc64le: return "ppc64le"; default: - return ZigLLVMGetArchTypeName(t->arch.arch); + return ZigLLVMGetArchTypeName(t->arch); } } static const char *getLDMOption(const ZigTarget *t) { - switch (t->arch.arch) { + switch (t->arch) { case ZigLLVM_x86: return "elf_i386"; case ZigLLVM_aarch64: @@ -149,7 +149,7 @@ static const char *getLDMOption(const ZigTarget *t) { case ZigLLVM_systemz: return "elf64_s390"; case ZigLLVM_x86_64: - if (t->env_type == ZigLLVM_GNUX32) { + if (t->abi == ZigLLVM_GNUX32) { return "elf32_x86_64"; } // Any target elf will use the freebsd osabi if suffixed with "_fbsd". @@ -191,8 +191,8 @@ static void construct_linker_job_elf(LinkJob *lj) { bool shared = !g->is_static && is_lib; Buf *soname = nullptr; if (g->is_static) { - if (g->zig_target->arch.arch == ZigLLVM_arm || g->zig_target->arch.arch == ZigLLVM_armeb || - g->zig_target->arch.arch == ZigLLVM_thumb || g->zig_target->arch.arch == ZigLLVM_thumbeb) + if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb || + g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb) { lj->args.append("-Bstatic"); } else { @@ -375,16 +375,16 @@ static void construct_linker_job_wasm(LinkJob *lj) { } //static bool is_target_cyg_mingw(const ZigTarget *target) { -// return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || -// (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); +// return (target->os == ZigLLVM_Win32 && target->abi == ZigLLVM_Cygnus) || +// (target->os == ZigLLVM_Win32 && target->abi == ZigLLVM_GNU); //} static void coff_append_machine_arg(CodeGen *g, ZigList *list) { - if (g->zig_target->arch.arch == ZigLLVM_x86) { + if (g->zig_target->arch == ZigLLVM_x86) { list->append("-MACHINE:X86"); - } else if (g->zig_target->arch.arch == ZigLLVM_x86_64) { + } else if (g->zig_target->arch == ZigLLVM_x86_64) { list->append("-MACHINE:X64"); - } else if (g->zig_target->arch.arch == ZigLLVM_arm) { + } else if (g->zig_target->arch == ZigLLVM_arm) { list->append("-MACHINE:ARM"); } } @@ -470,7 +470,7 @@ static void add_nt_link_args(LinkJob *lj, bool is_library) { // lj->args.append("-Bdynamic"); // if (dll || shared) { // lj->args.append("-e"); -// if (g->zig_target.arch.arch == ZigLLVM_x86) { +// if (g->zig_target.arch == ZigLLVM_x86) { // lj->args.append("_DllMainCRTStartup@12"); // } else { // lj->args.append("DllMainCRTStartup"); @@ -496,7 +496,7 @@ static void add_nt_link_args(LinkJob *lj, bool is_library) { //lj->args.append("-lmingw32"); //lj->args.append("-lgcc"); -//bool is_android = (g->zig_target.env_type == ZigLLVM_Android); +//bool is_android = (g->zig_target.abi == ZigLLVM_Android); //bool is_cyg_ming = is_target_cyg_mingw(&g->zig_target); //if (!g->is_static && !is_android) { // if (!is_cyg_ming) { @@ -637,7 +637,7 @@ static void construct_linker_job_coff(LinkJob *lj) { continue; } if (link_lib->provided_explicitly) { - if (lj->codegen->zig_target->env_type == ZigLLVM_GNU) { + if (lj->codegen->zig_target->abi == ZigLLVM_GNU) { Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); lj->args.append(buf_ptr(arg)); } @@ -764,8 +764,8 @@ static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { } if (platform->kind == IPhoneOS && - (g->zig_target->arch.arch == ZigLLVM_x86 || - g->zig_target->arch.arch == ZigLLVM_x86_64)) + (g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64)) { platform->kind = IPhoneOSSimulator; } @@ -886,7 +886,7 @@ static void construct_linker_job_macho(LinkJob *lj) { } break; case IPhoneOS: - if (g->zig_target->arch.arch == ZigLLVM_aarch64) { + if (g->zig_target->arch == ZigLLVM_aarch64) { // iOS does not need any crt1 files for arm64 } else if (darwin_version_lt(&platform, 3, 1)) { lj->args.append("-lcrt1.o"); diff --git a/src/main.cpp b/src/main.cpp index 1c154250ff..6af5dc3c37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,9 +69,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " --single-threaded source may assume it is only used single-threaded\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" - " --target-arch [name] specify target architecture\n" - " --target-environ [name] specify target environment\n" - " --target-os [name] specify target operating system\n" + " -target [name] -- see the targets command\n" " --verbose-tokenize enable compiler debug output for tokenization\n" " --verbose-ast enable compiler debug output for AST parsing\n" " --verbose-link enable compiler debug output for linking\n" @@ -145,6 +143,14 @@ static const char *ZIG_ZEN = "\n" " * Minimize energy spent on coding style.\n" " * Together we serve end users.\n"; +static bool arch_available_in_llvm(ZigLLVM_ArchType arch) { + LLVMTargetRef target_ref; + char *err_msg = nullptr; + char triple_string[128]; + sprintf(triple_string, "%s-unknown-unknown-unknown", ZigLLVMGetArchTypeName(arch)); + return !LLVMGetTargetFromTriple(triple_string, &target_ref, &err_msg); +} + static int print_target_list(FILE *f) { ZigTarget native; get_native_target(&native); @@ -152,28 +158,36 @@ static int print_target_list(FILE *f) { fprintf(f, "Architectures:\n"); size_t arch_count = target_arch_count(); for (size_t arch_i = 0; arch_i < arch_count; arch_i += 1) { - const ArchType *arch = get_target_arch(arch_i); - char arch_name[50]; - get_arch_name(arch_name, arch); - const char *native_str = (native.arch.arch == arch->arch && native.arch.sub_arch == arch->sub_arch) ? - " (native)" : ""; - fprintf(f, " %s%s\n", arch_name, native_str); + ZigLLVM_ArchType arch = target_arch_enum(arch_i); + if (!arch_available_in_llvm(arch)) + continue; + const char *arch_name = target_arch_name(arch); + SubArchList sub_arch_list = target_subarch_list(arch); + size_t sub_count = target_subarch_count(sub_arch_list); + const char *arch_native_str = (native.arch == arch) ? " (native)" : ""; + fprintf(stderr, " %s%s\n", arch_name, arch_native_str); + for (size_t sub_i = 0; sub_i < sub_count; sub_i += 1) { + ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i); + const char *sub_name = target_subarch_name(sub); + const char *sub_native_str = (native.arch == arch && native.sub_arch == sub) ? " (native)" : ""; + fprintf(f, " %s%s\n", sub_name, sub_native_str); + } } fprintf(f, "\nOperating Systems:\n"); size_t os_count = target_os_count(); for (size_t i = 0; i < os_count; i += 1) { - Os os_type = get_target_os(i); + Os os_type = target_os_enum(i); const char *native_str = (native.os == os_type) ? " (native)" : ""; - fprintf(f, " %s%s\n", get_target_os_name(os_type), native_str); + fprintf(f, " %s%s\n", target_os_name(os_type), native_str); } - fprintf(f, "\nEnvironments:\n"); - size_t environ_count = target_environ_count(); - for (size_t i = 0; i < environ_count; i += 1) { - ZigLLVM_EnvironmentType environ_type = get_target_environ(i); - const char *native_str = (native.env_type == environ_type) ? " (native)" : ""; - fprintf(f, " %s%s\n", ZigLLVMGetEnvironmentTypeName(environ_type), native_str); + fprintf(f, "\nC ABIs:\n"); + size_t abi_count = target_abi_count(); + for (size_t i = 0; i < abi_count; i += 1) { + ZigLLVM_EnvironmentType abi = target_abi_enum(i); + const char *native_str = (native.abi == abi) ? " (native)" : ""; + fprintf(f, " %s%s\n", target_abi_name(abi), native_str); } return EXIT_SUCCESS; @@ -397,9 +411,7 @@ int main(int argc, char **argv) { ZigList link_libs = {0}; ZigList forbidden_link_libs = {0}; ZigList frameworks = {0}; - const char *target_arch = nullptr; - const char *target_os = nullptr; - const char *target_environ = nullptr; + const char *target_string = nullptr; bool rdynamic = false; const char *mmacosx_version_min = nullptr; const char *mios_version_min = nullptr; @@ -740,12 +752,8 @@ int main(int argc, char **argv) { asm_files.append(argv[i]); } else if (strcmp(arg, "--cache-dir") == 0) { cache_dir = argv[i]; - } else if (strcmp(arg, "--target-arch") == 0) { - target_arch = argv[i]; - } else if (strcmp(arg, "--target-os") == 0) { - target_os = argv[i]; - } else if (strcmp(arg, "--target-environ") == 0) { - target_environ = argv[i]; + } else if (strcmp(arg, "-target") == 0) { + target_string = argv[i]; } else if (strcmp(arg, "-mmacosx-version-min") == 0) { mmacosx_version_min = argv[i]; } else if (strcmp(arg, "-mios-version-min") == 0) { @@ -874,27 +882,12 @@ int main(int argc, char **argv) { init_all_targets(); ZigTarget target; - if (!target_arch && !target_os && !target_environ) { + if (target_string == nullptr) { get_native_target(&target); } else { - get_unknown_target(&target); - if (target_arch) { - if (parse_target_arch(target_arch, &target.arch)) { - fprintf(stderr, "invalid --target-arch argument\n"); - return print_error_usage(arg0); - } - } - if (target_os) { - if (parse_target_os(target_os, &target.os)) { - fprintf(stderr, "invalid --target-os argument\n"); - return print_error_usage(arg0); - } - } - if (target_environ) { - if (parse_target_environ(target_environ, &target.env_type)) { - fprintf(stderr, "invalid --target-environ argument\n"); - return print_error_usage(arg0); - } + if ((err = target_parse_triple(&target, target_string))) { + fprintf(stderr, "invalid target: %s\n", err_str(err)); + return print_error_usage(arg0); } } diff --git a/src/target.cpp b/src/target.cpp index 49a1742945..74168033a5 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -13,115 +13,105 @@ #include -static const ArchType arch_list[] = { - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8_3a}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8_2a}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8_1a}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8r}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8m_baseline}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v8m_mainline}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7em}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7m}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7s}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7k}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v7ve}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6m}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6k}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v6t2}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v5}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v5te}, - {ZigLLVM_arm, ZigLLVM_ARMSubArch_v4t}, +static const SubArchList subarch_list_list[] = { + SubArchListNone, + SubArchListArm32, + SubArchListArm64, + SubArchListKalimba, +}; - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8_3a}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8_2a}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8_1a}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8r}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8m_baseline}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v8m_mainline}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7em}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7m}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7s}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7k}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v7ve}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6m}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6k}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v6t2}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v5}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v5te}, - {ZigLLVM_armeb, ZigLLVM_ARMSubArch_v4t}, +static const ZigLLVM_SubArchType subarch_list_arm32[] = { + ZigLLVM_ARMSubArch_v8_4a, + ZigLLVM_ARMSubArch_v8_3a, + ZigLLVM_ARMSubArch_v8_2a, + ZigLLVM_ARMSubArch_v8_1a, + ZigLLVM_ARMSubArch_v8, + ZigLLVM_ARMSubArch_v8r, + ZigLLVM_ARMSubArch_v8m_baseline, + ZigLLVM_ARMSubArch_v8m_mainline, + ZigLLVM_ARMSubArch_v7, + ZigLLVM_ARMSubArch_v7em, + ZigLLVM_ARMSubArch_v7m, + ZigLLVM_ARMSubArch_v7s, + ZigLLVM_ARMSubArch_v7k, + ZigLLVM_ARMSubArch_v7ve, + ZigLLVM_ARMSubArch_v6, + ZigLLVM_ARMSubArch_v6m, + ZigLLVM_ARMSubArch_v6k, + ZigLLVM_ARMSubArch_v6t2, + ZigLLVM_ARMSubArch_v5, + ZigLLVM_ARMSubArch_v5te, + ZigLLVM_ARMSubArch_v4t, +}; - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8_3a}, - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8_2a}, - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8_1a}, - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8}, - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8r}, - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8m_baseline}, - {ZigLLVM_aarch64, ZigLLVM_ARMSubArch_v8m_mainline}, +static const ZigLLVM_SubArchType subarch_list_arm64[] = { + ZigLLVM_ARMSubArch_v8_4a, + ZigLLVM_ARMSubArch_v8_3a, + ZigLLVM_ARMSubArch_v8_2a, + ZigLLVM_ARMSubArch_v8_1a, + ZigLLVM_ARMSubArch_v8, + ZigLLVM_ARMSubArch_v8r, + ZigLLVM_ARMSubArch_v8m_baseline, + ZigLLVM_ARMSubArch_v8m_mainline, +}; - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8_3a}, - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8_2a}, - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8_1a}, - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8}, - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8r}, - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8m_baseline}, - {ZigLLVM_aarch64_be, ZigLLVM_ARMSubArch_v8m_mainline}, +static const ZigLLVM_SubArchType subarch_list_kalimba[] = { + ZigLLVM_KalimbaSubArch_v5, + ZigLLVM_KalimbaSubArch_v4, + ZigLLVM_KalimbaSubArch_v3, +}; - {ZigLLVM_arc, ZigLLVM_NoSubArch}, - {ZigLLVM_avr, ZigLLVM_NoSubArch}, - {ZigLLVM_bpfel, ZigLLVM_NoSubArch}, - {ZigLLVM_bpfeb, ZigLLVM_NoSubArch}, - {ZigLLVM_hexagon, ZigLLVM_NoSubArch}, - {ZigLLVM_mips, ZigLLVM_NoSubArch}, - {ZigLLVM_mipsel, ZigLLVM_NoSubArch}, - {ZigLLVM_mips64, ZigLLVM_NoSubArch}, - {ZigLLVM_mips64el, ZigLLVM_NoSubArch}, - {ZigLLVM_msp430, ZigLLVM_NoSubArch}, - {ZigLLVM_nios2, ZigLLVM_NoSubArch}, - {ZigLLVM_ppc, ZigLLVM_NoSubArch}, - {ZigLLVM_ppc64, ZigLLVM_NoSubArch}, - {ZigLLVM_ppc64le, ZigLLVM_NoSubArch}, - {ZigLLVM_r600, ZigLLVM_NoSubArch}, - {ZigLLVM_amdgcn, ZigLLVM_NoSubArch}, - {ZigLLVM_riscv32, ZigLLVM_NoSubArch}, - {ZigLLVM_riscv64, ZigLLVM_NoSubArch}, - {ZigLLVM_sparc, ZigLLVM_NoSubArch}, - {ZigLLVM_sparcv9, ZigLLVM_NoSubArch}, - {ZigLLVM_sparcel, ZigLLVM_NoSubArch}, - {ZigLLVM_systemz, ZigLLVM_NoSubArch}, - {ZigLLVM_tce, ZigLLVM_NoSubArch}, - {ZigLLVM_tcele, ZigLLVM_NoSubArch}, - {ZigLLVM_thumb, ZigLLVM_NoSubArch}, - {ZigLLVM_thumbeb, ZigLLVM_NoSubArch}, - {ZigLLVM_x86, ZigLLVM_NoSubArch}, - {ZigLLVM_x86_64, ZigLLVM_NoSubArch}, - {ZigLLVM_xcore, ZigLLVM_NoSubArch}, - {ZigLLVM_nvptx, ZigLLVM_NoSubArch}, - {ZigLLVM_nvptx64, ZigLLVM_NoSubArch}, - {ZigLLVM_le32, ZigLLVM_NoSubArch}, - {ZigLLVM_le64, ZigLLVM_NoSubArch}, - {ZigLLVM_amdil, ZigLLVM_NoSubArch}, - {ZigLLVM_amdil64, ZigLLVM_NoSubArch}, - {ZigLLVM_hsail, ZigLLVM_NoSubArch}, - {ZigLLVM_hsail64, ZigLLVM_NoSubArch}, - {ZigLLVM_spir, ZigLLVM_NoSubArch}, - {ZigLLVM_spir64, ZigLLVM_NoSubArch}, - - {ZigLLVM_kalimba, ZigLLVM_KalimbaSubArch_v3}, - {ZigLLVM_kalimba, ZigLLVM_KalimbaSubArch_v4}, - {ZigLLVM_kalimba, ZigLLVM_KalimbaSubArch_v5}, - - {ZigLLVM_shave, ZigLLVM_NoSubArch}, - {ZigLLVM_lanai, ZigLLVM_NoSubArch}, - {ZigLLVM_wasm32, ZigLLVM_NoSubArch}, - {ZigLLVM_wasm64, ZigLLVM_NoSubArch}, - {ZigLLVM_renderscript32, ZigLLVM_NoSubArch}, - {ZigLLVM_renderscript64, ZigLLVM_NoSubArch}, +static const ZigLLVM_ArchType arch_list[] = { + ZigLLVM_arm, + ZigLLVM_armeb, + ZigLLVM_aarch64, + ZigLLVM_aarch64_be, + ZigLLVM_arc, + ZigLLVM_avr, + ZigLLVM_bpfel, + ZigLLVM_bpfeb, + ZigLLVM_hexagon, + ZigLLVM_mips, + ZigLLVM_mipsel, + ZigLLVM_mips64, + ZigLLVM_mips64el, + ZigLLVM_msp430, + ZigLLVM_nios2, + ZigLLVM_ppc, + ZigLLVM_ppc64, + ZigLLVM_ppc64le, + ZigLLVM_r600, + ZigLLVM_amdgcn, + ZigLLVM_riscv32, + ZigLLVM_riscv64, + ZigLLVM_sparc, + ZigLLVM_sparcv9, + ZigLLVM_sparcel, + ZigLLVM_systemz, + ZigLLVM_tce, + ZigLLVM_tcele, + ZigLLVM_thumb, + ZigLLVM_thumbeb, + ZigLLVM_x86, + ZigLLVM_x86_64, + ZigLLVM_xcore, + ZigLLVM_nvptx, + ZigLLVM_nvptx64, + ZigLLVM_le32, + ZigLLVM_le64, + ZigLLVM_amdil, + ZigLLVM_amdil64, + ZigLLVM_hsail, + ZigLLVM_hsail64, + ZigLLVM_spir, + ZigLLVM_spir64, + ZigLLVM_kalimba, + ZigLLVM_shave, + ZigLLVM_lanai, + ZigLLVM_wasm32, + ZigLLVM_wasm64, + ZigLLVM_renderscript32, + ZigLLVM_renderscript64, }; static const ZigLLVM_VendorType vendor_list[] = { @@ -179,7 +169,7 @@ static const Os os_list[] = { }; // Coordinate with zig_llvm.h -static const ZigLLVM_EnvironmentType environ_list[] = { +static const ZigLLVM_EnvironmentType abi_list[] = { ZigLLVM_UnknownEnvironment, ZigLLVM_GNU, @@ -214,11 +204,12 @@ size_t target_oformat_count(void) { return array_length(oformat_list); } -ZigLLVM_ObjectFormatType get_target_oformat(size_t index) { +ZigLLVM_ObjectFormatType target_oformat_enum(size_t index) { + assert(index < array_length(oformat_list)); return oformat_list[index]; } -const char *get_target_oformat_name(ZigLLVM_ObjectFormatType oformat) { +const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat) { switch (oformat) { case ZigLLVM_UnknownObjectFormat: return "unknown"; case ZigLLVM_COFF: return "coff"; @@ -233,22 +224,25 @@ size_t target_arch_count(void) { return array_length(arch_list); } -const ArchType *get_target_arch(size_t index) { - return &arch_list[index]; +ZigLLVM_ArchType target_arch_enum(size_t index) { + assert(index < array_length(arch_list)); + return arch_list[index]; } size_t target_vendor_count(void) { return array_length(vendor_list); } -ZigLLVM_VendorType get_target_vendor(size_t index) { +ZigLLVM_VendorType target_vendor_enum(size_t index) { + assert(index < array_length(vendor_list)); return vendor_list[index]; } size_t target_os_count(void) { return array_length(os_list); } -Os get_target_os(size_t index) { +Os target_os_enum(size_t index) { + assert(index < array_length(os_list)); return os_list[index]; } @@ -391,7 +385,7 @@ static Os get_zig_os_type(ZigLLVM_OSType os_type) { zig_unreachable(); } -const char *get_target_os_name(Os os_type) { +const char *target_os_name(Os os_type) { switch (os_type) { case OsFreestanding: return "freestanding"; @@ -434,80 +428,251 @@ const char *get_target_os_name(Os os_type) { zig_unreachable(); } -size_t target_environ_count(void) { - return array_length(environ_list); +size_t target_abi_count(void) { + return array_length(abi_list); } -ZigLLVM_EnvironmentType get_target_environ(size_t index) { - return environ_list[index]; +ZigLLVM_EnvironmentType target_abi_enum(size_t index) { + assert(index < array_length(abi_list)); + return abi_list[index]; +} +const char *target_abi_name(ZigLLVM_EnvironmentType abi) { + if (abi == ZigLLVM_UnknownEnvironment) + return "none"; + return ZigLLVMGetEnvironmentTypeName(abi); } void get_native_target(ZigTarget *target) { ZigLLVM_OSType os_type; ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os ZigLLVMGetNativeTarget( - &target->arch.arch, - &target->arch.sub_arch, + &target->arch, + &target->sub_arch, &target->vendor, &os_type, - &target->env_type, + &target->abi, &oformat); target->os = get_zig_os_type(os_type); target->is_native = true; + if (target->abi == ZigLLVM_UnknownEnvironment) { + target->abi = target_default_abi(target->arch, target->os); + } } -void get_unknown_target(ZigTarget *target) { - target->arch.arch = ZigLLVM_UnknownArch; - target->arch.sub_arch = ZigLLVM_NoSubArch; - target->vendor = ZigLLVM_UnknownVendor; - target->os = OsFreestanding; - target->env_type = ZigLLVM_UnknownEnvironment; - target->is_native = false; -} - -static void get_arch_name_raw(char *out_str, ZigLLVM_ArchType arch, ZigLLVM_SubArchType sub_arch) { - const char *sub_str = (sub_arch == ZigLLVM_NoSubArch) ? "" : ZigLLVMGetSubArchTypeName(sub_arch); - sprintf(out_str, "%s%s", ZigLLVMGetArchTypeName(arch), sub_str); -} - -void get_arch_name(char *out_str, const ArchType *arch) { - return get_arch_name_raw(out_str, arch->arch, arch->sub_arch); -} - -int parse_target_arch(const char *str, ArchType *out_arch) { - for (size_t i = 0; i < array_length(arch_list); i += 1) { - const ArchType *arch = &arch_list[i]; - char arch_name[50]; - get_arch_name_raw(arch_name, arch->arch, arch->sub_arch); - if (strcmp(arch_name, str) == 0) { - *out_arch = *arch; - return 0; +Error target_parse_archsub(ZigLLVM_ArchType *out_arch, ZigLLVM_SubArchType *out_sub, + const char *archsub_ptr, size_t archsub_len) +{ + for (size_t arch_i = 0; arch_i < array_length(arch_list); arch_i += 1) { + ZigLLVM_ArchType arch = arch_list[arch_i]; + SubArchList sub_arch_list = target_subarch_list(arch); + size_t subarch_count = target_subarch_count(sub_arch_list); + if (subarch_count == 0) { + if (mem_eql_str(archsub_ptr, archsub_len, target_arch_name(arch))) { + *out_arch = arch; + *out_sub = ZigLLVM_NoSubArch; + return ErrorNone; + } + continue; + } + for (size_t sub_i = 0; sub_i < subarch_count; sub_i += 1) { + ZigLLVM_SubArchType sub = target_subarch_enum(sub_arch_list, sub_i); + char arch_name[64]; + int n = sprintf(arch_name, "%s%s", target_arch_name(arch), target_subarch_name(sub)); + if (mem_eql_mem(arch_name, n, archsub_ptr, archsub_len)) { + *out_arch = arch; + *out_sub = sub; + return ErrorNone; + } } } - return ErrorFileNotFound; + return ErrorUnknownArchitecture; } -int parse_target_os(const char *str, Os *out_os) { +SubArchList target_subarch_list(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + return SubArchListArm32; + + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + return SubArchListArm64; + + case ZigLLVM_kalimba: + return SubArchListKalimba; + + case ZigLLVM_arc: + case ZigLLVM_avr: + case ZigLLVM_bpfel: + case ZigLLVM_bpfeb: + case ZigLLVM_hexagon: + case ZigLLVM_mips: + case ZigLLVM_mipsel: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_msp430: + case ZigLLVM_nios2: + case ZigLLVM_ppc: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + case ZigLLVM_r600: + case ZigLLVM_amdgcn: + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + case ZigLLVM_sparc: + case ZigLLVM_sparcv9: + case ZigLLVM_sparcel: + case ZigLLVM_systemz: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_x86: + case ZigLLVM_x86_64: + case ZigLLVM_xcore: + case ZigLLVM_nvptx: + case ZigLLVM_nvptx64: + case ZigLLVM_le32: + case ZigLLVM_le64: + case ZigLLVM_amdil: + case ZigLLVM_amdil64: + case ZigLLVM_hsail: + case ZigLLVM_hsail64: + case ZigLLVM_spir: + case ZigLLVM_spir64: + case ZigLLVM_shave: + case ZigLLVM_lanai: + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + case ZigLLVM_renderscript32: + case ZigLLVM_renderscript64: + return SubArchListNone; + } + zig_unreachable(); +} + +size_t target_subarch_count(SubArchList sub_arch_list) { + switch (sub_arch_list) { + case SubArchListNone: + return 0; + case SubArchListArm32: + return array_length(subarch_list_arm32); + case SubArchListArm64: + return array_length(subarch_list_arm64); + case SubArchListKalimba: + return array_length(subarch_list_kalimba); + } + zig_unreachable(); +} + +ZigLLVM_SubArchType target_subarch_enum(SubArchList sub_arch_list, size_t i) { + switch (sub_arch_list) { + case SubArchListNone: + zig_unreachable(); + case SubArchListArm32: + assert(i < array_length(subarch_list_arm32)); + return subarch_list_arm32[i]; + case SubArchListArm64: + assert(i < array_length(subarch_list_arm64)); + return subarch_list_arm64[i]; + case SubArchListKalimba: + assert(i < array_length(subarch_list_kalimba)); + return subarch_list_kalimba[i]; + } + zig_unreachable(); +} + +const char *target_subarch_name(ZigLLVM_SubArchType subarch) { + return ZigLLVMGetSubArchTypeName(subarch); +} + +size_t target_subarch_list_count(void) { + return array_length(subarch_list_list); +} + +SubArchList target_subarch_list_enum(size_t index) { + assert(index < array_length(subarch_list_list)); + return subarch_list_list[index]; +} + +const char *target_subarch_list_name(SubArchList sub_arch_list) { + switch (sub_arch_list) { + case SubArchListNone: + return "None"; + case SubArchListArm32: + return "Arm32"; + case SubArchListArm64: + return "Arm64"; + case SubArchListKalimba: + return "Kalimba"; + } + zig_unreachable(); +} + +Error target_parse_os(Os *out_os, const char *os_ptr, size_t os_len) { for (size_t i = 0; i < array_length(os_list); i += 1) { Os os = os_list[i]; - const char *os_name = get_target_os_name(os); - if (strcmp(os_name, str) == 0) { + const char *os_name = target_os_name(os); + if (mem_eql_str(os_ptr, os_len, os_name)) { *out_os = os; - return 0; + return ErrorNone; } } - return ErrorFileNotFound; + return ErrorUnknownOperatingSystem; } -int parse_target_environ(const char *str, ZigLLVM_EnvironmentType *out_environ) { - for (size_t i = 0; i < array_length(environ_list); i += 1) { - ZigLLVM_EnvironmentType env_type = environ_list[i]; - const char *environ_name = ZigLLVMGetEnvironmentTypeName(env_type); - if (strcmp(environ_name, str) == 0) { - *out_environ = env_type; - return 0; +Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, size_t abi_len) { + for (size_t i = 0; i < array_length(abi_list); i += 1) { + ZigLLVM_EnvironmentType abi = abi_list[i]; + const char *abi_name = target_abi_name(abi); + if (mem_eql_str(abi_ptr, abi_len, abi_name)) { + *out_abi = abi; + return ErrorNone; } } - return ErrorFileNotFound; + return ErrorUnknownABI; +} + +Error target_parse_triple(ZigTarget *target, const char *triple) { + Error err; + SplitIterator it = memSplit(str(triple), str("-")); + + Optional> opt_archsub = SplitIterator_next(&it); + Optional> opt_os = SplitIterator_next(&it); + Optional> opt_abi = SplitIterator_next(&it); + + if (!opt_archsub.is_some) + return ErrorMissingArchitecture; + + if (!opt_os.is_some) + return ErrorMissingOperatingSystem; + + if ((err = target_parse_archsub(&target->arch, &target->sub_arch, + (char*)opt_archsub.value.ptr, opt_archsub.value.len))) + { + return err; + } + + if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) { + return err; + } + + if (opt_abi.is_some) { + if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) { + return err; + } + } else { + target->abi = target_default_abi(target->arch, target->os); + } + + target->vendor = ZigLLVM_UnknownVendor; + target->is_native = false; + return ErrorNone; +} + +const char *target_arch_name(ZigLLVM_ArchType arch) { + return ZigLLVMGetArchTypeName(arch); } void init_all_targets(void) { @@ -519,37 +684,21 @@ void init_all_targets(void) { } void get_target_triple(Buf *triple, const ZigTarget *target) { - char arch_name[50]; - get_arch_name(arch_name, &target->arch); - buf_resize(triple, 0); - - // LLVM WebAssembly output support requires the target to be activated at - // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. - // - // LLVM determines the output format based on the environment suffix, - // defaulting to an object based on the architecture. The default format in - // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to - // explicitly set this ourself in order for it to work. - // - // This is fixed in LLVM 7 and you will be able to get wasm output by - // using the target triple `wasm32-unknown-unknown-unknown`. - if (!strncmp(arch_name, "wasm", 4)) { - buf_appendf(triple, "%s-%s-%s-wasm", arch_name, - ZigLLVMGetVendorTypeName(target->vendor), - ZigLLVMGetOSTypeName(get_llvm_os_type(target->os))); - } else { - buf_appendf(triple, "%s-%s-%s-%s", arch_name, - ZigLLVMGetVendorTypeName(target->vendor), - ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)), - ZigLLVMGetEnvironmentTypeName(target->env_type)); - } + buf_appendf(triple, "%s%s-%s-%s-%s", + ZigLLVMGetArchTypeName(target->arch), + ZigLLVMGetSubArchTypeName(target->sub_arch), + ZigLLVMGetVendorTypeName(target->vendor), + ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)), + ZigLLVMGetEnvironmentTypeName(target->abi)); } bool target_is_darwin(const ZigTarget *target) { switch (target->os) { case OsMacOSX: case OsIOS: + case OsWatchOS: + case OsTvOS: return true; default: return false; @@ -562,8 +711,8 @@ ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target) { } else if (target_is_darwin(target)) { return ZigLLVM_MachO; } - if (target->arch.arch == ZigLLVM_wasm32 || - target->arch.arch == ZigLLVM_wasm64) + if (target->arch == ZigLLVM_wasm32 || + target->arch == ZigLLVM_wasm64) { return ZigLLVM_Wasm; } @@ -639,7 +788,7 @@ static int get_arch_pointer_bit_width(ZigLLVM_ArchType arch) { uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { switch (target->os) { case OsFreestanding: - switch (target->arch.arch) { + switch (target->arch) { case ZigLLVM_msp430: switch (id) { case CIntTypeShort: @@ -667,7 +816,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { return 32; case CIntTypeLong: case CIntTypeULong: - return get_arch_pointer_bit_width(target->arch.arch); + return get_arch_pointer_bit_width(target->arch); case CIntTypeLongLong: case CIntTypeULongLong: return 64; @@ -690,7 +839,7 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { return 32; case CIntTypeLong: case CIntTypeULong: - return get_arch_pointer_bit_width(target->arch.arch); + return get_arch_pointer_bit_width(target->arch); case CIntTypeLongLong: case CIntTypeULongLong: return 64; @@ -748,7 +897,7 @@ bool target_allows_addr_zero(const ZigTarget *target) { } const char *target_o_file_ext(const ZigTarget *target) { - if (target->env_type == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) { + if (target->abi == ZigLLVM_MSVC || target->os == OsWindows || target->os == OsUefi) { return ".obj"; } else { return ".o"; @@ -798,7 +947,7 @@ enum FloatAbi { }; static FloatAbi get_float_abi(const ZigTarget *target) { - const ZigLLVM_EnvironmentType env = target->env_type; + const ZigLLVM_EnvironmentType env = target->abi; if (env == ZigLLVM_GNUEABIHF || env == ZigLLVM_EABIHF || env == ZigLLVM_MuslEABIHF) @@ -813,71 +962,154 @@ static bool is_64_bit(ZigLLVM_ArchType arch) { return get_arch_pointer_bit_width(arch) == 64; } -Buf *target_dynamic_linker(const ZigTarget *target) { - if (target->os == OsFreeBSD) { - return buf_create_from_str("/libexec/ld-elf.so.1"); - } - if (target->os == OsNetBSD) { - return buf_create_from_str("/libexec/ld.elf_so"); - } +const char *target_dynamic_linker(const ZigTarget *target) { + switch (target->os) { + case OsFreeBSD: + return "/libexec/ld-elf.so.1"; + case OsNetBSD: + return "/libexec/ld.elf_so"; + case OsLinux: { + const ZigLLVM_EnvironmentType abi = target->abi; + if (abi == ZigLLVM_Android) { + if (is_64_bit(target->arch)) { + return "/system/bin/linker64"; + } else { + return "/system/bin/linker"; + } + } - const ZigLLVM_ArchType arch = target->arch.arch; - const ZigLLVM_EnvironmentType env = target->env_type; + switch (target->arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_x86: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + return "/lib/ld-linux.so.2"; - if (env == ZigLLVM_Android) { - if (is_64_bit(arch)) { - return buf_create_from_str("/system/bin/linker64"); - } else { - return buf_create_from_str("/system/bin/linker"); + case ZigLLVM_aarch64: + return "/lib/ld-linux-aarch64.so.1"; + + case ZigLLVM_aarch64_be: + return "/lib/ld-linux-aarch64_be.so.1"; + + case ZigLLVM_arm: + case ZigLLVM_thumb: + if (get_float_abi(target) == FloatAbiHard) { + return "/lib/ld-linux-armhf.so.3"; + } else { + return "/lib/ld-linux.so.3"; + } + + case ZigLLVM_armeb: + case ZigLLVM_thumbeb: + if (get_float_abi(target) == FloatAbiHard) { + return "/lib/ld-linux-armhf.so.3"; + } else { + return "/lib/ld-linux.so.3"; + } + + case ZigLLVM_mips: + case ZigLLVM_mipsel: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + zig_panic("TODO implement target_dynamic_linker for mips"); + + case ZigLLVM_ppc: + return "/lib/ld.so.1"; + + case ZigLLVM_ppc64: + return "/lib64/ld64.so.2"; + + case ZigLLVM_ppc64le: + return "/lib64/ld64.so.2"; + + case ZigLLVM_systemz: + return "/lib64/ld64.so.1"; + + case ZigLLVM_sparcv9: + return "/lib64/ld-linux.so.2"; + + case ZigLLVM_x86_64: + if (abi == ZigLLVM_GNUX32) { + return "/libx32/ld-linux-x32.so.2"; + } + if (abi == ZigLLVM_Musl || abi == ZigLLVM_MuslEABI || abi == ZigLLVM_MuslEABIHF) { + return "/lib/ld-musl-x86_64.so.1"; + } + return "/lib64/ld-linux-x86-64.so.2"; + + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + return nullptr; + + case ZigLLVM_arc: + case ZigLLVM_avr: + case ZigLLVM_bpfel: + case ZigLLVM_bpfeb: + case ZigLLVM_hexagon: + case ZigLLVM_msp430: + case ZigLLVM_nios2: + case ZigLLVM_r600: + case ZigLLVM_amdgcn: + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_xcore: + case ZigLLVM_nvptx: + case ZigLLVM_nvptx64: + case ZigLLVM_le32: + case ZigLLVM_le64: + case ZigLLVM_amdil: + case ZigLLVM_amdil64: + case ZigLLVM_hsail: + case ZigLLVM_hsail64: + case ZigLLVM_spir: + case ZigLLVM_spir64: + case ZigLLVM_kalimba: + case ZigLLVM_shave: + case ZigLLVM_lanai: + case ZigLLVM_renderscript32: + case ZigLLVM_renderscript64: + zig_panic("TODO implement target_dynamic_linker for this arch"); + } + zig_unreachable(); } - } else if (arch == ZigLLVM_x86 || - arch == ZigLLVM_sparc || - arch == ZigLLVM_sparcel) - { - return buf_create_from_str("/lib/ld-linux.so.2"); - } else if (arch == ZigLLVM_aarch64) { - return buf_create_from_str("/lib/ld-linux-aarch64.so.1"); - } else if (arch == ZigLLVM_aarch64_be) { - return buf_create_from_str("/lib/ld-linux-aarch64_be.so.1"); - } else if (arch == ZigLLVM_arm || arch == ZigLLVM_thumb) { - if (get_float_abi(target) == FloatAbiHard) { - return buf_create_from_str("/lib/ld-linux-armhf.so.3"); - } else { - return buf_create_from_str("/lib/ld-linux.so.3"); - } - } else if (arch == ZigLLVM_armeb || arch == ZigLLVM_thumbeb) { - if (get_float_abi(target) == FloatAbiHard) { - return buf_create_from_str("/lib/ld-linux-armhf.so.3"); - } else { - return buf_create_from_str("/lib/ld-linux.so.3"); - } - } else if (arch == ZigLLVM_mips || arch == ZigLLVM_mipsel || - arch == ZigLLVM_mips64 || arch == ZigLLVM_mips64el) - { - // when you want to solve this TODO, grep clang codebase for - // getLinuxDynamicLinker - zig_panic("TODO figure out MIPS dynamic linker name"); - } else if (arch == ZigLLVM_ppc) { - return buf_create_from_str("/lib/ld.so.1"); - } else if (arch == ZigLLVM_ppc64) { - return buf_create_from_str("/lib64/ld64.so.2"); - } else if (arch == ZigLLVM_ppc64le) { - return buf_create_from_str("/lib64/ld64.so.2"); - } else if (arch == ZigLLVM_systemz) { - return buf_create_from_str("/lib64/ld64.so.1"); - } else if (arch == ZigLLVM_sparcv9) { - return buf_create_from_str("/lib64/ld-linux.so.2"); - } else if (arch == ZigLLVM_x86_64 && - env == ZigLLVM_GNUX32) - { - return buf_create_from_str("/libx32/ld-linux-x32.so.2"); - } else if (arch == ZigLLVM_x86_64 && - (env == ZigLLVM_Musl || env == ZigLLVM_MuslEABI || env == ZigLLVM_MuslEABIHF)) - { - return buf_create_from_str("/lib/ld-musl-x86_64.so.1"); - } else { - return buf_create_from_str("/lib64/ld-linux-x86-64.so.2"); + case OsFreestanding: + case OsIOS: + case OsTvOS: + case OsWatchOS: + case OsMacOSX: + return nullptr; + + case OsAnanas: + case OsCloudABI: + case OsDragonFly: + case OsFuchsia: + case OsKFreeBSD: + case OsLv2: + case OsOpenBSD: + case OsSolaris: + case OsWindows: + case OsHaiku: + case OsMinix: + case OsRTEMS: + case OsNaCl: + case OsCNK: + case OsAIX: + case OsCUDA: + case OsNVCL: + case OsAMDHSA: + case OsPS4: + case OsELFIAMCU: + case OsMesa3D: + case OsContiki: + case OsAMDPAL: + case OsZen: + case OsUefi: + zig_panic("TODO implement target_dynamic_linker for this OS"); } + zig_unreachable(); } bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target) { @@ -888,15 +1120,15 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target return true; } - if (guest_target->os == host_target->os && guest_target->arch.arch == host_target->arch.arch && - guest_target->arch.sub_arch == host_target->arch.sub_arch) + if (guest_target->os == host_target->os && guest_target->arch == host_target->arch && + guest_target->sub_arch == host_target->sub_arch) { // OS, arch, and sub-arch match return true; } if (guest_target->os == OsWindows && host_target->os == OsWindows && - host_target->arch.arch == ZigLLVM_x86_64 && guest_target->arch.arch == ZigLLVM_x86) + host_target->arch == ZigLLVM_x86_64 && guest_target->arch == ZigLLVM_x86) { // 64-bit windows can run 32-bit programs return true; @@ -905,8 +1137,8 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target return false; } -const char *arch_stack_pointer_register_name(const ArchType *arch) { - switch (arch->arch) { +const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { + switch (arch) { case ZigLLVM_UnknownArch: zig_unreachable(); case ZigLLVM_x86: @@ -969,7 +1201,7 @@ const char *arch_stack_pointer_register_name(const ArchType *arch) { } bool target_is_arm(const ZigTarget *target) { - switch (target->arch.arch) { + switch (target->arch) { case ZigLLVM_UnknownArch: zig_unreachable(); case ZigLLVM_aarch64: @@ -1031,12 +1263,12 @@ bool target_is_arm(const ZigTarget *target) { // Valgrind supports more, but Zig does not support them yet. bool target_has_valgrind_support(const ZigTarget *target) { - switch (target->arch.arch) { + switch (target->arch) { case ZigLLVM_UnknownArch: zig_unreachable(); case ZigLLVM_x86_64: return (target->os == OsLinux || target_is_darwin(target) || target->os == OsSolaris || - (target->os == OsWindows && target->env_type != ZigLLVM_MSVC)); + (target->os == OsWindows && target->abi != ZigLLVM_MSVC)); default: return false; } @@ -1055,3 +1287,46 @@ bool target_supports_fpic(const ZigTarget *target) { // C compiler argument is valid. return target->os != OsWindows; } + +ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { + switch (os) { + case OsFreestanding: + case OsAnanas: + case OsCloudABI: + case OsDragonFly: + case OsLv2: + case OsSolaris: + case OsHaiku: + case OsMinix: + case OsRTEMS: + case OsNaCl: + case OsCNK: + case OsAIX: + case OsCUDA: + case OsNVCL: + case OsAMDHSA: + case OsPS4: + case OsELFIAMCU: + case OsMesa3D: + case OsContiki: + case OsAMDPAL: + case OsZen: + return ZigLLVM_EABI; + case OsOpenBSD: + case OsMacOSX: + case OsFreeBSD: + case OsIOS: + case OsTvOS: + case OsWatchOS: + case OsFuchsia: + case OsKFreeBSD: + case OsNetBSD: + return ZigLLVM_GNU; + case OsWindows: + case OsUefi: + return ZigLLVM_MSVC; + case OsLinux: + return ZigLLVM_Musl; + } + zig_unreachable(); +} diff --git a/src/target.hpp b/src/target.hpp index dceeee2eca..e3bdc244dc 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -12,11 +12,6 @@ struct Buf; -struct ArchType { - ZigLLVM_ArchType arch; - ZigLLVM_SubArchType sub_arch; -}; - // Synchronize with target.cpp::os_list enum Os { OsFreestanding, @@ -54,6 +49,14 @@ enum Os { OsUefi, }; +// Synchronize with target.cpp::subarch_list_list +enum SubArchList { + SubArchListNone, + SubArchListArm32, + SubArchListArm64, + SubArchListKalimba, +}; + enum TargetSubsystem { TargetSubsystemAuto, // Zig should infer the subsystem TargetSubsystemConsole, @@ -67,10 +70,11 @@ enum TargetSubsystem { }; struct ZigTarget { - ArchType arch; + ZigLLVM_ArchType arch; + ZigLLVM_SubArchType sub_arch; ZigLLVM_VendorType vendor; Os os; - ZigLLVM_EnvironmentType env_type; + ZigLLVM_EnvironmentType abi; bool is_native; }; @@ -87,39 +91,50 @@ enum CIntType { CIntTypeCount, }; -size_t target_arch_count(void); -const ArchType *get_target_arch(size_t index); -void get_arch_name(char *out_str, const ArchType *arch); +Error target_parse_triple(ZigTarget *target, const char *triple); +Error target_parse_archsub(ZigLLVM_ArchType *arch, ZigLLVM_SubArchType *sub, + const char *archsub_ptr, size_t archsub_len); +Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); +Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); -const char *arch_stack_pointer_register_name(const ArchType *arch); +size_t target_arch_count(void); +ZigLLVM_ArchType target_arch_enum(size_t index); +const char *target_arch_name(ZigLLVM_ArchType arch); + +SubArchList target_subarch_list(ZigLLVM_ArchType arch); +size_t target_subarch_count(SubArchList sub_arch_list); +ZigLLVM_SubArchType target_subarch_enum(SubArchList subarch_list, size_t index); +const char *target_subarch_name(ZigLLVM_SubArchType subarch); + +size_t target_subarch_list_count(void); +SubArchList target_subarch_list_enum(size_t index); +const char *target_subarch_list_name(SubArchList sub_arch_list); + +const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch); size_t target_vendor_count(void); -ZigLLVM_VendorType get_target_vendor(size_t index); +ZigLLVM_VendorType target_vendor_enum(size_t index); size_t target_os_count(void); -Os get_target_os(size_t index); -const char *get_target_os_name(Os os_type); +Os target_os_enum(size_t index); +const char *target_os_name(Os os_type); -size_t target_environ_count(void); -ZigLLVM_EnvironmentType get_target_environ(size_t index); +size_t target_abi_count(void); +ZigLLVM_EnvironmentType target_abi_enum(size_t index); +const char *target_abi_name(ZigLLVM_EnvironmentType abi); +ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os); size_t target_oformat_count(void); -ZigLLVM_ObjectFormatType get_target_oformat(size_t index); -const char *get_target_oformat_name(ZigLLVM_ObjectFormatType oformat); +ZigLLVM_ObjectFormatType target_oformat_enum(size_t index); +const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat); ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target); void get_native_target(ZigTarget *target); -void get_unknown_target(ZigTarget *target); - -int parse_target_arch(const char *str, ArchType *arch); -int parse_target_os(const char *str, Os *os); -int parse_target_environ(const char *str, ZigLLVM_EnvironmentType *env_type); +void get_target_triple(Buf *triple, const ZigTarget *target); void init_all_targets(void); -void get_target_triple(Buf *triple, const ZigTarget *target); - void resolve_target_object_format(ZigTarget *target); uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id); @@ -131,7 +146,7 @@ const char *target_exe_file_ext(const ZigTarget *target); const char *target_lib_file_ext(const ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch); -Buf *target_dynamic_linker(const ZigTarget *target); +const char *target_dynamic_linker(const ZigTarget *target); bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); ZigLLVM_OSType get_llvm_os_type(Os os_type); diff --git a/src/util.hpp b/src/util.hpp index cc83022328..a0e759567e 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -147,11 +147,14 @@ static inline T clamp(T min_value, T value, T max_value) { return max(min(value, max_value), min_value); } -static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) { - size_t str_len = strlen(str); - if (str_len != mem_len) +static inline bool mem_eql_mem(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) { + if (a_len != b_len) return false; - return memcmp(mem, str, mem_len) == 0; + return memcmp(a_ptr, b_ptr, a_len) == 0; +} + +static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) { + return mem_eql_mem(mem, mem_len, str, strlen(str)); } static inline bool is_power_of_2(uint64_t x) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index e5a4890914..4a7712ca1e 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -730,7 +730,7 @@ void ZigLLVMGetNativeTarget(ZigLLVM_ArchType *arch_type, ZigLLVM_SubArchType *su const char *ZigLLVMGetSubArchTypeName(ZigLLVM_SubArchType sub_arch) { switch (sub_arch) { case ZigLLVM_NoSubArch: - return "(none)"; + return ""; case ZigLLVM_ARMSubArch_v8_4a: return "v8_4a"; case ZigLLVM_ARMSubArch_v8_3a: diff --git a/src/zig_llvm.h b/src/zig_llvm.h index b5b0e97b53..743df123f4 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -215,7 +215,7 @@ ZIG_EXTERN_C void ZigLLVMParseCommandLineOptions(size_t argc, const char *const // copied from include/llvm/ADT/Triple.h - +// synchronize with target.cpp::arch_list enum ZigLLVM_ArchType { ZigLLVM_UnknownArch, @@ -273,6 +273,7 @@ enum ZigLLVM_ArchType { ZigLLVM_LastArchType = ZigLLVM_renderscript64 }; +// synchronize with lists in target.cpp enum ZigLLVM_SubArchType { ZigLLVM_NoSubArch, @@ -404,7 +405,7 @@ ZIG_EXTERN_C const char *ZigLLVMGetArchTypeName(enum ZigLLVM_ArchType arch); ZIG_EXTERN_C const char *ZigLLVMGetSubArchTypeName(enum ZigLLVM_SubArchType sub_arch); ZIG_EXTERN_C const char *ZigLLVMGetVendorTypeName(enum ZigLLVM_VendorType vendor); ZIG_EXTERN_C const char *ZigLLVMGetOSTypeName(enum ZigLLVM_OSType os); -ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType env_type); +ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentType abi); ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, void (*append_diagnostic)(void *, const char *, size_t), void *context); diff --git a/std/build.zig b/std/build.zig index d88a9b3270..e90d827602 100644 --- a/std/build.zig +++ b/std/build.zig @@ -731,20 +731,40 @@ const Version = struct { const CrossTarget = struct { arch: builtin.Arch, os: builtin.Os, - environ: builtin.Environ, + abi: builtin.Abi, }; pub const Target = union(enum) { Native: void, Cross: CrossTarget, - pub fn oFileExt(self: *const Target) []const u8 { - const environ = switch (self.*) { - Target.Native => builtin.environ, - Target.Cross => |t| t.environ, + fn archSubArchName(arch: builtin.Arch) []const u8 { + return switch (arch) { + builtin.Arch.arm => |sub| @tagName(sub), + builtin.Arch.armeb => |sub| @tagName(sub), + builtin.Arch.thumb => |sub| @tagName(sub), + builtin.Arch.thumbeb => |sub| @tagName(sub), + builtin.Arch.aarch64 => |sub| @tagName(sub), + builtin.Arch.aarch64_be => |sub| @tagName(sub), + builtin.Arch.kalimba => |sub| @tagName(sub), + else => "", }; - return switch (environ) { - builtin.Environ.msvc => ".obj", + } + + pub fn subArchName(self: Target) []const u8 { + switch (self) { + Target.Native => return archSubArchName(builtin.arch), + Target.Cross => |cross| return archSubArchName(cross.arch), + } + } + + pub fn oFileExt(self: *const Target) []const u8 { + const abi = switch (self.*) { + Target.Native => builtin.abi, + Target.Cross => |t| t.abi, + }; + return switch (abi) { + builtin.Abi.msvc => ".obj", else => ".o", }; } @@ -978,12 +998,17 @@ pub const LibExeObjStep = struct { } } - pub fn setTarget(self: *LibExeObjStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void { + pub fn setTarget( + self: *LibExeObjStep, + target_arch: builtin.Arch, + target_os: builtin.Os, + target_abi: builtin.Abi, + ) void { self.target = Target{ .Cross = CrossTarget{ .arch = target_arch, .os = target_os, - .environ = target_environ, + .abi = target_abi, }, }; self.computeOutFileNames(); @@ -1296,14 +1321,16 @@ pub const LibExeObjStep = struct { switch (self.target) { Target.Native => {}, Target.Cross => |cross_target| { - zig_args.append("--target-arch") catch unreachable; - zig_args.append(@tagName(cross_target.arch)) catch unreachable; + const triple = builder.fmt( + "{}{}-{}-{}", + @tagName(cross_target.arch), + Target.archSubArchName(cross_target.arch), + @tagName(cross_target.os), + @tagName(cross_target.abi), + ); - zig_args.append("--target-os") catch unreachable; - zig_args.append(@tagName(cross_target.os)) catch unreachable; - - zig_args.append("--target-environ") catch unreachable; - zig_args.append(@tagName(cross_target.environ)) catch unreachable; + try zig_args.append("-target"); + try zig_args.append(triple); }, } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index f006dfdbdc..4f864b3662 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -1118,7 +1118,7 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); testing.expect(mem.eql(u8, result, "f64: nan\n")); } - if (builtin.arch != builtin.Arch.armv8) { + if (builtin.arch != builtin.Arch.arm) { // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan var buf1: [32]u8 = undefined; diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index ec90755164..b78c1b3da5 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -6,7 +6,7 @@ const vdso = @import("vdso.zig"); pub use switch (builtin.arch) { builtin.Arch.x86_64 => @import("x86_64.zig"), builtin.Arch.i386 => @import("i386.zig"), - builtin.Arch.aarch64v8 => @import("arm64.zig"), + builtin.Arch.aarch64 => @import("arm64.zig"), else => @compileError("unsupported arch"), }; pub use @import("errno.zig"); diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 1192d7e46f..950fd5cbb9 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -31,7 +31,7 @@ nakedcc fn _start() noreturn { : [argc] "=r" (-> [*]usize) ); }, - builtin.Arch.aarch64v8 => { + builtin.Arch.aarch64, builtin.Arch.aarch64_be => { argc_ptr = asm ("mov %[argc], sp" : [argc] "=r" (-> [*]usize) ); diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig index 6715df1805..7657ce6118 100644 --- a/std/special/compiler_rt/index.zig +++ b/std/special/compiler_rt/index.zig @@ -184,60 +184,10 @@ const is_arm_64 = switch (builtin.arch) { }; const is_arm_arch = switch (builtin.arch) { - builtin.Arch.armv8_3a, - builtin.Arch.armv8_2a, - builtin.Arch.armv8_1a, - builtin.Arch.armv8, - builtin.Arch.armv8r, - builtin.Arch.armv8m_baseline, - builtin.Arch.armv8m_mainline, - builtin.Arch.armv7, - builtin.Arch.armv7em, - builtin.Arch.armv7m, - builtin.Arch.armv7s, - builtin.Arch.armv7k, - builtin.Arch.armv7ve, - builtin.Arch.armv6, - builtin.Arch.armv6m, - builtin.Arch.armv6k, - builtin.Arch.armv6t2, - builtin.Arch.armv5, - builtin.Arch.armv5te, - builtin.Arch.armv4t, - builtin.Arch.armebv8_3a, - builtin.Arch.armebv8_2a, - builtin.Arch.armebv8_1a, - builtin.Arch.armebv8, - builtin.Arch.armebv8r, - builtin.Arch.armebv8m_baseline, - builtin.Arch.armebv8m_mainline, - builtin.Arch.armebv7, - builtin.Arch.armebv7em, - builtin.Arch.armebv7m, - builtin.Arch.armebv7s, - builtin.Arch.armebv7k, - builtin.Arch.armebv7ve, - builtin.Arch.armebv6, - builtin.Arch.armebv6m, - builtin.Arch.armebv6k, - builtin.Arch.armebv6t2, - builtin.Arch.armebv5, - builtin.Arch.armebv5te, - builtin.Arch.armebv4t, - builtin.Arch.aarch64v8_3a, - builtin.Arch.aarch64v8_2a, - builtin.Arch.aarch64v8_1a, - builtin.Arch.aarch64v8, - builtin.Arch.aarch64v8r, - builtin.Arch.aarch64v8m_baseline, - builtin.Arch.aarch64v8m_mainline, - builtin.Arch.aarch64_bev8_3a, - builtin.Arch.aarch64_bev8_2a, - builtin.Arch.aarch64_bev8_1a, - builtin.Arch.aarch64_bev8, - builtin.Arch.aarch64_bev8r, - builtin.Arch.aarch64_bev8m_baseline, - builtin.Arch.aarch64_bev8m_mainline, + builtin.Arch.arm, + builtin.Arch.armeb, + builtin.Arch.aarch64, + builtin.Arch.aarch64_be, builtin.Arch.thumb, builtin.Arch.thumbeb, => true, diff --git a/test/tests.zig b/test/tests.zig index 2591ab4f2a..582736e8d5 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -24,24 +24,24 @@ const gen_h = @import("gen_h.zig"); const TestTarget = struct { os: builtin.Os, arch: builtin.Arch, - environ: builtin.Environ, + abi: builtin.Abi, }; const test_targets = []TestTarget{ TestTarget{ .os = builtin.Os.linux, .arch = builtin.Arch.x86_64, - .environ = builtin.Environ.gnu, + .abi = builtin.Abi.gnu, }, TestTarget{ .os = builtin.Os.macosx, .arch = builtin.Arch.x86_64, - .environ = builtin.Environ.unknown, + .abi = builtin.Abi.gnu, }, TestTarget{ .os = builtin.Os.windows, .arch = builtin.Arch.x86_64, - .environ = builtin.Environ.msvc, + .abi = builtin.Abi.msvc, }, }; @@ -189,7 +189,7 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons these_tests.setFilter(test_filter); these_tests.setBuildMode(mode); if (!is_native) { - these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); + these_tests.setTarget(test_target.arch, test_target.os, test_target.abi); } if (link_libc) { these_tests.linkSystemLibrary("c"); From 33174f11ef5cbb3534217dbc1502886d97226778 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 16:24:32 -0500 Subject: [PATCH 121/134] fix regressions on Windows --- src-self-hosted/libc_installation.zig | 4 ++-- src/codegen.cpp | 13 ++++++------- src/libc_installation.cpp | 4 ++-- src/translate_c.cpp | 6 ++++++ 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index b196d7bc70..6a530da1f0 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -283,7 +283,7 @@ pub const LibCInstallation = struct { switch (builtin.arch) { builtin.Arch.i386 => try stream.write("x86"), builtin.Arch.x86_64 => try stream.write("x64"), - builtin.Arch.aarch64v8 => try stream.write("arm"), + builtin.Arch.aarch64 => try stream.write("arm"), else => return error.UnsupportedArchitecture, } const ucrt_lib_path = try std.os.path.join( @@ -361,7 +361,7 @@ pub const LibCInstallation = struct { switch (builtin.arch) { builtin.Arch.i386 => try stream.write("x86\\"), builtin.Arch.x86_64 => try stream.write("x64\\"), - builtin.Arch.aarch64v8 => try stream.write("arm\\"), + builtin.Arch.aarch64 => try stream.write("arm\\"), else => return error.UnsupportedArchitecture, } const kernel32_path = try std.os.path.join( diff --git a/src/codegen.cpp b/src/codegen.cpp index 4e8cd070f4..58682cdd88 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8174,20 +8174,19 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { args.append(buf_ptr(out_dep_path)); } - args.append("-nobuiltininc"); args.append("-nostdinc"); - args.append("-nostdinc++"); - - if (g->libc_link_lib == nullptr) { - args.append("-nolibc"); - } - args.append("-fno-spell-checking"); args.append("-isystem"); args.append(buf_ptr(g->zig_c_headers_dir)); if (g->libc != nullptr) { + if (buf_len(&g->libc->msvc_lib_dir) != 0) { + Buf *include_dir = buf_sprintf("%s" OS_SEP ".." OS_SEP ".." OS_SEP "include", buf_ptr(&g->libc->msvc_lib_dir)); + args.append("-isystem"); + args.append(buf_ptr(include_dir)); + } + args.append("-isystem"); args.append(buf_ptr(&g->libc->include_dir)); } diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 6e410ad5ac..5591aa1bbb 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -172,7 +172,7 @@ static Error zig_libc_find_crt_dir_windows(ZigLibCInstallation *self, ZigWindows bool verbose) { Error err; - if ((err = os_get_win32_ucrt_lib_path(sdk, &self->crt_dir, target->arch.arch))) { + if ((err = os_get_win32_ucrt_lib_path(sdk, &self->crt_dir, target->arch))) { if (verbose) { fprintf(stderr, "Unable to determine ucrt path: %s\n", err_str(err)); } @@ -184,7 +184,7 @@ static Error zig_libc_find_kernel32_lib_dir(ZigLibCInstallation *self, ZigWindow bool verbose) { Error err; - if ((err = os_get_win32_kern32_path(sdk, &self->kernel32_lib_dir, target->arch.arch))) { + if ((err = os_get_win32_kern32_path(sdk, &self->kernel32_lib_dir, target->arch))) { if (verbose) { fprintf(stderr, "Unable to determine kernel32 path: %s\n", err_str(err)); } diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 8525d73f56..c38de6bf8c 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4815,6 +4815,12 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); if (codegen->libc != nullptr) { + if (buf_len(&codegen->libc->msvc_lib_dir) != 0) { + Buf *include_dir = buf_sprintf("%s" OS_SEP ".." OS_SEP ".." OS_SEP "include", buf_ptr(&codegen->libc->msvc_lib_dir)); + clang_argv.append("-isystem"); + clang_argv.append(buf_ptr(include_dir)); + } + clang_argv.append("-isystem"); clang_argv.append(buf_ptr(&codegen->libc->include_dir)); } From 6365f5a9f3f60c1a65f91dcf2926fefee4228b21 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 17:17:20 -0500 Subject: [PATCH 122/134] introduce sys_include_dir for when sys/* files are not with stdlib.h --- src/codegen.cpp | 12 ++--- src/libc_installation.cpp | 96 ++++++++++++++++++++++++++++++++------- src/libc_installation.hpp | 1 + src/translate_c.cpp | 11 ++--- 4 files changed, 92 insertions(+), 28 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 58682cdd88..78bd037839 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8181,14 +8181,13 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { args.append(buf_ptr(g->zig_c_headers_dir)); if (g->libc != nullptr) { - if (buf_len(&g->libc->msvc_lib_dir) != 0) { - Buf *include_dir = buf_sprintf("%s" OS_SEP ".." OS_SEP ".." OS_SEP "include", buf_ptr(&g->libc->msvc_lib_dir)); - args.append("-isystem"); - args.append(buf_ptr(include_dir)); - } - args.append("-isystem"); args.append(buf_ptr(&g->libc->include_dir)); + + if (!buf_eql_buf(&g->libc->include_dir, &g->libc->sys_include_dir)) { + args.append("-isystem"); + args.append(buf_ptr(&g->libc->sys_include_dir)); + } } if (g->zig_target->is_native) { @@ -8848,6 +8847,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); if (g->libc) { cache_buf(ch, &g->libc->include_dir); + cache_buf(ch, &g->libc->sys_include_dir); cache_buf(ch, &g->libc->crt_dir); cache_buf(ch, &g->libc->lib_dir); cache_buf(ch, &g->libc->static_lib_dir); diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 5591aa1bbb..2a8aade6a5 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -12,6 +12,7 @@ static const char *zig_libc_keys[] = { "include_dir", + "sys_include_dir", "crt_dir", "lib_dir", "static_lib_dir", @@ -34,6 +35,7 @@ static bool zig_libc_match_key(Slice name, Slice value, bool * static void zig_libc_init_empty(ZigLibCInstallation *libc) { *libc = {}; buf_init_from_str(&libc->include_dir, ""); + buf_init_from_str(&libc->sys_include_dir, ""); buf_init_from_str(&libc->crt_dir, ""); buf_init_from_str(&libc->lib_dir, ""); buf_init_from_str(&libc->static_lib_dir, ""); @@ -46,7 +48,7 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget Error err; zig_libc_init_empty(libc); - bool found_keys[array_length(zig_libc_keys)] = {}; // zig_libc_keys_len + bool found_keys[array_length(zig_libc_keys)] = {}; Buf *contents = buf_alloc(); if ((err = os_fetch_file_path(libc_file, contents, false))) { @@ -76,12 +78,13 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget Slice value = SplitIterator_rest(&line_it); bool match = false; match = match || zig_libc_match_key(name, value, found_keys, 0, &libc->include_dir); - match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->crt_dir); - match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->static_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->msvc_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->kernel32_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 6, &libc->dynamic_linker_path); + match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->sys_include_dir); + match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->crt_dir); + match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->static_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->msvc_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 6, &libc->kernel32_lib_dir); + match = match || zig_libc_match_key(name, value, found_keys, 7, &libc->dynamic_linker_path); } for (size_t i = 0; i < zig_libc_keys_len; i += 1) { @@ -100,6 +103,13 @@ Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget return ErrorSemanticAnalyzeFail; } + if (buf_len(&libc->sys_include_dir) == 0) { + if (verbose) { + fprintf(stderr, "sys_include_dir may not be empty\n"); + } + return ErrorSemanticAnalyzeFail; + } + if (buf_len(&libc->crt_dir) == 0) { if (!target_is_darwin(target)) { if (verbose) { @@ -195,13 +205,40 @@ static Error zig_libc_find_kernel32_lib_dir(ZigLibCInstallation *self, ZigWindow static Error zig_libc_find_native_msvc_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { if (sdk->msvc_lib_dir_ptr == nullptr) { if (verbose) { - fprintf(stderr, "Unable to determine vcruntime path\n"); + fprintf(stderr, "Unable to determine vcruntime.lib path\n"); } return ErrorFileNotFound; } buf_init_from_mem(&self->msvc_lib_dir, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); return ErrorNone; } +static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { + Error err; + if (sdk->msvc_lib_dir_ptr == nullptr) { + if (verbose) { + fprintf(stderr, "Unable to determine vcruntime.h path\n"); + } + return ErrorFileNotFound; + } + Buf search_path = BUF_INIT; + buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); + buf_append_str(&search_path, "\\..\\..\\include"); + + Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path)); + bool exists; + if ((err = os_file_exists(vcruntime_path, &exists))) { + exists = false; + } + if (exists) { + self->sys_include_dir = search_path; + return ErrorNone; + } + + if (verbose) { + fprintf(stderr, "Unable to determine vcruntime.h path\n"); + } + return ErrorFileNotFound; +} #else static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, bool verbose) { const char *cc_exe = getenv("CC"); @@ -253,18 +290,38 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b while (*search_path == ' ') { search_path += 1; } - Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path); - bool exists; - if ((err = os_file_exists(stdlib_path, &exists))) { - exists = false; + + if (buf_len(&self->include_dir) == 0) { + Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path); + bool exists; + if ((err = os_file_exists(stdlib_path, &exists))) { + exists = false; + } + if (exists) { + buf_init_from_str(&self->include_dir, search_path); + } } - if (exists) { - buf_init_from_str(&self->include_dir, search_path); + if (buf_len(&self->sys_include_dir) == 0) { + Buf *stdlib_path = buf_sprintf("%s/sys/errno.h", search_path); + bool exists; + if ((err = os_file_exists(stdlib_path, &exists))) { + exists = false; + } + if (exists) { + buf_init_from_str(&self->sys_include_dir, search_path); + } + } + if (buf_len(&self->include_dir) != 0 && buf_len(&self->sys_include_dir) != 0) { return ErrorNone; } } if (verbose) { - fprintf(stderr, "unable to determine libc include path: stdlib.h not found in '%s' search paths\n", cc_exe); + if (buf_len(&self->include_dir) == 0) { + fprintf(stderr, "unable to determine libc include path: stdlib.h not found in '%s' search paths\n", cc_exe); + } + if (buf_len(&self->sys_include_dir) == 0) { + fprintf(stderr, "unable to determine libc include path: sys/errno.h not found in '%s' search paths\n", cc_exe); + } } return ErrorFileNotFound; } @@ -345,8 +402,12 @@ static Error zig_libc_find_native_dynamic_linker_posix(ZigLibCInstallation *self void zig_libc_render(ZigLibCInstallation *self, FILE *file) { fprintf(file, "# The directory that contains `stdlib.h`.\n" - "# On POSIX, can be found with: `cc -E -Wp,-v -xc /dev/null`\n" + "# On POSIX, include directories be found with: `cc -E -Wp,-v -xc /dev/null`\n" "include_dir=%s\n" + "# The system-specific include directory. May be the same as `include_dir`.\n" + "# On Windows it's the directory that includes `vcruntime.h`.\n" + "# On POSIX it's the directory that includes `sys/errno.h`.\n" + "sys_include_dir=%s\n" "\n" "# The directory that contains `crt1.o`.\n" "# On POSIX, can be found with `cc -print-file-name=crt1.o`.\n" @@ -377,6 +438,7 @@ void zig_libc_render(ZigLibCInstallation *self, FILE *file) { "\n" , buf_ptr(&self->include_dir), + buf_ptr(&self->sys_include_dir), buf_ptr(&self->crt_dir), buf_ptr(&self->lib_dir), buf_ptr(&self->static_lib_dir), @@ -395,6 +457,8 @@ Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) { ZigWindowsSDK *sdk; switch (zig_find_windows_sdk(&sdk)) { case ZigFindWindowsSdkErrorNone: + if ((err = zig_libc_find_native_msvc_include_dir(self, sdk, verbose))) + return err; if ((err = zig_libc_find_native_msvc_lib_dir(self, sdk, verbose))) return err; if ((err = zig_libc_find_kernel32_lib_dir(self, sdk, &native_target, verbose))) diff --git a/src/libc_installation.hpp b/src/libc_installation.hpp index 7a1fb37d67..167f9ee834 100644 --- a/src/libc_installation.hpp +++ b/src/libc_installation.hpp @@ -17,6 +17,7 @@ // Must be synchronized with zig_libc_keys struct ZigLibCInstallation { Buf include_dir; + Buf sys_include_dir; Buf crt_dir; Buf lib_dir; Buf static_lib_dir; diff --git a/src/translate_c.cpp b/src/translate_c.cpp index c38de6bf8c..a0db339065 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -4815,14 +4815,13 @@ Error parse_h_file(ImportTableEntry *import, ZigList *errors, const clang_argv.append(buf_ptr(codegen->zig_c_headers_dir)); if (codegen->libc != nullptr) { - if (buf_len(&codegen->libc->msvc_lib_dir) != 0) { - Buf *include_dir = buf_sprintf("%s" OS_SEP ".." OS_SEP ".." OS_SEP "include", buf_ptr(&codegen->libc->msvc_lib_dir)); - clang_argv.append("-isystem"); - clang_argv.append(buf_ptr(include_dir)); - } - clang_argv.append("-isystem"); clang_argv.append(buf_ptr(&codegen->libc->include_dir)); + + if (!buf_eql_buf(&codegen->libc->include_dir, &codegen->libc->sys_include_dir)) { + clang_argv.append("-isystem"); + clang_argv.append(buf_ptr(&codegen->libc->sys_include_dir)); + } } // windows c runtime requires -D_DEBUG if using debug libraries From 4563f6b4242957971d619aa23263938fdcb442d3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 18:10:40 -0500 Subject: [PATCH 123/134] add builder.addFmt API and use it to test stage1 zig fmt closes #1968 --- CMakeLists.txt | 1 + build.zig | 7 +++++++ std/build.zig | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab69a0d081..830a3456cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -448,6 +448,7 @@ set(ZIG_STD_FILES "buf_set.zig" "buffer.zig" "build.zig" + "build/fmt.zig" "c/darwin.zig" "c/freebsd.zig" "c/index.zig" diff --git a/build.zig b/build.zig index 71bbfd0b23..756ec53708 100644 --- a/build.zig +++ b/build.zig @@ -55,6 +55,8 @@ pub fn build(b: *Builder) !void { var test_stage2 = b.addTest("src-self-hosted/test.zig"); test_stage2.setBuildMode(builtin.Mode.Debug); + const fmt_build_zig = b.addFmt([][]const u8{"build.zig"}); + var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); exe.setBuildMode(mode); @@ -106,6 +108,11 @@ pub fn build(b: *Builder) !void { } const modes = chosen_modes[0..chosen_mode_index]; + // run stage1 `zig fmt` on this build.zig file just to make sure it works + test_step.dependOn(&fmt_build_zig.step); + const fmt_step = b.step("test-fmt", "Run zig fmt against build.zig to make sure it works"); + fmt_step.dependOn(&fmt_build_zig.step); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); diff --git a/std/build.zig b/std/build.zig index e90d827602..e3fdfbccee 100644 --- a/std/build.zig +++ b/std/build.zig @@ -15,6 +15,8 @@ const BufSet = std.BufSet; const BufMap = std.BufMap; const fmt_lib = std.fmt; +pub const FmtStep = @import("build/fmt.zig").FmtStep; + pub const Builder = struct { uninstall_tls: TopLevelStep, install_tls: TopLevelStep, @@ -205,6 +207,10 @@ pub const Builder = struct { return remove_dir_step; } + pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep { + return FmtStep.create(self, paths); + } + pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { return Version{ .major = major, From 1d7e5479b0826776e53b499811198356bc180706 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 18:33:27 -0500 Subject: [PATCH 124/134] fix .gitignore file and add commit missing std lib file --- .gitignore | 6 +++--- std/build/fmt.zig | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 std/build/fmt.zig diff --git a/.gitignore b/.gitignore index 4b7bff11a6..f85fc969b1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,6 @@ # -andrewrk zig-cache/ -build/ -build-*/ -docgen_tmp/ +/build/ +/build-*/ +/docgen_tmp/ diff --git a/std/build/fmt.zig b/std/build/fmt.zig new file mode 100644 index 0000000000..ec0536dd34 --- /dev/null +++ b/std/build/fmt.zig @@ -0,0 +1,35 @@ +const std = @import("../index.zig"); +const build = @import("../build.zig"); +const Step = build.Step; +const Builder = build.Builder; +const BufMap = std.BufMap; +const mem = std.mem; + +pub const FmtStep = struct { + step: Step, + builder: *Builder, + argv: [][]const u8, + + pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep { + const self = builder.allocator.create(FmtStep) catch unreachable; + const name = "zig fmt"; + self.* = FmtStep{ + .step = Step.init(name, builder.allocator, make), + .builder = builder, + .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable, + }; + + self.argv[0] = builder.zig_exe; + self.argv[1] = "fmt"; + for (paths) |path, i| { + self.argv[2 + i] = builder.pathFromRoot(path); + } + return self; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(FmtStep, "step", step); + + return self.builder.spawnChild(self.argv); + } +}; From d5fc826437e16653087d023388f8d6a230a71d68 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 19:34:22 -0500 Subject: [PATCH 125/134] add test coverage for binary OR on error sets closes #1074 --- test/compile_errors.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 58a7190c67..81e47e171a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,17 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "binary OR operator on error sets", + \\pub const A = error.A; + \\pub const AB = A | error.B; + \\export fn entry() void { + \\ var x: AB = undefined; + \\} + , + ".tmp_source.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", + ); + if (builtin.os == builtin.Os.linux) { cases.addTest( "implicit dependency on libc", From 763357c9c3e8feba34a932c60b55f2224989f2d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 19:41:36 -0500 Subject: [PATCH 126/134] add test coverage for type used as switch case closes #996 --- test/compile_errors.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 81e47e171a..1e6dc84605 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,25 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest( + "not an enum type", + \\export fn entry() void { + \\ var self: Error = undefined; + \\ switch (self) { + \\ InvalidToken => |x| return x.token, + \\ ExpectedVarDeclOrFn => |x| return x.token, + \\ } + \\} + \\const Error = union(enum) { + \\ A: InvalidToken, + \\ B: ExpectedVarDeclOrFn, + \\}; + \\const InvalidToken = struct {}; + \\const ExpectedVarDeclOrFn = struct {}; + , + ".tmp_source.zig:4:9: error: not an enum type", + ); + cases.addTest( "binary OR operator on error sets", \\pub const A = error.A; From 557983e5020d0a28dba1f63ee9e515c6b779bfd4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 19:42:39 -0500 Subject: [PATCH 127/134] fix handling when there are multiple externs and an export in the same object closes #529 --- src/all_types.hpp | 2 +- src/analyze.cpp | 4 ++-- src/codegen.cpp | 17 +++++++++++++++-- src/ir.cpp | 14 ++++++++++++-- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/529.zig | 15 +++++++++++++++ test/stage1/behavior/bugs/529_other_file.zig | 5 +++++ test/stage1/behavior/bugs/529_other_file_2.zig | 4 ++++ 8 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 test/stage1/behavior/bugs/529.zig create mode 100644 test/stage1/behavior/bugs/529_other_file.zig create mode 100644 test/stage1/behavior/bugs/529_other_file_2.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 6876aaa3ba..902c6e13c2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1679,7 +1679,7 @@ struct CodeGen { HashMap generic_table; HashMap memoized_fn_eval_table; HashMap llvm_fn_table; - HashMap exported_symbol_names; + HashMap exported_symbol_names; HashMap external_prototypes; HashMap string_literals_table; HashMap type_info_cache; diff --git a/src/analyze.cpp b/src/analyze.cpp index a6c158a780..3ceda41934 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3439,9 +3439,9 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { if (is_export) { g->resolve_queue.append(tld); - auto entry = g->exported_symbol_names.put_unique(tld->name, tld->source_node); + auto entry = g->exported_symbol_names.put_unique(tld->name, tld); if (entry) { - AstNode *other_source_node = entry->value; + AstNode *other_source_node = entry->value->source_node; ErrorMsg *msg = add_node_error(g, tld->source_node, buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name))); add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here")); diff --git a/src/codegen.cpp b/src/codegen.cpp index 78bd037839..520c1eed00 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -478,10 +478,23 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { fn_table_entry->llvm_value = LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, 0)); return fn_table_entry->llvm_value; } else { - fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + auto entry = g->exported_symbol_names.maybe_get(symbol_name); + if (entry == nullptr) { + fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + } else { + assert(entry->value->id == TldIdFn); + TldFn *tld_fn = reinterpret_cast(entry->value); + tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), + tld_fn->fn_entry->type_entry->data.fn.raw_type_ref); + fn_table_entry->llvm_value = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, + LLVMPointerType(fn_llvm_type, 0)); + return fn_table_entry->llvm_value; + } } } else { - fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + if (fn_table_entry->llvm_value == nullptr) { + fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type); + } for (size_t i = 1; i < fn_table_entry->export_list.length; i += 1) { FnExport *fn_export = &fn_table_entry->export_list.items[i]; diff --git a/src/ir.cpp b/src/ir.cpp index d59acb7015..87e03ea519 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13279,12 +13279,21 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio } } - auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, instruction->base.source_node); + // TODO: This function needs to be audited. + // It's not clear how all the different types are supposed to be handled. + // Need comprehensive tests for exporting one thing in one file and declaring an extern var + // in another file. + TldFn *tld_fn = allocate(1); + tld_fn->base.id = TldIdFn; + tld_fn->base.source_node = instruction->base.source_node; + + auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base); if (entry) { - AstNode *other_export_node = entry->value; + AstNode *other_export_node = entry->value->source_node; ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name))); add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here")); + return ira->codegen->invalid_instruction; } bool want_var_export = false; @@ -13295,6 +13304,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdFn: { assert(target->value.data.x_ptr.special == ConstPtrSpecialFunction); ZigFn *fn_entry = target->value.data.x_ptr.data.fn.fn_entry; + tld_fn->fn_entry = fn_entry; CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; switch (cc) { case CallingConventionUnspecified: { diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 07158ae784..d1286cbf8a 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -22,6 +22,7 @@ comptime { _ = @import("behavior/bugs/2006.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); + _ = @import("behavior/bugs/529.zig"); _ = @import("behavior/bugs/655.zig"); _ = @import("behavior/bugs/656.zig"); _ = @import("behavior/bugs/704.zig"); diff --git a/test/stage1/behavior/bugs/529.zig b/test/stage1/behavior/bugs/529.zig new file mode 100644 index 0000000000..08d6768f7c --- /dev/null +++ b/test/stage1/behavior/bugs/529.zig @@ -0,0 +1,15 @@ +const A = extern struct { + field: c_int, +}; + +extern fn issue529(?*A) void; + +comptime { + _ = @import("529_other_file_2.zig"); +} + +test "issue 529 fixed" { + @import("529_other_file.zig").issue529(null); + issue529(null); +} + diff --git a/test/stage1/behavior/bugs/529_other_file.zig b/test/stage1/behavior/bugs/529_other_file.zig new file mode 100644 index 0000000000..4ee4e31a10 --- /dev/null +++ b/test/stage1/behavior/bugs/529_other_file.zig @@ -0,0 +1,5 @@ +pub const A = extern struct { + field: c_int, +}; + +pub extern fn issue529(?*A) void; diff --git a/test/stage1/behavior/bugs/529_other_file_2.zig b/test/stage1/behavior/bugs/529_other_file_2.zig new file mode 100644 index 0000000000..b493959e50 --- /dev/null +++ b/test/stage1/behavior/bugs/529_other_file_2.zig @@ -0,0 +1,4 @@ +pub const A = extern struct { + field: c_int, +}; +export fn issue529(a: ?*A) void {} From 0d48011f5eb15d02b7caf9f5f01c3eecb5c4754f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 21:33:35 -0500 Subject: [PATCH 128/134] fix stage1 zig fmt on macos --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index 6af5dc3c37..d2099a9f80 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -568,6 +568,7 @@ int main(int argc, char **argv) { os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); CodeGen *g = codegen_create(fmt_runner_path, &target, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), nullptr, nullptr); + buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); g->valgrind_support = valgrind_support; g->is_single_threaded = true; codegen_set_out_name(g, buf_create_from_str("fmt")); From 22dd0db9bf18e225799e8540ec27451fc48ea86e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 22:46:35 -0500 Subject: [PATCH 129/134] improve docs for unions and switching on tagged unions closes #1943 --- doc/langref.html.in | 239 +++++++++++++++++++++++++++----------------- 1 file changed, 150 insertions(+), 89 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7c1a1a81d3..9dc0a31b23 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2046,6 +2046,13 @@ test "linked list" { assert(list2.first.?.data == 1234); } {#code_end#} + + {#header_open|extern struct#} +

    An {#syntax#}extern struct{#endsyntax#} has in-memory layout guaranteed to match the + C ABI for the target.

    + {#see_also|extern union|extern enum#} + {#header_close#} + {#header_open|packed struct#}

    Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: @@ -2412,12 +2419,32 @@ test "packed enum" { {#see_also|@memberName|@memberCount|@tagName|@sizeOf#} {#header_close#} {#header_open|union#} - {#code_begin|test|union#} -const std = @import("std"); -const assert = std.debug.assert; -const mem = std.mem; - -// A union has only 1 active field at a time. +

    + A bare {#syntax#}union{#endsyntax#} defines a set of possible types that a value + can be as a list of fields. Only one field can be active at a time. + The in-memory representation of bare unions is not guaranteed. + Bare unions cannot be used to reinterpret memory. For that, use {#link|@ptrCast#}, + or use an {#link|extern union#} or a {#link|packed union#} which have + guaranteed in-memory layout. + {#link|Accessing the non-active field|Wrong Union Field Access#} is + safety-checked {#link|Undefined Behavior#}: +

    + {#code_begin|test_err|inactive union field#} +const Payload = union { + Int: i64, + Float: f64, + Bool: bool, +}; +test "simple union" { + var payload = Payload{ .Int = 1234 }; + payload.Float = 12.34; +} + {#code_end#} +

    You can activate another field by assigning the entire union:

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + const Payload = union { Int: i64, Float: f64, @@ -2425,14 +2452,25 @@ const Payload = union { }; test "simple union" { var payload = Payload{ .Int = 1234 }; - // payload.Float = 12.34; // ERROR! field not active assert(payload.Int == 1234); - // You can activate another field by assigning the entire union. payload = Payload{ .Float = 12.34 }; assert(payload.Float == 12.34); } + {#code_end#} +

    + In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}. +

    + + {#header_open|Tagged union#} +

    Unions can be declared with an enum tag type. + This turns the union into a tagged union, which makes it eligible + to use with {#link|switch#} expressions. One can use {#link|@TagType#} to + obtain the enum type from the union type. +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; -// Unions can be given an enum tag type: const ComplexTypeTag = enum { Ok, NotOk, @@ -2442,56 +2480,68 @@ const ComplexType = union(ComplexTypeTag) { NotOk: void, }; -// Declare a specific instance of the union variant. -test "declare union value" { - const c = ComplexType{ .Ok = 0 }; +test "switch on tagged union" { + const c = ComplexType{ .Ok = 42 }; assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); + + switch (c) { + ComplexTypeTag.Ok => |value| assert(value == 42), + ComplexTypeTag.NotOk => unreachable, + } } -// @TagType can be used to access the enum tag type of a tagged union. test "@TagType" { assert(@TagType(ComplexType) == ComplexTypeTag); } + {#code_end#} +

    In order to modify the payload of a tagged union in a switch expression, + place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; -// Unions can be made to infer the enum tag type. -const Foo = union(enum) { - String: []const u8, - Number: u64, - - // void can be omitted when inferring enum tag type. - None, +const ComplexTypeTag = enum { + Ok, + NotOk, +}; +const ComplexType = union(ComplexTypeTag) { + Ok: u8, + NotOk: void, }; -test "union variant switch" { - const p = Foo{ .Number = 54 }; - const what_is_it = switch (p) { - // Capture by reference - Foo.String => |*x| blk: { - break :blk "this is a string"; - }, - // Capture by value - Foo.Number => |x| blk: { - assert(x == 54); - break :blk "this is a number"; - }, +test "modify tagged union in switch" { + var c = ComplexType{ .Ok = 42 }; + assert(ComplexTypeTag(c) == ComplexTypeTag.Ok); - Foo.None => blk: { - break :blk "this is a none"; - }, - }; - assert(mem.eql(u8, what_is_it, "this is a number")); + switch (c) { + ComplexTypeTag.Ok => |*value| value.* += 1, + ComplexTypeTag.NotOk => unreachable, + } + + assert(c.Ok == 43); } - -// Unions can have methods just like structs and enums: + {#code_end#} +

    + Unions can be made to infer the enum tag type. + Further, unions can have methods just like structs and enums. +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; const Variant = union(enum) { Int: i32, Bool: bool, + // void can be omitted when inferring enum tag type. + None, + fn truthy(self: Variant) bool { return switch (self) { Variant.Int => |x_int| x_int != 0, Variant.Bool => |x_bool| x_bool, + Variant.None => false, }; } }; @@ -2503,38 +2553,34 @@ test "union method" { assert(v1.truthy()); assert(!v2.truthy()); } + {#code_end#} +

    + {#link|@tagName#} can be used to return a {#link|comptime#} + {#syntax#}[]const u8{#endsyntax#} value representing the field name: +

    + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; -const Small = union { - A: i32, - B: bool, - C: u8, -}; - -// @memberCount tells how many fields a union has: -test "@memberCount" { - assert(@memberCount(Small) == 3); -} - -// @memberName tells the name of a field in an enum: -test "@memberName" { - assert(mem.eql(u8, @memberName(Small, 1), "B")); -} - -// @tagName gives a []const u8 representation of an enum value, -// but only if the union has an enum tag type. const Small2 = union(enum) { A: i32, B: bool, C: u8, }; test "@tagName" { - assert(mem.eql(u8, @tagName(Small2.C), "C")); + assert(std.mem.eql(u8, @tagName(Small2.C), "C")); } {#code_end#} + {#header_close#} + + {#header_open|extern union#}

    - Unions with an enum tag are generated as a struct with a tag field and union field. Zig - sorts the order of the tag and union field by the largest alignment. + An {#syntax#}extern union{#endsyntax#} has memory layout guaranteed to be compatible with + the target C ABI.

    + {#see_also|extern struct#} + {#header_close#} + {#header_open|packed union#}

    A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible to be in a {#link|packed struct#}. @@ -2623,7 +2669,7 @@ test "switch simple" { // Ranges can be specified using the ... syntax. These are inclusive // both ends. - 5 ... 100 => 1, + 5...100 => 1, // Branches can be arbitrarily complex. 101 => blk: { @@ -2649,34 +2695,6 @@ test "switch simple" { assert(b == 1); } -test "switch enum" { - const Item = union(enum) { - A: u32, - C: struct { x: u8, y: u8 }, - D, - }; - - var a = Item { .A = 3 }; - - // Switching on more complex enums is allowed. - const b = switch (a) { - // A capture group is allowed on a match, and will return the enum - // value matched. - Item.A => |item| item, - - // A reference to the matched value can be obtained using `*` syntax. - Item.C => |*item| blk: { - item.*.x += 1; - break :blk 6; - }, - - // No else is required if the types cases was exhaustively handled - Item.D => 8, - }; - - assert(b == 3); -} - // Switch expressions can be used outside a function: const os_msg = switch (builtin.os) { builtin.Os.linux => "we found a linux user", @@ -2695,6 +2713,48 @@ test "switch inside function" { }, else => {}, } +} + {#code_end#} +

    + {#syntax#}switch{#endsyntax#} can be used to capture the field values + of a {#link|Tagged union#}. Modifications to the field values can be + done by placing a {#syntax#}*{#endsyntax#} before the capture variable name, + turning it into a pointer. +

    + {#code_begin|test#} +const assert = @import("std").debug.assert; + +test "switch on tagged union" { + const Point = struct { + x: u8, + y: u8, + }; + const Item = union(enum) { + A: u32, + C: Point, + D, + }; + + var a = Item{ .C = Point{ .x = 1, .y = 2 } }; + + // Switching on more complex enums is allowed. + const b = switch (a) { + // A capture group is allowed on a match, and will return the enum + // value matched. + Item.A => |item| item, + + // A reference to the matched value can be obtained using `*` syntax. + Item.C => |*item| blk: { + item.*.x += 1; + break :blk 6; + }, + + // No else is required if the types cases was exhaustively handled + Item.D => 8, + }; + + assert(b == 6); + assert(a.C.x == 2); } {#code_end#} {#see_also|comptime|enum|@compileError|Compile Variables#} @@ -7630,6 +7690,7 @@ fn bar(f: *Foo) void { f.float = 12.34; } {#code_end#} + {#see_also|union|extern union#} {#header_close#} {#header_open|Out of Bounds Float to Integer Cast#} From c59ce046a0ad468ac3f80c2d43ffa056772df7f1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Feb 2019 23:02:57 -0500 Subject: [PATCH 130/134] windows returns EINVAL for fopen when there is an asterisk in the name closes #508 --- src/error.cpp | 1 + src/error.hpp | 1 + src/os.cpp | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/error.cpp b/src/error.cpp index 80f7963538..17d44bffed 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -42,6 +42,7 @@ const char *err_str(Error err) { case ErrorUnknownArchitecture: return "unrecognized architecture"; case ErrorUnknownOperatingSystem: return "unrecognized operating system"; case ErrorUnknownABI: return "unrecognized C ABI"; + case ErrorInvalidFilename: return "invalid filename"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 50dfeecc32..6d9727a217 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -44,6 +44,7 @@ enum Error { ErrorUnknownArchitecture, ErrorUnknownOperatingSystem, ErrorUnknownABI, + ErrorInvalidFilename, }; const char *err_str(Error err); diff --git a/src/os.cpp b/src/os.cpp index f52325af00..732baea359 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1099,7 +1099,7 @@ Error os_fetch_file_path(Buf *full_path, Buf *out_contents, bool skip_shebang) { case EINTR: return ErrorInterrupted; case EINVAL: - zig_unreachable(); + return ErrorInvalidFilename; case ENFILE: case ENOMEM: return ErrorSystemResources; From d093f51f16ab9fe4f119a47c80c59d99a90a590f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Feb 2019 08:39:51 -0500 Subject: [PATCH 131/134] stage1: fix unused function error on freebsd and netbsd closes #2012 closes #2013 --- src/libc_installation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 2a8aade6a5..8be704887a 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -325,7 +325,7 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b } return ErrorFileNotFound; } -#if !defined(ZIG_OS_DARWIN) +#if !defined(ZIG_OS_DARWIN) && !defined(ZIG_OS_FREEBSD) && !defined(ZIG_OS_NETBSD) static Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose) { const char *cc_exe = getenv("CC"); cc_exe = (cc_exe == nullptr) ? "cc" : cc_exe; From 14ca0fd4937b7a4ab2a7c4c704066179d443bfdf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Feb 2019 09:30:48 -0500 Subject: [PATCH 132/134] uefi os: better auto detection of subsystem --- src/link.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/link.cpp b/src/link.cpp index df2eb37f0a..7eb8cb111f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -559,7 +559,11 @@ static void construct_linker_job_coff(LinkJob *lj) { bool is_library = g->out_type == OutTypeLib; switch (g->subsystem) { case TargetSubsystemAuto: - add_nt_link_args(lj, is_library); + if (g->zig_target->os == OsUefi) { + add_uefi_link_args(lj); + } else { + add_nt_link_args(lj, is_library); + } break; case TargetSubsystemConsole: lj->args.append("/SUBSYSTEM:console"); From 3bfacf071e0d208d445292c62f6e19b3f5c607d7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Feb 2019 16:02:07 -0500 Subject: [PATCH 133/134] print the command that failed when C source code fails to build also respect the --color arg when building C code --- src/codegen.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 520c1eed00..d7dbab4556 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8164,6 +8164,14 @@ static void gen_global_asm(CodeGen *g) { } } +static void print_zig_cc_cmd(const char *zig_exe, ZigList *args) { + fprintf(stderr, zig_exe); + for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) { + fprintf(stderr, " %s", args->at(arg_i)); + } + fprintf(stderr, "\n"); +} + static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { Error err; @@ -8190,6 +8198,19 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { args.append("-nostdinc"); args.append("-fno-spell-checking"); + switch (g->err_color) { + case ErrColorAuto: + break; + case ErrColorOff: + args.append("-fno-color-diagnostics"); + args.append("-fno-caret-diagnostics"); + break; + case ErrColorOn: + args.append("-fcolor-diagnostics"); + args.append("-fcaret-diagnostics"); + break; + } + args.append("-isystem"); args.append(buf_ptr(g->zig_c_headers_dir)); @@ -8263,16 +8284,12 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { } if (g->verbose_cc) { - fprintf(stderr, "zig"); - for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { - fprintf(stderr, " %s", args.at(arg_i)); - } - fprintf(stderr, "\n"); + print_zig_cc_cmd("zig", &args); } - os_spawn_process(buf_ptr(self_exe_path), args, &term); if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "`zig cc` failed\n"); + fprintf(stderr, "\nThe following command failed:\n"); + print_zig_cc_cmd(buf_ptr(self_exe_path), &args); exit(1); } From 9753e875453ceeb6f3683b434a7492e20b639d36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Feb 2019 20:09:07 -0500 Subject: [PATCH 134/134] fix incorrect use of printf in previous commit --- src/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index d7dbab4556..0d6a8a7f50 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8165,7 +8165,7 @@ static void gen_global_asm(CodeGen *g) { } static void print_zig_cc_cmd(const char *zig_exe, ZigList *args) { - fprintf(stderr, zig_exe); + fprintf(stderr, "%s", zig_exe); for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) { fprintf(stderr, " %s", args->at(arg_i)); }