From c0ea9290c4576f2111c8fc6b2d448f278effd80e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 23 Jan 2016 02:14:01 -0700 Subject: main returns %void --- src/all_types.hpp | 8 +- src/analyze.cpp | 245 +++++++++++++++++++++++++++++++++--------------------- src/codegen.cpp | 57 ++++++++----- 3 files changed, 195 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/all_types.hpp b/src/all_types.hpp index f358bd8bd6..af80b36f3f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -60,6 +60,11 @@ struct ConstPtrValue { uint64_t len; }; +struct ConstErrValue { + ErrorTableEntry *err; + ConstExprValue *payload; +}; + struct ConstExprValue { bool ok; // true if constant expression evalution worked bool depends_on_compile_var; @@ -70,8 +75,8 @@ struct ConstExprValue { bool x_bool; FnTableEntry *x_fn; TypeTableEntry *x_type; - ErrorTableEntry *x_err; ConstExprValue *x_maybe; + ConstErrValue x_err; ConstEnumValue x_enum; ConstStructValue x_struct; ConstArrayValue x_array; @@ -309,6 +314,7 @@ enum CastOp { CastOpIntWidenOrShorten, CastOpToUnknownSizeArray, CastOpMaybeWrap, + CastOpErrorWrap, CastOpPointerReinterpret, CastOpErrToInt, }; diff --git a/src/analyze.cpp b/src/analyze.cpp index c2fe9ba076..a137fdb3cb 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1130,6 +1130,61 @@ static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTa return false; } +static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { + if (expected_type == actual_type) + return true; + + // pointer const + if (expected_type->id == TypeTableEntryIdPointer && + actual_type->id == TypeTableEntryIdPointer && + (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const)) + { + return types_match_const_cast_only(expected_type->data.pointer.child_type, + actual_type->data.pointer.child_type); + } + + // unknown size array const + if (expected_type->id == TypeTableEntryIdStruct && + actual_type->id == TypeTableEntryIdStruct && + expected_type->data.structure.is_unknown_size_array && + actual_type->data.structure.is_unknown_size_array && + (!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const || + expected_type->data.structure.fields[0].type_entry->data.pointer.is_const)) + { + return types_match_const_cast_only( + expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, + actual_type->data.structure.fields[0].type_entry->data.pointer.child_type); + } + + // maybe + if (expected_type->id == TypeTableEntryIdMaybe && + actual_type->id == TypeTableEntryIdMaybe) + { + return types_match_const_cast_only( + expected_type->data.maybe.child_type, + actual_type->data.maybe.child_type); + } + + // error + if (expected_type->id == TypeTableEntryIdError && + actual_type->id == TypeTableEntryIdError) + { + return types_match_const_cast_only( + expected_type->data.error.child_type, + actual_type->data.error.child_type); + } + + // fn + if (expected_type->id == TypeTableEntryIdFn && + actual_type->id == TypeTableEntryIdFn) + { + zig_panic("TODO types_match_const_cast_only for fns"); + } + + + return false; +} + static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node, AstNode **child_nodes, TypeTableEntry **child_types, int child_count) { @@ -1143,6 +1198,12 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa AstNode *cur_node = child_nodes[i]; if (cur_type->id == TypeTableEntryIdInvalid) { return cur_type; + } else if (types_match_const_cast_only(prev_type, cur_type)) { + continue; + } else if (types_match_const_cast_only(cur_type, prev_type)) { + prev_type = cur_type; + prev_node = cur_node; + continue; } else if (prev_type->id == TypeTableEntryIdUnreachable) { prev_type = cur_type; prev_node = cur_node; @@ -1163,6 +1224,16 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa prev_type = cur_type; prev_node = cur_node; } + } else if (prev_type->id == TypeTableEntryIdError && + types_match_const_cast_only(prev_type->data.error.child_type, cur_type)) + { + continue; + } else if (cur_type->id == TypeTableEntryIdError && + types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) + { + prev_type = cur_type; + prev_node = cur_node; + continue; } else if (prev_type->id == TypeTableEntryIdNumLitFloat && cur_type->id == TypeTableEntryIdNumLitFloat) { @@ -1189,8 +1260,6 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa } else { return g->builtin_types.entry_invalid; } - } else if (prev_type == cur_type) { - continue; } else { add_node_error(g, parent_source_node, buf_sprintf("incompatible types: '%s' and '%s'", @@ -1202,61 +1271,6 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa return prev_type; } -static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { - if (expected_type == actual_type) - return true; - - // pointer const - if (expected_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer && - (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const)) - { - return types_match_const_cast_only(expected_type->data.pointer.child_type, - actual_type->data.pointer.child_type); - } - - // unknown size array const - if (expected_type->id == TypeTableEntryIdStruct && - actual_type->id == TypeTableEntryIdStruct && - expected_type->data.structure.is_unknown_size_array && - actual_type->data.structure.is_unknown_size_array && - (!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const || - expected_type->data.structure.fields[0].type_entry->data.pointer.is_const)) - { - return types_match_const_cast_only( - expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, - actual_type->data.structure.fields[0].type_entry->data.pointer.child_type); - } - - // maybe - if (expected_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdMaybe) - { - return types_match_const_cast_only( - expected_type->data.maybe.child_type, - actual_type->data.maybe.child_type); - } - - // error - if (expected_type->id == TypeTableEntryIdError && - actual_type->id == TypeTableEntryIdError) - { - return types_match_const_cast_only( - expected_type->data.error.child_type, - actual_type->data.error.child_type); - } - - // fn - if (expected_type->id == TypeTableEntryIdFn && - actual_type->id == TypeTableEntryIdFn) - { - zig_panic("TODO types_match_const_cast_only for fns"); - } - - - return false; -} - static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type, AstNode *literal_node, bool *reported_err) { @@ -1272,6 +1286,14 @@ static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_ return true; } + // implicit conversion from error child type to error type + if (expected_type->id == TypeTableEntryIdError && + types_match_with_implicit_cast(g, expected_type->data.error.child_type, actual_type, + literal_node, reported_err)) + { + return true; + } + // implicit widening conversion if (expected_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && @@ -1381,9 +1403,10 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEn if (!child_nodes[i]) { continue; } - Expr *expr = get_resolved_expr(child_nodes[i]); + AstNode **child_node = child_nodes[i]->parent_field; TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, block_context, - child_nodes[i], expected_type, child_types[i]); + *child_node, expected_type, child_types[i]); + Expr *expr = get_resolved_expr(*child_node); expr->type_entry = resolved_type; add_global_const_expr(g, expr); } @@ -1812,7 +1835,7 @@ static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, F static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, ErrorTableEntry *err) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; - expr->const_val.data.x_err = err; + expr->const_val.data.x_err.err = err; return get_error_type(g, g->builtin_types.entry_void); } @@ -2868,10 +2891,18 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex const_val->data.x_maybe = other_val; const_val->ok = true; break; - case CastOpErrToInt: - bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_err->value); + case CastOpErrorWrap: + const_val->data.x_err.err = nullptr; + const_val->data.x_err.payload = other_val; const_val->ok = true; break; + case CastOpErrToInt: + { + uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; + bignum_init_unsigned(&const_val->data.x_bignum, value); + const_val->ok = true; + break; + } } } @@ -2965,6 +2996,25 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } } + // explicit cast from child type of error type to error type + if (wanted_type->id == TypeTableEntryIdError) { + if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { + node->data.fn_call_expr.cast_op = CastOpErrorWrap; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } else if (actual_type->id == TypeTableEntryIdNumLitInt || + actual_type->id == TypeTableEntryIdNumLitFloat) + { + if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) { + node->data.fn_call_expr.cast_op = CastOpErrorWrap; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } else { + return g->builtin_types.entry_invalid; + } + } + } + // explicit cast from number literal to another type if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) @@ -3579,6 +3629,42 @@ static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry } } +static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + BlockContext *child_context = new_block_context(node, context); + node->data.block.block_context = child_context; + TypeTableEntry *return_type = g->builtin_types.entry_void; + + for (int i = 0; i < node->data.block.statements.length; i += 1) { + AstNode *child = node->data.block.statements.at(i); + if (child->type == NodeTypeLabel) { + LabelTableEntry *label_entry = child->data.label.label_entry; + assert(label_entry); + label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable); + return_type = g->builtin_types.entry_void; + continue; + } + if (return_type->id == TypeTableEntryIdUnreachable) { + if (is_node_void_expr(child)) { + // {unreachable;void;void} is allowed. + // ignore void statements once we enter unreachable land. + analyze_expression(g, import, context, g->builtin_types.entry_void, child); + continue; + } + add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code")); + break; + } + bool is_last = (i == node->data.block.statements.length - 1); + TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; + return_type = analyze_expression(g, import, child_context, passed_expected_type, child); + if (!is_last && return_type->id == TypeTableEntryIdMetaType) { + add_node_error(g, child, buf_sprintf("expected expression, found type")); + } + } + return return_type; +} + // When you call analyze_expression, the node you pass might no longer be the child node // you thought it was due to implicit casting rewriting the AST. static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3587,39 +3673,8 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, TypeTableEntry *return_type = nullptr; switch (node->type) { case NodeTypeBlock: - { - BlockContext *child_context = new_block_context(node, context); - node->data.block.block_context = child_context; - return_type = g->builtin_types.entry_void; - - for (int i = 0; i < node->data.block.statements.length; i += 1) { - AstNode *child = node->data.block.statements.at(i); - if (child->type == NodeTypeLabel) { - LabelTableEntry *label_entry = child->data.label.label_entry; - assert(label_entry); - label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable); - return_type = g->builtin_types.entry_void; - continue; - } - if (return_type->id == TypeTableEntryIdUnreachable) { - if (is_node_void_expr(child)) { - // {unreachable;void;void} is allowed. - // ignore void statements once we enter unreachable land. - analyze_expression(g, import, context, g->builtin_types.entry_void, child); - continue; - } - add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code")); - break; - } - bool is_last = (i == node->data.block.statements.length - 1); - TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; - return_type = analyze_expression(g, import, child_context, passed_expected_type, child); - if (!is_last && return_type->id == TypeTableEntryIdMetaType) { - add_node_error(g, child, buf_sprintf("expected expression, found type")); - } - } - break; - } + return_type = analyze_block_expr(g, import, context, expected_type, node); + break; case NodeTypeReturnExpr: return_type = analyze_return_expr(g, import, context, expected_type, node); diff --git a/src/codegen.cpp b/src/codegen.cpp index e2629a44e6..786127cd42 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -268,6 +268,26 @@ static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntr } } +static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeTableEntry *actual_type, + TypeTableEntry *wanted_type, LLVMValueRef expr_val) +{ + if (actual_type->size_in_bits == wanted_type->size_in_bits) { + return expr_val; + } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { + if (actual_type->data.integral.is_signed) { + add_debug_source_node(g, source_node); + return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); + } else { + add_debug_source_node(g, source_node); + return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); + } + } else { + assert(actual_type->size_in_bits > wanted_type->size_in_bits); + add_debug_source_node(g, source_node); + return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); + } +} + static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -288,7 +308,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { case CastOpErrToInt: assert(actual_type->id == TypeTableEntryIdError); if (actual_type->data.error.child_type->size_in_bits == 0) { - return expr_val; + return gen_widen_or_shorten(g, node, g->err_tag_type, wanted_type, expr_val); } else { zig_panic("TODO"); } @@ -309,6 +329,13 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { return cast_expr->tmp_ptr; } + case CastOpErrorWrap: + assert(wanted_type->id == TypeTableEntryIdError); + if (wanted_type->data.error.child_type->size_in_bits == 0) { + return LLVMConstNull(g->err_tag_type->type_ref); + } else { + zig_panic("TODO"); + } case CastOpPtrToInt: add_debug_source_node(g, node); return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); @@ -316,21 +343,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { add_debug_source_node(g, node); return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpIntWidenOrShorten: - if (actual_type->size_in_bits == wanted_type->size_in_bits) { - return expr_val; - } else if (actual_type->size_in_bits < wanted_type->size_in_bits) { - if (actual_type->data.integral.is_signed) { - add_debug_source_node(g, node); - return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, ""); - } else { - add_debug_source_node(g, node); - return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); - } - } else { - assert(actual_type->size_in_bits > wanted_type->size_in_bits); - add_debug_source_node(g, node); - return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, ""); - } + return gen_widen_or_shorten(g, node, actual_type, wanted_type, expr_val); case CastOpToUnknownSizeArray: { assert(cast_expr->tmp_ptr); @@ -1279,7 +1292,7 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV return nullptr; } - assert(!use_expr_value); + assert(!use_expr_value || then_type->id == TypeTableEntryIdError); LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); @@ -1292,7 +1305,12 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV LLVMBuildBr(g->builder, endif_block); LLVMPositionBuilderAtEnd(g->builder, endif_block); - return nullptr; + + if (use_expr_value) { + return LLVMConstNull(g->err_tag_type->type_ref); + } else { + return nullptr; + } } static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) { @@ -2132,7 +2150,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE } } else if (type_entry->id == TypeTableEntryIdError) { if (type_entry->data.error.child_type->size_in_bits == 0) { - return LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err->value, false); + 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 { zig_panic("TODO"); } -- cgit v1.2.3