| author | |
| committer | |
| log | 1f7ec741fa5b1d9ba3826e06a8a8a0feec58876f |
| tree | b74ec329f2e7e128087282adedcd37752b7cceaf |
| parent | 6149f7318986214a34b09347f28ba48b2a614fef |
5 files changed, 102 insertions(+), 3 deletions(-)
doc/codegen.md created+33| ... | ... | @@ -0,0 +1,33 @@ |
| 1 | # Code Generation | |
| 2 | ||
| 3 | ## Data Representation | |
| 4 | ||
| 5 | Every type has a "handle". If a type is a simple primitive type such as i32 or | |
| 6 | f64, the handle is "by value", meaning that we pass around the value itself when | |
| 7 | we refer to a value of that type. | |
| 8 | ||
| 9 | If a type is a container, error union, maybe type, slice, or array, then its | |
| 10 | handle is a pointer, and everywhere we refer to a value of this type we refer to | |
| 11 | a pointer. | |
| 12 | ||
| 13 | Parameters and return values are always passed as handles. | |
| 14 | ||
| 15 | Error union types are represented as: | |
| 16 | ||
| 17 | struct { | |
| 18 | error: u32, | |
| 19 | payload: T, | |
| 20 | } | |
| 21 | ||
| 22 | Maybe types are represented as: | |
| 23 | ||
| 24 | struct { | |
| 25 | payload: T, | |
| 26 | is_non_null: u1, | |
| 27 | } | |
| 28 | ||
| 29 | ## Data Optimizations | |
| 30 | ||
| 31 | Maybe pointer types are special: the 0x0 pointer value is used to represent a | |
| 32 | null pointer. Thus, instead of the struct above, maybe pointer types are | |
| 33 | represented as a `usize` in codegen and the handle is by value. |
src/analyze.cpp+28-1| ... | ... | @@ -6271,7 +6271,34 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, |
| 6271 | 6271 | } |
| 6272 | 6272 | } |
| 6273 | 6273 | case ReturnKindMaybe: |
| 6274 | zig_panic("TODO"); | |
| 6274 | { | |
| 6275 | TypeTableEntry *expected_maybe_type; | |
| 6276 | if (expected_type) { | |
| 6277 | expected_maybe_type = get_maybe_type(g, expected_type); | |
| 6278 | } else { | |
| 6279 | expected_maybe_type = nullptr; | |
| 6280 | } | |
| 6281 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, expected_maybe_type, | |
| 6282 | node->data.return_expr.expr); | |
| 6283 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 6284 | return resolved_type; | |
| 6285 | } else if (resolved_type->id == TypeTableEntryIdMaybe) { | |
| 6286 | TypeTableEntry *return_type = context->fn_entry->type_entry->data.fn.fn_type_id.return_type; | |
| 6287 | if (return_type->id != TypeTableEntryIdMaybe) { | |
| 6288 | ErrorMsg *msg = add_node_error(g, node, | |
| 6289 | buf_sprintf("?return statement in function with return type '%s'", | |
| 6290 | buf_ptr(&return_type->name))); | |
| 6291 | AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type; | |
| 6292 | add_error_note(g, msg, return_type_node, buf_sprintf("function return type here")); | |
| 6293 | } | |
| 6294 | ||
| 6295 | return resolved_type->data.maybe.child_type; | |
| 6296 | } else { | |
| 6297 | add_node_error(g, node->data.return_expr.expr, | |
| 6298 | buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); | |
| 6299 | return g->builtin_types.entry_invalid; | |
| 6300 | } | |
| 6301 | } | |
| 6275 | 6302 | } |
| 6276 | 6303 | zig_unreachable(); |
| 6277 | 6304 | } |
src/codegen.cpp+39-1| ... | ... | @@ -2497,7 +2497,45 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { |
| 2497 | 2497 | } |
| 2498 | 2498 | } |
| 2499 | 2499 | case ReturnKindMaybe: |
| 2500 | zig_panic("TODO"); | |
| 2500 | { | |
| 2501 | assert(value_type->id == TypeTableEntryIdMaybe); | |
| 2502 | TypeTableEntry *child_type = value_type->data.maybe.child_type; | |
| 2503 | ||
| 2504 | LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetReturn"); | |
| 2505 | LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeRetContinue"); | |
| 2506 | ||
| 2507 | set_debug_source_node(g, node); | |
| 2508 | LLVMValueRef maybe_val_ptr = LLVMBuildStructGEP(g->builder, value, 1, ""); | |
| 2509 | LLVMValueRef is_non_null = LLVMBuildLoad(g->builder, maybe_val_ptr, ""); | |
| 2510 | ||
| 2511 | LLVMValueRef zero = LLVMConstNull(LLVMInt1Type()); | |
| 2512 | LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntNE, is_non_null, zero, ""); | |
| 2513 | LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block); | |
| 2514 | ||
| 2515 | LLVMPositionBuilderAtEnd(g->builder, return_block); | |
| 2516 | TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; | |
| 2517 | assert(return_type->id == TypeTableEntryIdMaybe); | |
| 2518 | if (handle_is_ptr(return_type)) { | |
| 2519 | assert(g->cur_ret_ptr); | |
| 2520 | ||
| 2521 | set_debug_source_node(g, node); | |
| 2522 | LLVMValueRef maybe_bit_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 1, ""); | |
| 2523 | LLVMBuildStore(g->builder, zero, maybe_bit_ptr); | |
| 2524 | LLVMBuildRetVoid(g->builder); | |
| 2525 | } else { | |
| 2526 | LLVMValueRef ret_zero_value = LLVMConstNull(return_type->type_ref); | |
| 2527 | gen_return(g, node, ret_zero_value, ReturnKnowledgeKnownNull); | |
| 2528 | } | |
| 2529 | ||
| 2530 | LLVMPositionBuilderAtEnd(g->builder, continue_block); | |
| 2531 | if (type_has_bits(child_type)) { | |
| 2532 | set_debug_source_node(g, node); | |
| 2533 | LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 0, ""); | |
| 2534 | return get_handle_value(g, node, val_ptr, child_type); | |
| 2535 | } else { | |
| 2536 | return nullptr; | |
| 2537 | } | |
| 2538 | } | |
| 2501 | 2539 | } |
| 2502 | 2540 | zig_unreachable(); |
| 2503 | 2541 | } |
test/cases/return_type_type.zig+1-1| ... | ... | @@ -11,7 +11,7 @@ pub struct SmallList(inline T: type, inline STATIC_SIZE: usize) { |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | #attribute("test") |
| 14 | fn function_with_return_type_type() { | |
| 14 | fn functionWithReturnTypeType() { | |
| 15 | 15 | var list: List(i32) = undefined; |
| 16 | 16 | var list2: List(i32) = undefined; |
| 17 | 17 | list.length = 10; |
test/self_hosted.zig+1| ... | ... | @@ -6,6 +6,7 @@ const other = @import("other.zig"); |
| 6 | 6 | const test_return_type_type = @import("cases/return_type_type.zig"); |
| 7 | 7 | const test_zeroes = @import("cases/zeroes.zig"); |
| 8 | 8 | const test_sizeof_and_typeof = @import("cases/sizeof_and_typeof.zig"); |
| 9 | const test_maybe_return = @import("cases/maybe_return.zig"); | |
| 9 | 10 | |
| 10 | 11 | // normal comment |
| 11 | 12 | /// this is a documentation comment |