diff --git a/README.md b/README.md index 425d309a1f..38d25cb868 100644 --- a/README.md +++ b/README.md @@ -112,3 +112,8 @@ To fix this, you have 2 options: * Compile Zig with the same compiler that LLVM was compiled with. * Add `-DZIG_LLVM_OLD_CXX_ABI=yes` to the cmake configure line. + +## Community + +Zig is in its infancy. However one place you can gather to chat is the `#zig` +IRC channel on Freenode. diff --git a/src/analyze.cpp b/src/analyze.cpp index e0519cdc10..abc161ce72 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -199,43 +199,54 @@ static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { return entry; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe); - // create a struct with a boolean whether this is the null value assert(child_type->type_ref); - LLVMTypeRef elem_types[] = { - child_type->type_ref, - LLVMInt1Type(), - }; - entry->type_ref = LLVMStructType(elem_types, 2, false); - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); - entry->size_in_bits = child_type->size_in_bits + 8; - entry->align_in_bits = child_type->align_in_bits; assert(child_type->di_type); + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); - LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); - LLVMZigDIFile *di_file = nullptr; - unsigned line = 0; - entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, - LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), - compile_unit_scope, di_file, line); + if (child_type->id == TypeTableEntryIdPointer) { + // this is an optimization but also is necessary for calling C + // functions where all pointers are maybe pointers + entry->size_in_bits = child_type->size_in_bits; + entry->align_in_bits = child_type->align_in_bits; + entry->type_ref = child_type->type_ref; + entry->di_type = child_type->di_type; + } else { + // create a struct with a boolean whether this is the null value + LLVMTypeRef elem_types[] = { + child_type->type_ref, + LLVMInt1Type(), + }; + entry->type_ref = LLVMStructType(elem_types, 2, false); + entry->size_in_bits = child_type->size_in_bits + 8; + entry->align_in_bits = child_type->align_in_bits; - LLVMZigDIType *di_element_types[] = { - LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), - "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0, - child_type->di_type), - LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), - "maybe", di_file, line, 8, 8, child_type->size_in_bits, 0, - child_type->di_type), - }; - LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, - compile_unit_scope, - buf_ptr(&entry->name), - di_file, line, entry->size_in_bits, entry->align_in_bits, 0, - nullptr, di_element_types, 2, 0, nullptr, ""); - LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); - entry->di_type = replacement_di_type; + LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); + LLVMZigDIFile *di_file = nullptr; + unsigned line = 0; + entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, + LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), + compile_unit_scope, di_file, line); + + LLVMZigDIType *di_element_types[] = { + LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), + "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0, + child_type->di_type), + LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), + "maybe", di_file, line, 8, 8, child_type->size_in_bits, 0, + child_type->di_type), + }; + LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&entry->name), + di_file, line, entry->size_in_bits, entry->align_in_bits, 0, + nullptr, di_element_types, 2, 0, nullptr, ""); + + LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); + entry->di_type = replacement_di_type; + } entry->data.maybe.child_type = child_type; @@ -5078,12 +5089,13 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { return false; case TypeTableEntryIdArray: case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: return true; case TypeTableEntryIdErrorUnion: return type_entry->data.error.child_type->size_in_bits > 0; case TypeTableEntryIdEnum: return type_entry->data.enumeration.gen_field_count != 0; + case TypeTableEntryIdMaybe: + return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer; } zig_unreachable(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index edbaf0b871..2d55000cc4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -74,7 +74,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node); static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, TypeTableEntry **out_type_entry); static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue); static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl, - bool unwrap_maybe, LLVMValueRef *init_val); + bool unwrap_maybe, LLVMValueRef *init_val, TypeTableEntry **init_val_type); static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op, LLVMValueRef target_ref, LLVMValueRef value, TypeTableEntry *op1_type, TypeTableEntry *op2_type); @@ -389,14 +389,20 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { assert(wanted_type->id == TypeTableEntryIdMaybe); assert(actual_type); - add_debug_source_node(g, node); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, ""); - gen_assign_raw(g, node, BinOpTypeAssign, - val_ptr, expr_val, wanted_type->data.maybe.child_type, actual_type); + TypeTableEntry *child_type = wanted_type->data.maybe.child_type; - add_debug_source_node(g, node); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, ""); - LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); + if (child_type->id == TypeTableEntryIdPointer) { + return expr_val; + } else { + add_debug_source_node(g, node); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, ""); + gen_assign_raw(g, node, BinOpTypeAssign, + val_ptr, expr_val, child_type, actual_type); + + add_debug_source_node(g, node); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, ""); + LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); + } return cast_expr->tmp_ptr; } @@ -1245,10 +1251,20 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { } static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) { - add_debug_source_node(g, node); - LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, ""); - // TODO if it's a struct we might not want to load the pointer - return LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + TypeTableEntry *type_entry = get_expr_type(node); + assert(type_entry->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = type_entry->data.maybe.child_type; + if (child_type->id == TypeTableEntryIdPointer) { + return maybe_struct_ref; + } else { + add_debug_source_node(g, node); + LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, ""); + if (handle_is_ptr(child_type)) { + return maybe_field_ptr; + } else { + return LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + } + } } static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { @@ -1260,29 +1276,32 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node); - add_debug_source_node(g, node); - LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, ""); - LLVMValueRef cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + TypeTableEntry *maybe_type = get_expr_type(op1_node); + assert(maybe_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = maybe_type->data.maybe.child_type; + + LLVMValueRef cond_value; + if (child_type->id == TypeTableEntryIdPointer) { + cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref, + LLVMConstNull(child_type->type_ref), ""); + } else { + add_debug_source_node(g, node); + LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, ""); + cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + } LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull"); LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull"); - LLVMBasicBlockRef end_block; + LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd"); - bool non_null_reachable = get_expr_type(op1_node)->id != TypeTableEntryIdUnreachable; bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable; - bool end_reachable = non_null_reachable || null_reachable; - if (end_reachable) { - end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd"); - } LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block); LLVMPositionBuilderAtEnd(g->builder, non_null_block); LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref); - if (non_null_reachable) { - add_debug_source_node(g, node); - LLVMBuildBr(g->builder, end_block); - } + add_debug_source_node(g, node); + LLVMBuildBr(g->builder, end_block); LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder); LLVMPositionBuilderAtEnd(g->builder, null_block); @@ -1293,18 +1312,16 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) { } LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder); - if (end_reachable) { - LLVMPositionBuilderAtEnd(g->builder, end_block); - if (null_reachable) { - add_debug_source_node(g, node); - LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), ""); - LLVMValueRef incoming_values[2] = {non_null_result, null_result}; - LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block}; - LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); - return phi; - } else { - return non_null_result; - } + LLVMPositionBuilderAtEnd(g->builder, end_block); + if (null_reachable) { + add_debug_source_node(g, node); + LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), ""); + LLVMValueRef incoming_values[2] = {non_null_result, null_result}; + LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block}; + LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); + return phi; + } else { + return non_null_result; } return nullptr; @@ -1607,12 +1624,20 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { assert(node->data.if_var_expr.var_decl.expr); LLVMValueRef init_val; - gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, true, &init_val); + TypeTableEntry *expr_type; + gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, true, &init_val, &expr_type); // test if value is the maybe state - add_debug_source_node(g, node); - LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, ""); - LLVMValueRef cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + assert(expr_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = expr_type->data.maybe.child_type; + LLVMValueRef cond_value; + if (child_type->id == TypeTableEntryIdPointer) { + cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), ""); + } else { + add_debug_source_node(g, node); + LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, ""); + cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); + } LLVMValueRef return_value = gen_if_bool_expr_raw(g, node, cond_value, node->data.if_var_expr.then_block, @@ -1978,7 +2003,7 @@ static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) { } static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl, - bool unwrap_maybe, LLVMValueRef *init_value) + bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type) { VariableTableEntry *variable = var_decl->variable; @@ -1987,6 +2012,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa if (var_decl->expr) { *init_value = gen_expr(g, var_decl->expr); + *expr_type = get_expr_type(var_decl->expr); } if (variable->type->size_in_bits == 0) { return nullptr; @@ -2005,7 +2031,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa if (unwrap_maybe) { assert(var_decl->expr); assert(expr_type->id == TypeTableEntryIdMaybe); - value = gen_unwrap_maybe(g, source_node, *init_value); + value = gen_unwrap_maybe(g, var_decl->expr, *init_value); expr_type = expr_type->data.maybe.child_type; } else { value = *init_value; @@ -2089,7 +2115,8 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) { } LLVMValueRef init_val; - return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val); + TypeTableEntry *init_val_type; + return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type); } static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { @@ -2100,11 +2127,7 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { return nullptr; } else if (variable->is_ptr) { assert(variable->value_ref); - if (variable->type->id == TypeTableEntryIdArray) { - return variable->value_ref; - } else if (variable->type->id == TypeTableEntryIdStruct || - variable->type->id == TypeTableEntryIdMaybe) - { + if (handle_is_ptr(variable->type)) { return variable->value_ref; } else { add_debug_source_node(g, node); @@ -2330,20 +2353,28 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE case TypeTableEntryIdMaybe: { TypeTableEntry *child_type = type_entry->data.maybe.child_type; - LLVMValueRef child_val; - LLVMValueRef maybe_val; - if (const_val->data.x_maybe) { - child_val = gen_const_val(g, child_type, const_val->data.x_maybe); - maybe_val = LLVMConstAllOnes(LLVMInt1Type()); + if (child_type->id == TypeTableEntryIdPointer) { + if (const_val->data.x_maybe) { + return gen_const_val(g, child_type, const_val->data.x_maybe); + } else { + return LLVMConstNull(child_type->type_ref); + } } else { - child_val = LLVMConstNull(child_type->type_ref); - maybe_val = LLVMConstNull(LLVMInt1Type()); + LLVMValueRef child_val; + LLVMValueRef maybe_val; + if (const_val->data.x_maybe) { + child_val = gen_const_val(g, child_type, const_val->data.x_maybe); + maybe_val = LLVMConstAllOnes(LLVMInt1Type()); + } else { + child_val = LLVMConstNull(child_type->type_ref); + maybe_val = LLVMConstNull(LLVMInt1Type()); + } + LLVMValueRef fields[] = { + child_val, + maybe_val, + }; + return LLVMConstStruct(fields, 2, false); } - LLVMValueRef fields[] = { - child_val, - maybe_val, - }; - return LLVMConstStruct(fields, 2, false); } case TypeTableEntryIdStruct: { diff --git a/src/parseh.cpp b/src/parseh.cpp index e0b3506e12..20a061d85e 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -97,6 +97,14 @@ static AstNode *create_var_decl_node(Context *c, const char *var_name, AstNode * return node; } +static AstNode *create_prefix_node(Context *c, PrefixOp op, AstNode *child_node) { + AstNode *node = create_node(c, NodeTypePrefixOpExpr); + node->data.prefix_op_expr.prefix_op = op; + node->data.prefix_op_expr.primary_expr = child_node; + normalize_parent_ptrs(node); + return node; +} + static const char *decl_name(const Decl *decl) { const NamedDecl *named_decl = static_cast(decl); return (const char *)named_decl->getName().bytes_begin(); @@ -132,11 +140,9 @@ static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) { if (!type_node) { return nullptr; } - AstNode *node = create_node(c, NodeTypePrefixOpExpr); - node->data.prefix_op_expr.prefix_op = is_const ? PrefixOpConstAddressOf : PrefixOpAddressOf; - node->data.prefix_op_expr.primary_expr = convert_to_c_void(c, type_node); - normalize_parent_ptrs(node); - return node; + PrefixOp op = is_const ? PrefixOpConstAddressOf : PrefixOpAddressOf; + AstNode *child_node = create_prefix_node(c, op, convert_to_c_void(c, type_node)); + return create_prefix_node(c, PrefixOpMaybe, child_node); } static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 96aa7cc2db..f82a6b63bd 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1827,7 +1827,7 @@ pub const Foo = enum_Foo;)OUTPUT"); add_parseh_case("restrict -> noalias", R"SOURCE( void foo(void *restrict bar, void *restrict); )SOURCE", R"OUTPUT(pub const c_void = u8; -pub extern fn foo(noalias bar: &c_void, noalias arg1: &c_void);)OUTPUT"); +pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);)OUTPUT"); } static void print_compiler_invocation(TestCase *test_case) {