From 707154da36ee10c7893cacfafdd3034385d69103 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 27 Jan 2016 14:18:20 -0700 Subject: [PATCH] fix codegen for pointers to void --- src/all_types.hpp | 4 + src/analyze.cpp | 48 ++++++-- src/codegen.cpp | 272 +++++++++++++++++++++++++-------------------- test/run_tests.cpp | 13 +++ 4 files changed, 208 insertions(+), 129 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 25fb6ecf2..70ba9ff40 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -788,6 +788,8 @@ struct TypeTableEntryStruct { // set this flag temporarily to detect infinite loops bool embedded_in_current; bool reported_infinite_err; + // whether we've finished resolving it + bool complete; }; struct TypeTableEntryMaybe { @@ -812,6 +814,8 @@ struct TypeTableEntryEnum { // set this flag temporarily to detect infinite loops bool embedded_in_current; bool reported_infinite_err; + // whether we've finished resolving it + bool complete; }; struct TypeTableEntryFn { diff --git a/src/analyze.cpp b/src/analyze.cpp index 281dfc7ef..992e0e7fb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -25,6 +25,7 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry * BlockContext *context, AstNode *node, Buf *err_name); static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); +static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); static AstNode *first_executing_node(AstNode *node) { switch (node->type) { @@ -152,17 +153,34 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool return *parent_pointer; } else { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); - entry->type_ref = LLVMPointerType(child_type->type_ref, 0); const char *const_str = is_const ? "const " : ""; buf_resize(&entry->name, 0); buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name)); - entry->size_in_bits = g->pointer_size_bytes * 8; - entry->align_in_bits = g->pointer_size_bytes * 8; - assert(child_type->di_type); - entry->di_type = LLVMZigCreateDebugPointerType(g->dbuilder, child_type->di_type, - entry->size_in_bits, entry->align_in_bits, buf_ptr(&entry->name)); + bool zero_bits; + if (child_type->size_in_bits == 0) { + if (child_type->id == TypeTableEntryIdStruct) { + zero_bits = child_type->data.structure.complete; + } else if (child_type->id == TypeTableEntryIdEnum) { + zero_bits = child_type->data.enumeration.complete; + } else { + zero_bits = true; + } + } else { + zero_bits = false; + } + + if (!zero_bits) { + entry->type_ref = LLVMPointerType(child_type->type_ref, 0); + + entry->size_in_bits = g->pointer_size_bytes * 8; + entry->align_in_bits = g->pointer_size_bytes * 8; + assert(child_type->di_type); + entry->di_type = LLVMZigCreateDebugPointerType(g->dbuilder, child_type->di_type, + entry->size_in_bits, entry->align_in_bits, buf_ptr(&entry->name)); + } + entry->data.pointer.child_type = child_type; entry->data.pointer.is_const = is_const; @@ -511,6 +529,8 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t // after the gen_param_index += 1 because 0 is the return type param_di_types[gen_param_index] = gen_type->di_type; gen_return_type = g->builtin_types.entry_void; + } else if (return_type->size_in_bits == 0) { + gen_return_type = g->builtin_types.entry_void; } else { gen_return_type = return_type; } @@ -584,7 +604,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage); - if (gen_return_type->id == TypeTableEntryIdUnreachable) { + if (return_type->id == TypeTableEntryIdUnreachable) { LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute); } LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention); @@ -707,6 +727,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt // unset temporary flag enum_type->data.enumeration.embedded_in_current = false; + enum_type->data.enumeration.complete = true; if (!enum_type->data.enumeration.is_invalid) { enum_type->data.enumeration.gen_field_count = gen_field_index; @@ -872,6 +893,7 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE struct_type->data.structure.embedded_in_current = false; struct_type->data.structure.gen_field_count = gen_field_index; + struct_type->data.structure.complete = true; if (!struct_type->data.structure.is_invalid) { @@ -1186,7 +1208,9 @@ static bool type_has_codegen_value(TypeTableEntryId id) { static void add_global_const_expr(CodeGen *g, Expr *expr) { if (expr->const_val.ok && - type_has_codegen_value(expr->type_entry->id) && !expr->has_global_const) + type_has_codegen_value(expr->type_entry->id) && + !expr->has_global_const && + expr->type_entry->size_in_bits > 0) { g->global_const_list.append(expr); expr->has_global_const = true; @@ -1776,7 +1800,7 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry add_node_error(g, node, buf_sprintf("void expression expects no arguments")); return g->builtin_types.entry_invalid; } else { - return container_type; + return resolve_expr_const_val_as_void(g, node); } } else if (container_type->id == TypeTableEntryIdUnreachable) { if (container_init_expr->entries.length != 0) { @@ -1925,6 +1949,12 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i return return_type; } +static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node) { + Expr *expr = get_resolved_expr(node); + expr->const_val.ok = true; + return g->builtin_types.entry_void; +} + static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; diff --git a/src/codegen.cpp b/src/codegen.cpp index 89abae8ce..008ec9b39 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -510,7 +510,6 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { } TypeTableEntry *src_return_type = fn_type->data.fn.src_return_type; - TypeTableEntry *gen_return_type = fn_type->data.fn.gen_return_type; int fn_call_param_count = node->data.fn_call_expr.params.length; bool first_arg_ret = handle_is_ptr(src_return_type); @@ -544,7 +543,7 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { LLVMValueRef result = LLVMZigBuildCall(g->builder, fn_val, gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, ""); - if (gen_return_type->id == TypeTableEntryIdUnreachable) { + if (src_return_type->id == TypeTableEntryIdUnreachable) { return LLVMBuildUnreachable(g->builder); } else if (first_arg_ret) { return node->data.fn_call_expr.tmp_ptr; @@ -821,8 +820,6 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, VariableTableEntry *var = find_variable(expr_node->block_context, &node->data.symbol_expr.symbol); assert(var); - // semantic checking ensures no variables are constant - assert(!var->is_const); *out_type_entry = var->type; target_ref = var->value_ref; @@ -895,8 +892,13 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { case PrefixOpDereference: { LLVMValueRef expr = gen_expr(g, expr_node); - add_debug_source_node(g, node); - return LLVMBuildLoad(g->builder, expr, ""); + TypeTableEntry *type_entry = get_expr_type(expr_node); + if (type_entry->size_in_bits == 0) { + return nullptr; + } else { + add_debug_source_node(g, node); + return LLVMBuildLoad(g->builder, expr, ""); + } } case PrefixOpMaybe: { @@ -2182,8 +2184,12 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { Expr *expr = get_resolved_expr(node); if (expr->const_val.ok) { - assert(expr->const_llvm_val); - return expr->const_llvm_val; + if (expr->type_entry->size_in_bits == 0) { + return nullptr; + } else { + assert(expr->const_llvm_val); + return expr->const_llvm_val; + } } switch (node->type) { case NodeTypeBinOpExpr: @@ -2291,119 +2297,144 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE return LLVMGetUndef(type_entry->type_ref); } - if (type_entry->id == TypeTableEntryIdInt) { - return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); - } else if (type_entry->id == TypeTableEntryIdPureError) { - assert(const_val->data.x_err.err); - return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref, const_val->data.x_err.err->value, false); - } else if (type_entry->id == TypeTableEntryIdFloat) { - if (const_val->data.x_bignum.kind == BigNumKindFloat) { - return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float); - } else { - int64_t x = const_val->data.x_bignum.data.x_uint; - if (const_val->data.x_bignum.is_negative) { - x = -x; - } - return LLVMConstReal(type_entry->type_ref, x); - } - } else if (type_entry->id == TypeTableEntryIdBool) { - if (const_val->data.x_bool) { - return LLVMConstAllOnes(LLVMInt1Type()); - } else { - return LLVMConstNull(LLVMInt1Type()); - } - } else if (type_entry->id == 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()); - } else { - child_val = LLVMConstNull(child_type->type_ref); - maybe_val = LLVMConstNull(LLVMInt1Type()); - } - LLVMValueRef fields[] = { - child_val, - maybe_val, - }; - return LLVMConstStruct(fields, 2, false); - } else if (type_entry->id == TypeTableEntryIdStruct) { - LLVMValueRef *fields = allocate(type_entry->data.structure.gen_field_count); - for (int i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - TypeStructField *type_struct_field = &type_entry->data.structure.fields[i]; - fields[type_struct_field->gen_index] = gen_const_val(g, type_struct_field->type_entry, - const_val->data.x_struct.fields[i]); - } - return LLVMConstNamedStruct(type_entry->type_ref, fields, type_entry->data.structure.gen_field_count); - } else if (type_entry->id == TypeTableEntryIdArray) { - TypeTableEntry *child_type = type_entry->data.array.child_type; - uint64_t len = type_entry->data.array.len; - LLVMValueRef *values = allocate(len); - for (int i = 0; i < len; i += 1) { - ConstExprValue *field_value = const_val->data.x_array.fields[i]; - values[i] = gen_const_val(g, child_type, field_value); - } - return LLVMConstArray(child_type->type_ref, values, len); - } else if (type_entry->id == TypeTableEntryIdEnum) { - LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; - LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false); - if (type_entry->data.enumeration.gen_field_count == 0) { - return tag_value; - } else { - zig_panic("TODO"); - } - } else if (type_entry->id == TypeTableEntryIdFn) { - return const_val->data.x_fn->fn_value; - } else if (type_entry->id == TypeTableEntryIdPointer) { - TypeTableEntry *child_type = type_entry->data.pointer.child_type; - int len = const_val->data.x_ptr.len; - LLVMValueRef target_val; - if (len == 1) { - target_val = gen_const_val(g, child_type, const_val->data.x_ptr.ptr[0]); - } else if (len > 1) { - LLVMValueRef *values = allocate(len); - for (int i = 0; i < len; i += 1) { - values[i] = gen_const_val(g, child_type, const_val->data.x_ptr.ptr[i]); - } - target_val = LLVMConstArray(child_type->type_ref, values, len); - } else { - zig_unreachable(); - } - LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(target_val), ""); - LLVMSetInitializer(global_value, target_val); - LLVMSetLinkage(global_value, LLVMPrivateLinkage); - LLVMSetGlobalConstant(global_value, type_entry->data.pointer.is_const); - LLVMSetUnnamedAddr(global_value, true); - - if (len > 1) { - return LLVMConstBitCast(global_value, type_entry->type_ref); - } else { - return global_value; - } - } else if (type_entry->id == TypeTableEntryIdErrorUnion) { - TypeTableEntry *child_type = type_entry->data.error.child_type; - if (child_type->size_in_bits == 0) { - uint64_t value = const_val->data.x_err.err ? const_val->data.x_err.err->value : 0; - return LLVMConstInt(g->err_tag_type->type_ref, value, false); - } else { - LLVMValueRef err_tag_value; - LLVMValueRef err_payload_value; - if (const_val->data.x_err.err) { - err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err.err->value, false); - err_payload_value = LLVMConstNull(child_type->type_ref); + switch (type_entry->id) { + case TypeTableEntryIdInt: + return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); + case TypeTableEntryIdPureError: + assert(const_val->data.x_err.err); + return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref, + const_val->data.x_err.err->value, false); + case TypeTableEntryIdFloat: + if (const_val->data.x_bignum.kind == BigNumKindFloat) { + return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float); } else { - err_tag_value = LLVMConstNull(g->err_tag_type->type_ref); - err_payload_value = gen_const_val(g, child_type, const_val->data.x_err.payload); + int64_t x = const_val->data.x_bignum.data.x_uint; + if (const_val->data.x_bignum.is_negative) { + x = -x; + } + return LLVMConstReal(type_entry->type_ref, x); } - LLVMValueRef fields[] = { - err_tag_value, - err_payload_value, - }; - return LLVMConstStruct(fields, 2, false); - } - } else { - zig_unreachable(); + case TypeTableEntryIdBool: + if (const_val->data.x_bool) { + return LLVMConstAllOnes(LLVMInt1Type()); + } else { + return LLVMConstNull(LLVMInt1Type()); + } + 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()); + } else { + child_val = LLVMConstNull(child_type->type_ref); + maybe_val = LLVMConstNull(LLVMInt1Type()); + } + LLVMValueRef fields[] = { + child_val, + maybe_val, + }; + return LLVMConstStruct(fields, 2, false); + } + case TypeTableEntryIdStruct: + { + LLVMValueRef *fields = allocate(type_entry->data.structure.gen_field_count); + for (int i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + TypeStructField *type_struct_field = &type_entry->data.structure.fields[i]; + if (type_struct_field->gen_index == -1) { + continue; + } + fields[type_struct_field->gen_index] = gen_const_val(g, type_struct_field->type_entry, + const_val->data.x_struct.fields[i]); + } + return LLVMConstNamedStruct(type_entry->type_ref, fields, + type_entry->data.structure.gen_field_count); + } + case TypeTableEntryIdArray: + { + TypeTableEntry *child_type = type_entry->data.array.child_type; + uint64_t len = type_entry->data.array.len; + LLVMValueRef *values = allocate(len); + for (int i = 0; i < len; i += 1) { + ConstExprValue *field_value = const_val->data.x_array.fields[i]; + values[i] = gen_const_val(g, child_type, field_value); + } + return LLVMConstArray(child_type->type_ref, values, len); + } + case TypeTableEntryIdEnum: + { + LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; + LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false); + if (type_entry->data.enumeration.gen_field_count == 0) { + return tag_value; + } else { + zig_panic("TODO"); + } + } + case TypeTableEntryIdFn: + return const_val->data.x_fn->fn_value; + case TypeTableEntryIdPointer: + { + TypeTableEntry *child_type = type_entry->data.pointer.child_type; + int len = const_val->data.x_ptr.len; + LLVMValueRef target_val; + if (len == 1) { + target_val = gen_const_val(g, child_type, const_val->data.x_ptr.ptr[0]); + } else if (len > 1) { + LLVMValueRef *values = allocate(len); + for (int i = 0; i < len; i += 1) { + values[i] = gen_const_val(g, child_type, const_val->data.x_ptr.ptr[i]); + } + target_val = LLVMConstArray(child_type->type_ref, values, len); + } else { + zig_unreachable(); + } + LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(target_val), ""); + LLVMSetInitializer(global_value, target_val); + LLVMSetLinkage(global_value, LLVMPrivateLinkage); + LLVMSetGlobalConstant(global_value, type_entry->data.pointer.is_const); + LLVMSetUnnamedAddr(global_value, true); + + if (len > 1) { + return LLVMConstBitCast(global_value, type_entry->type_ref); + } else { + return global_value; + } + } + case TypeTableEntryIdErrorUnion: + { + TypeTableEntry *child_type = type_entry->data.error.child_type; + if (child_type->size_in_bits == 0) { + uint64_t value = const_val->data.x_err.err ? const_val->data.x_err.err->value : 0; + return LLVMConstInt(g->err_tag_type->type_ref, value, false); + } else { + LLVMValueRef err_tag_value; + LLVMValueRef err_payload_value; + if (const_val->data.x_err.err) { + err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err.err->value, false); + err_payload_value = LLVMConstNull(child_type->type_ref); + } else { + err_tag_value = LLVMConstNull(g->err_tag_type->type_ref); + err_payload_value = gen_const_val(g, child_type, const_val->data.x_err.payload); + } + LLVMValueRef fields[] = { + err_tag_value, + err_payload_value, + }; + return LLVMConstStruct(fields, 2, false); + } + } + case TypeTableEntryIdInvalid: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdVoid: + zig_unreachable(); + } } @@ -2438,7 +2469,8 @@ static void do_code_gen(CodeGen *g) { VariableTableEntry *var = g->global_vars.at(i); if (var->type->id == TypeTableEntryIdNumLitFloat || - var->type->id == TypeTableEntryIdNumLitInt) + var->type->id == TypeTableEntryIdNumLitInt || + var->type->size_in_bits == 0) { continue; } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 4e9da801b..06419320e 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1316,6 +1316,19 @@ pub fn main(args: [][]u8) -> %void { %%stdout.printf("BAD\n"); } %%stdout.printf("OK\n"); +} + )SOURCE", "OK\n"); + + add_simple_case("pointer to void return type", R"SOURCE( +import "std.zig"; +const x = void{}; +fn f() -> &void { + %%stdout.printf("OK\n"); + return &x; +} +pub fn main(args: [][]u8) -> %void { + const a = f(); + return *a; } )SOURCE", "OK\n"); }