From 1f7ec741fa5b1d9ba3826e06a8a8a0feec58876f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 23 Aug 2016 15:05:26 -0700 Subject: [PATCH] implement `?return` expression --- doc/codegen.md | 33 +++++++++++++++++++++++++++ src/analyze.cpp | 29 +++++++++++++++++++++++- src/codegen.cpp | 40 ++++++++++++++++++++++++++++++++- test/cases/return_type_type.zig | 2 +- test/self_hosted.zig | 1 + 5 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 doc/codegen.md diff --git a/doc/codegen.md b/doc/codegen.md new file mode 100644 index 000000000..02406fae8 --- /dev/null +++ b/doc/codegen.md @@ -0,0 +1,33 @@ +# Code Generation + +## Data Representation + +Every type has a "handle". If a type is a simple primitive type such as i32 or +f64, the handle is "by value", meaning that we pass around the value itself when +we refer to a value of that type. + +If a type is a container, error union, maybe type, slice, or array, then its +handle is a pointer, and everywhere we refer to a value of this type we refer to +a pointer. + +Parameters and return values are always passed as handles. + +Error union types are represented as: + + struct { + error: u32, + payload: T, + } + +Maybe types are represented as: + + struct { + payload: T, + is_non_null: u1, + } + +## Data Optimizations + +Maybe pointer types are special: the 0x0 pointer value is used to represent a +null pointer. Thus, instead of the struct above, maybe pointer types are +represented as a `usize` in codegen and the handle is by value. diff --git a/src/analyze.cpp b/src/analyze.cpp index 4899ec533..639444c6c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -6271,7 +6271,34 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, } } case ReturnKindMaybe: - zig_panic("TODO"); + { + TypeTableEntry *expected_maybe_type; + if (expected_type) { + expected_maybe_type = get_maybe_type(g, expected_type); + } else { + expected_maybe_type = nullptr; + } + TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type, + node->data.return_expr.expr); + if (resolved_type->id == TypeTableEntryIdInvalid) { + return resolved_type; + } else if (resolved_type->id == TypeTableEntryIdMaybe) { + TypeTableEntry *return_type = context->fn_entry->type_entry->data.fn.fn_type_id.return_type; + if (return_type->id != TypeTableEntryIdMaybe) { + ErrorMsg *msg = add_node_error(g, node, + buf_sprintf("?return statement in function with return type '%s'", + buf_ptr(&return_type->name))); + AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type; + add_error_note(g, msg, return_type_node, buf_sprintf("function return type here")); + } + + return resolved_type->data.maybe.child_type; + } else { + add_node_error(g, node->data.return_expr.expr, + buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); + return g->builtin_types.entry_invalid; + } + } } zig_unreachable(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 3b954da4f..12ba5378a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2497,7 +2497,45 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { } } case ReturnKindMaybe: - zig_panic("TODO"); + { + assert(value_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *child_type = value_type->data.maybe.child_type; + + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn"); + LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue"); + + set_debug_source_node(g, node); + LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, ""); + LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, ""); + + LLVMValueRef zero = LLVMConstNull(LLVMInt1Type()); + LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, ""); + LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block); + + LLVMPositionBuilderAtEnd(g->builder, return_block); + TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; + assert(return_type->id == TypeTableEntryIdMaybe); + if (handle_is_ptr(return_type)) { + assert(g->cur_ret_ptr); + + set_debug_source_node(g, node); + LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, ""); + LLVMBuildStore(g->builder, zero, maybe_bit_ptr); + LLVMBuildRetVoid(g->builder); + } else { + LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref); + gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull); + } + + LLVMPositionBuilderAtEnd(g->builder, continue_block); + if (type_has_bits(child_type)) { + set_debug_source_node(g, node); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, ""); + return get_handle_value(g, node, val_ptr, child_type); + } else { + return nullptr; + } + } } zig_unreachable(); } diff --git a/test/cases/return_type_type.zig b/test/cases/return_type_type.zig index 4f1ac04e4..4337c072b 100644 --- a/test/cases/return_type_type.zig +++ b/test/cases/return_type_type.zig @@ -11,7 +11,7 @@ pub struct SmallList(inline T: type, inline STATIC_SIZE: usize) { } #attribute("test") -fn function_with_return_type_type() { +fn functionWithReturnTypeType() { var list: List(i32) = undefined; var list2: List(i32) = undefined; list.length = 10; diff --git a/test/self_hosted.zig b/test/self_hosted.zig index beffaf923..a107715af 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -6,6 +6,7 @@ const other = @import("other.zig"); const test_return_type_type = @import("cases/return_type_type.zig"); const test_zeroes = @import("cases/zeroes.zig"); const test_sizeof_and_typeof = @import("cases/sizeof_and_typeof.zig"); +const test_maybe_return = @import("cases/maybe_return.zig"); // normal comment /// this is a documentation comment