From 272fe1c54c76ddab2dbecc2b302a651929c4b994 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Jan 2016 15:31:35 -0700 Subject: [PATCH] implicit casts rewrite the AST --- src/all_types.hpp | 46 ++-- src/analyze.cpp | 519 ++++++++++++++++++++++++++------------------- src/codegen.cpp | 202 +++++++----------- src/parser.cpp | 242 ++++++++++++++++++++- src/parser.hpp | 2 + test/run_tests.cpp | 2 +- 6 files changed, 637 insertions(+), 376 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 24d4011bc..118a673c7 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -22,7 +22,6 @@ struct FnTableEntry; struct BlockContext; struct TypeTableEntry; struct VariableTableEntry; -struct Cast; struct BuiltinFnEntry; struct LabelTableEntry; struct TypeStructField; @@ -41,15 +40,6 @@ enum CodeGenBuildType { CodeGenBuildTypeRelease, }; -enum CastOp { - CastOpNothing, - CastOpPtrToInt, - CastOpIntWidenOrShorten, - CastOpToUnknownSizeArray, - CastOpMaybeWrap, - CastOpPointerReinterpret, -}; - struct ConstEnumValue { uint64_t tag; ConstExprValue *payload; @@ -69,29 +59,15 @@ struct ConstExprValue { } data; }; -struct Cast { - CastOp op; - // if op is CastOpArrayToString, this will be a pointer to - // the string struct on the stack - LLVMValueRef ptr; - TypeTableEntry *after_type; - AstNode *source_node; - ConstExprValue const_val; -}; - struct Expr { TypeTableEntry *type_entry; - TypeTableEntry *resolved_type; // the context in which this expression is evaluated. // for blocks, this points to the containing scope, not the block's own scope for its children. BlockContext *block_context; - // may be null for no cast - Cast implicit_cast; // happens first - Cast implicit_maybe_cast; // happens second - LLVMValueRef const_llvm_val; ConstExprValue const_val; + bool has_global_const; }; struct StructValExprCodeGen { @@ -305,6 +281,16 @@ struct AstNodeBinOpExpr { Expr resolved_expr; }; +enum CastOp { + CastOpNoCast, // signifies the function call expression is not a cast + CastOpNoop, // fn call expr is a cast, but does nothing + CastOpPtrToInt, + CastOpIntWidenOrShorten, + CastOpToUnknownSizeArray, + CastOpMaybeWrap, + CastOpPointerReinterpret, +}; + struct AstNodeFnCallExpr { AstNode *fn_ref_expr; ZigList params; @@ -313,8 +299,11 @@ struct AstNodeFnCallExpr { // populated by semantic analyzer: BuiltinFnEntry *builtin_fn; Expr resolved_expr; - Cast cast; FnTableEntry *fn_entry; + CastOp cast_op; + // if cast_op is CastOpArrayToString, this will be a pointer to + // the string struct on the stack + LLVMValueRef tmp_ptr; }; struct AstNodeArrayAccessExpr { @@ -610,6 +599,8 @@ struct AstNodeSymbolExpr { Expr resolved_expr; VariableTableEntry *variable; FnTableEntry *fn_entry; + // set this to instead of analyzing the node, pretend it's a type entry and it's this one. + TypeTableEntry *override_type_entry; }; struct AstNodeBoolLiteral { @@ -644,6 +635,7 @@ struct AstNode { int column; uint32_t create_index; // for determinism purposes ImportTableEntry *owner; + AstNode **parent_field; // for AST rewriting union { AstNodeRoot root; AstNodeRootExportDecl root_export_decl; @@ -997,7 +989,7 @@ struct BlockContext { BlockContext *parent; // null when this is the root HashMap variable_table; HashMap type_table; - ZigList cast_expr_alloca_list; + ZigList cast_alloca_list; ZigList struct_val_expr_alloca_list; ZigList variable_list; AstNode *parent_loop_node; diff --git a/src/analyze.cpp b/src/analyze.cpp index 1325574b0..b751c113d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -17,6 +17,8 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type); static TypeTableEntry *unwrapped_node_type(AstNode *node); +static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node); static AstNode *first_executing_node(AstNode *node) { switch (node->type) { @@ -369,6 +371,9 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, TypeTableEntry *c // and returns invalid type. Otherwise, returns the type of the constant expression value. // Must be called after analyze_expression on the same node. static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { + if (node->type == NodeTypeSymbol && node->data.symbol_expr.override_type_entry) { + return node->data.symbol_expr.override_type_entry; + } Expr *expr = get_resolved_expr(node); assert(expr->type_entry); if (expr->type_entry->id == TypeTableEntryIdInvalid) { @@ -393,8 +398,9 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { - analyze_expression(g, import, context, nullptr, node); - return resolve_type(g, node); + AstNode **node_ptr = node->parent_field; + analyze_expression(g, import, context, nullptr, *node_ptr); + return resolve_type(g, *node_ptr); } static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, @@ -1044,12 +1050,43 @@ static TypeTableEntry *get_return_type(BlockContext *context) { return unwrapped_node_type(return_type_node); } +static bool type_has_codegen_value(TypeTableEntryId id) { + switch (id) { + case TypeTableEntryIdInvalid: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + return false; + + case TypeTableEntryIdBool: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdMaybe: + case TypeTableEntryIdError: + case TypeTableEntryIdEnum: + case TypeTableEntryIdFn: + return true; + } + zig_unreachable(); +} + +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) { + g->global_const_list.append(expr); + expr->has_global_const = true; + } +} + static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) { Expr *expr = get_resolved_expr(literal_node); ConstExprValue *const_val = &expr->const_val; assert(const_val->ok); if (other_type->id == TypeTableEntryIdFloat) { - expr->resolved_type = other_type; return true; } else if (other_type->id == TypeTableEntryIdInt && const_val->data.x_bignum.kind == BigNumKindInt) @@ -1057,7 +1094,6 @@ static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTa if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type->size_in_bits, other_type->data.integral.is_signed)) { - expr->resolved_type = other_type; return true; } } else if (other_type->id == TypeTableEntryIdNumLitFloat || @@ -1145,39 +1181,74 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa return prev_type; } -static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *context, AstNode *node, - TypeTableEntry *expected_type, TypeTableEntry *actual_type) -{ - if (expected_type == nullptr) - return actual_type; // anything will do +static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) - return expected_type; // match - if (expected_type->id == TypeTableEntryIdInvalid || actual_type->id == TypeTableEntryIdInvalid) - return expected_type; // already complained - if (actual_type->id == TypeTableEntryIdUnreachable) - return actual_type; // sorry toots; gotta run. good luck with that expected 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) { - TypeTableEntry *expected_child = expected_type->data.maybe.child_type; - TypeTableEntry *actual_child = actual_type->data.maybe.child_type; - return resolve_type_compatibility(g, context, node, expected_child, actual_child); + 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) +{ + if (types_match_const_cast_only(expected_type, actual_type)) { + return true; } // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdMaybe) { - TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node, - expected_type->data.maybe.child_type, actual_type); - if (resolved_type->id == TypeTableEntryIdInvalid) { - return resolved_type; - } - Expr *expr = get_resolved_expr(node); - expr->implicit_maybe_cast.op = CastOpMaybeWrap; - expr->implicit_maybe_cast.after_type = expected_type; - expr->implicit_maybe_cast.source_node = node; - context->cast_expr_alloca_list.append(&expr->implicit_maybe_cast); - return expected_type; + if (expected_type->id == TypeTableEntryIdMaybe && + types_match_with_implicit_cast(g, expected_type->data.maybe.child_type, actual_type, + literal_node, reported_err)) + { + return true; } // implicit widening conversion @@ -1186,78 +1257,94 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont expected_type->data.integral.is_signed == actual_type->data.integral.is_signed && expected_type->size_in_bits >= actual_type->size_in_bits) { - Expr *expr = get_resolved_expr(node); - expr->implicit_cast.after_type = expected_type; - expr->implicit_cast.op = CastOpIntWidenOrShorten; - expr->implicit_cast.source_node = node; - return expected_type; + return true; } // implicit constant sized array to unknown size array conversion if (expected_type->id == TypeTableEntryIdStruct && expected_type->data.structure.is_unknown_size_array && actual_type->id == TypeTableEntryIdArray && - actual_type->data.array.child_type == expected_type->data.structure.fields[0].type_entry->data.pointer.child_type) + types_match_const_cast_only( + expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, + actual_type->data.array.child_type)) { - Expr *expr = get_resolved_expr(node); - expr->implicit_cast.after_type = expected_type; - expr->implicit_cast.op = CastOpToUnknownSizeArray; - expr->implicit_cast.source_node = node; - context->cast_expr_alloca_list.append(&expr->implicit_cast); - return expected_type; - } - - // implicit non-const to const for pointers - if (expected_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer && - (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const)) - { - TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node, - expected_type->data.pointer.child_type, - actual_type->data.pointer.child_type); - if (resolved_type->id == TypeTableEntryIdInvalid) { - return resolved_type; - } - return expected_type; - } - - // implicit non-const to const for unknown size arrays - 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)) - { - TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node, - expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, - actual_type->data.structure.fields[0].type_entry->data.pointer.child_type); - if (resolved_type->id == TypeTableEntryIdInvalid) { - return resolved_type; - } - return expected_type; + return true; } + // implicit number literal to typed number if ((actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt)) { - if (num_lit_fits_in_other_type(g, node, expected_type)) { - return expected_type; + if (num_lit_fits_in_other_type(g, literal_node, expected_type)) { + return true; } else { - return g->builtin_types.entry_invalid; + *reported_err = true; } } - add_node_error(g, first_executing_node(node), - buf_sprintf("expected type '%s', got '%s'", - buf_ptr(&expected_type->name), - buf_ptr(&actual_type->name))); + + return false; +} + +static AstNode *create_ast_node(CodeGen *g, ImportTableEntry *import, NodeType kind) { + AstNode *node = allocate(1); + node->type = kind; + node->owner = import; + node->create_index = g->next_node_index; + g->next_node_index += 1; + return node; +} + +static AstNode *create_ast_type_node(CodeGen *g, ImportTableEntry *import, TypeTableEntry *type_entry) { + AstNode *node = create_ast_node(g, import, NodeTypeSymbol); + node->data.symbol_expr.override_type_entry = type_entry; + return node; +} + +static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry *import, + BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node) +{ + AstNode *new_parent_node = create_ast_node(g, import, NodeTypeFnCallExpr); + *node->parent_field = new_parent_node; + new_parent_node->parent_field = node->parent_field; + + new_parent_node->data.fn_call_expr.fn_ref_expr = create_ast_type_node(g, import, cast_to_type); + new_parent_node->data.fn_call_expr.params.append(node); + normalize_parent_ptrs(new_parent_node); + + return analyze_expression(g, import, context, cast_to_type, new_parent_node); +} + +static TypeTableEntry *resolve_type_compatibility(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node, + TypeTableEntry *expected_type, TypeTableEntry *actual_type) +{ + if (expected_type == nullptr) + return actual_type; // anything will do + if (expected_type == actual_type) + return expected_type; // match + if (expected_type->id == TypeTableEntryIdInvalid || actual_type->id == TypeTableEntryIdInvalid) + return g->builtin_types.entry_invalid; + if (actual_type->id == TypeTableEntryIdUnreachable) + return actual_type; + + bool reported_err = false; + if (types_match_with_implicit_cast(g, expected_type, actual_type, node, &reported_err)) { + return create_and_analyze_cast_node(g, import, context, expected_type, node); + } + + if (!reported_err) { + add_node_error(g, first_executing_node(node), + buf_sprintf("expected type '%s', got '%s'", + buf_ptr(&expected_type->name), + buf_ptr(&actual_type->name))); + } return g->builtin_types.entry_invalid; } -static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext *block_context, - AstNode *parent_source_node, +static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import, + BlockContext *block_context, AstNode *parent_source_node, AstNode **child_nodes, TypeTableEntry **child_types, int child_count) { assert(child_count > 0); @@ -1270,7 +1357,14 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext } for (int i = 0; i < child_count; i += 1) { - resolve_type_compatibility(g, block_context, child_nodes[i], expected_type, child_types[i]); + if (!child_nodes[i]) { + continue; + } + Expr *expr = get_resolved_expr(child_nodes[i]); + TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, block_context, + child_nodes[i], expected_type, child_types[i]); + expr->type_entry = resolved_type; + add_global_const_expr(g, expr); } return expected_type; @@ -1667,13 +1761,7 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode *node, AstNode *other) { Expr *expr = get_resolved_expr(node); Expr *other_expr = get_resolved_expr(other); - ConstExprValue *other_const_val; - if (other_expr->implicit_maybe_cast.after_type) { - other_const_val = &other_expr->implicit_maybe_cast.const_val; - } else { - other_const_val = &other_expr->const_val; - } - expr->const_val = *other_const_val; + expr->const_val = other_expr->const_val; return other_expr->type_entry; } @@ -1758,6 +1846,10 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode * static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { + if (node->data.symbol_expr.override_type_entry) { + return resolve_expr_const_val_as_type(g, node, node->data.symbol_expr.override_type_entry); + } + Buf *variable_name = &node->data.symbol_expr.symbol; auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name); @@ -1772,13 +1864,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, AstNode *decl_node = var->decl_node; if (decl_node->type == NodeTypeVariableDeclaration) { AstNode *expr_node = decl_node->data.variable_declaration.expr; - Expr *other_expr = get_resolved_expr(expr_node); - ConstExprValue *other_const_val; - if (other_expr->implicit_maybe_cast.after_type) { - other_const_val = &other_expr->implicit_maybe_cast.const_val; - } else { - other_const_val = &other_expr->const_val; - } + ConstExprValue *other_const_val = &get_resolved_expr(expr_node)->const_val; if (other_const_val->ok) { return resolve_expr_const_val_as_other_expr(g, node, expr_node); } @@ -1955,7 +2041,7 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im AstNode *op_nodes[] = {op1, op2}; TypeTableEntry *op_types[] = {op1_type, op2_type}; - TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node, + TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2); if (resolved_type->id == TypeTableEntryIdInvalid) { @@ -2109,7 +2195,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, AstNode *op_nodes[] = {op1, op2}; TypeTableEntry *op_types[] = {lhs_type, rhs_type}; - TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node, + TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node, op_nodes, op_types, 2); if (resolved_type->id == TypeTableEntryIdInvalid) { @@ -2162,6 +2248,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, add_node_error(g, op1, buf_sprintf("expected maybe type, got '%s'", buf_ptr(&lhs_type->name))); + return g->builtin_types.entry_invalid; } } case BinOpTypeInvalid: @@ -2502,8 +2589,8 @@ static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import if (else_node) { else_type = analyze_expression(g, import, context, expected_type, else_node); } else { - else_type = g->builtin_types.entry_void; - else_type = resolve_type_compatibility(g, context, parent_node, expected_type, else_type); + else_type = resolve_type_compatibility(g, import, context, parent_node, expected_type, + g->builtin_types.entry_void); } @@ -2512,7 +2599,7 @@ static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import } else { AstNode *op_nodes[] = {then_block, else_node}; TypeTableEntry *op_types[] = {then_type, else_type}; - return resolve_peer_type_compatibility(g, context, parent_node, op_nodes, op_types, 2); + return resolve_peer_type_compatibility(g, import, context, parent_node, op_nodes, op_types, 2); } } @@ -2616,37 +2703,30 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor } } -static void eval_const_expr_implicit_cast(CodeGen *g, ImportTableEntry *import, BlockContext *context, - AstNode *node, Cast *cast, AstNode *expr_node) -{ - switch (cast->op) { - case CastOpNothing: +static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *expr_node) { + assert(node->type == NodeTypeFnCallExpr); + ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; + ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + if (!other_val->ok) { + return; + } + assert(other_val != const_val); + switch (node->data.fn_call_expr.cast_op) { + case CastOpNoCast: + zig_unreachable(); + case CastOpNoop: case CastOpPtrToInt: case CastOpIntWidenOrShorten: case CastOpPointerReinterpret: - { - ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; - ConstExprValue *const_val = &cast->const_val; - assert(const_val != other_val); - *const_val = *other_val; - break; - } + *const_val = *other_val; + break; case CastOpToUnknownSizeArray: - // TODO eval const expr + zig_panic("TODO CastOpToUnknownSizeArray"); break; case CastOpMaybeWrap: - { - ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; - ConstExprValue *const_val = &cast->const_val; - if (!other_val->ok) { - break; - } - assert(const_val != other_val); - - const_val->data.x_maybe = other_val; - const_val->ok = true; - break; - } + const_val->data.x_maybe = other_val; + const_val->ok = true; + break; } } @@ -2673,51 +2753,91 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B return g->builtin_types.entry_invalid; } - Cast *cast = &node->data.fn_call_expr.cast; - cast->source_node = node; - cast->after_type = wanted_type; + // explicit match or non-const to const + if (types_match_const_cast_only(wanted_type, actual_type)) { + node->data.fn_call_expr.cast_op = CastOpNoop; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } + // explicit cast from pointer to isize or usize if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) && actual_type->id == TypeTableEntryIdPointer) { - cast->op = CastOpPtrToInt; - eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node); + node->data.fn_call_expr.cast_op = CastOpPtrToInt; + eval_const_expr_implicit_cast(g, node, expr_node); return wanted_type; - } else if (wanted_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdInt) - { - cast->op = CastOpIntWidenOrShorten; - eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node); - return wanted_type; - } else if (wanted_type->id == TypeTableEntryIdStruct && - wanted_type->data.structure.is_unknown_size_array && - actual_type->id == TypeTableEntryIdArray && - actual_type->data.array.child_type == wanted_type->data.structure.fields[0].type_entry) - { - cast->op = CastOpToUnknownSizeArray; - context->cast_expr_alloca_list.append(cast); - eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node); - return wanted_type; - } else if (actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt) - { - num_lit_fits_in_other_type(g, expr_node, wanted_type); - cast->op = CastOpNothing; - eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node); - return wanted_type; - } else if (actual_type->id == TypeTableEntryIdPointer && - wanted_type->id == TypeTableEntryIdPointer) - { - cast->op = CastOpPointerReinterpret; - eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node); - return wanted_type; - } else { - add_node_error(g, node, - buf_sprintf("invalid cast from type '%s' to '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->name))); - return g->builtin_types.entry_invalid; } + + // explicit cast from any int to any other int + if (wanted_type->id == TypeTableEntryIdInt && + actual_type->id == TypeTableEntryIdInt) + { + node->data.fn_call_expr.cast_op = CastOpIntWidenOrShorten; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } + + // explicit cast from fixed size array to unknown size array + if (wanted_type->id == TypeTableEntryIdStruct && + wanted_type->data.structure.is_unknown_size_array && + actual_type->id == TypeTableEntryIdArray && + types_match_const_cast_only( + wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type, + actual_type->data.array.child_type)) + { + node->data.fn_call_expr.cast_op = CastOpToUnknownSizeArray; + context->cast_alloca_list.append(node); + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } + + // explicit cast from pointer to another pointer + if (actual_type->id == TypeTableEntryIdPointer && + wanted_type->id == TypeTableEntryIdPointer) + { + node->data.fn_call_expr.cast_op = CastOpPointerReinterpret; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } + + // explicit cast from child type of maybe type to maybe type + if (wanted_type->id == TypeTableEntryIdMaybe) { + if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) { + node->data.fn_call_expr.cast_op = CastOpMaybeWrap; + 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.maybe.child_type)) { + node->data.fn_call_expr.cast_op = CastOpMaybeWrap; + 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) + { + if (num_lit_fits_in_other_type(g, expr_node, wanted_type)) { + node->data.fn_call_expr.cast_op = CastOpNoop; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } else { + return g->builtin_types.entry_invalid; + } + } + + add_node_error(g, node, + buf_sprintf("invalid cast from type '%s' to '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->name))); + return g->builtin_types.entry_invalid; } static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -3281,39 +3401,14 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, actual_return_type = g->builtin_types.entry_invalid; } - resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type); + resolve_type_compatibility(g, import, context, node, expected_return_type, actual_return_type); return g->builtin_types.entry_unreachable; } -static bool type_has_codegen_value(TypeTableEntryId id) { - switch (id) { - case TypeTableEntryIdInvalid: - case TypeTableEntryIdMetaType: - case TypeTableEntryIdVoid: - case TypeTableEntryIdUnreachable: - return false; - - // TODO make num lits return false when we make implicit casts insert ast nodes - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: - - case TypeTableEntryIdBool: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdPointer: - case TypeTableEntryIdArray: - case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: - case TypeTableEntryIdError: - case TypeTableEntryIdEnum: - case TypeTableEntryIdFn: - return true; - } - zig_unreachable(); -} - -static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, +// 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, TypeTableEntry *expected_type, AstNode *node) { TypeTableEntry *return_type = nullptr; @@ -3494,39 +3589,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, zig_unreachable(); } assert(return_type); - resolve_type_compatibility(g, context, node, expected_type, return_type); + // resolve_type_compatibility might do implicit cast which means node is now a child + // of the actual node that we want to return the type of. + //AstNode **field = node->parent_field; + TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, context, node, + expected_type, return_type); Expr *expr = get_resolved_expr(node); - if (!expr->resolved_type) { - expr->resolved_type = return_type; - } - expr->type_entry = expr->resolved_type; + expr->type_entry = return_type; expr->block_context = context; - if (expr->const_val.ok && type_has_codegen_value(expr->resolved_type->id)) { - g->global_const_list.append(expr); - } + add_global_const_expr(g, expr); - - if (expr->type_entry->id == TypeTableEntryIdUnreachable) { - return expr->type_entry; - } - - /* TODO delete this code when we make implicit casts insert ast nodes - Cast *cast_node = &expr->implicit_cast; - if (cast_node->after_type) { - eval_const_expr_implicit_cast(g, import, context, node, cast_node, node); - expr->type_entry = cast_node->after_type; - } - */ - - Cast *cast_node = &expr->implicit_maybe_cast; - if (cast_node->after_type) { - eval_const_expr_implicit_cast(g, import, context, node, cast_node, node); - expr->type_entry = cast_node->after_type; - } - - return expr->type_entry; + return resolved_type; } static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 8e9ebe97a..690086515 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -71,8 +71,6 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op, LLVMValueRef target_ref, LLVMValueRef value, TypeTableEntry *op1_type, TypeTableEntry *op2_type); -static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_val, - TypeTableEntry *actual_type, TypeTableEntry *wanted_type, Cast *cast_node); static TypeTableEntry *get_type_for_type_node(AstNode *node) { Expr *expr = get_resolved_expr(node); @@ -111,17 +109,7 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str, bool c) { } static TypeTableEntry *get_expr_type(AstNode *node) { - Expr *expr = get_resolved_expr(node); - if (expr->implicit_maybe_cast.after_type) { - return expr->implicit_maybe_cast.after_type; - } - if (expr->implicit_cast.after_type) { - return expr->implicit_cast.after_type; - } - if (expr->resolved_type) { - return expr->resolved_type; - } - return expr->type_entry; + return get_resolved_expr(node)->type_entry; } static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) { @@ -305,18 +293,84 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { TypeTableEntry *actual_type = get_expr_type(expr_node); TypeTableEntry *wanted_type = get_expr_type(node); - Cast *cast_node = &node->data.fn_call_expr.cast; + AstNodeFnCallExpr *cast_expr = &node->data.fn_call_expr; - return gen_bare_cast(g, node, expr_val, actual_type, wanted_type, cast_node); + switch (cast_expr->cast_op) { + case CastOpNoCast: + zig_unreachable(); + case CastOpNoop: + return expr_val; + case CastOpMaybeWrap: + { + assert(cast_expr->tmp_ptr); + 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); + + 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; + } + case CastOpPtrToInt: + add_debug_source_node(g, node); + return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpPointerReinterpret: + 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, ""); + } + case CastOpToUnknownSizeArray: + { + assert(cast_expr->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdStruct); + assert(wanted_type->data.structure.is_unknown_size_array); + + TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry; + + add_debug_source_node(g, node); + + LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, ""); + LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, ""); + LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr); + + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, ""); + LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, + actual_type->data.array.len, false); + LLVMBuildStore(g->builder, len_val, len_ptr); + + return cast_expr->tmp_ptr; + } + } + zig_unreachable(); } + static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); if (node->data.fn_call_expr.is_builtin) { return gen_builtin_fn_call_expr(g, node); - } else if (node->data.fn_call_expr.cast.after_type) { + } else if (node->data.fn_call_expr.cast_op != CastOpNoCast) { return gen_cast_expr(g, node); } @@ -752,74 +806,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } -static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_val, - TypeTableEntry *actual_type, TypeTableEntry *wanted_type, Cast *cast_node) -{ - switch (cast_node->op) { - case CastOpNothing: - return expr_val; - case CastOpMaybeWrap: - { - assert(cast_node->ptr); - assert(wanted_type->id == TypeTableEntryIdMaybe); - assert(actual_type); - - add_debug_source_node(g, node); - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 0, ""); - gen_assign_raw(g, node, BinOpTypeAssign, - val_ptr, expr_val, wanted_type->data.maybe.child_type, actual_type); - - add_debug_source_node(g, node); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 1, ""); - LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); - - return cast_node->ptr; - } - case CastOpPtrToInt: - add_debug_source_node(g, node); - return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); - case CastOpPointerReinterpret: - 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, ""); - } - case CastOpToUnknownSizeArray: - { - assert(cast_node->ptr); - - TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry; - - add_debug_source_node(g, node); - - LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 0, ""); - LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, ""); - LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr); - - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 1, ""); - LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, - actual_type->data.array.len, false); - LLVMBuildStore(g->builder, len_val, len_ptr); - - return cast_node->ptr; - } - } - zig_unreachable(); -} - static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, TypeTableEntry *op1_type, TypeTableEntry *op2_type, @@ -1970,7 +1956,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { return phi; } -static LLVMValueRef gen_expr_no_cast(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); @@ -2072,33 +2058,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { zig_unreachable(); } -static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { - LLVMValueRef val = gen_expr_no_cast(g, node); - - if (is_node_void_expr(node)) { - return val; - } - - Expr *expr = get_resolved_expr(node); - - TypeTableEntry *before_type = expr->type_entry; - if (before_type && before_type->id == TypeTableEntryIdUnreachable) { - return val; - } - Cast *cast_node = &expr->implicit_cast; - if (cast_node->after_type) { - val = gen_bare_cast(g, node, val, before_type, cast_node->after_type, cast_node); - before_type = cast_node->after_type; - } - - cast_node = &expr->implicit_maybe_cast; - if (cast_node->after_type) { - val = gen_bare_cast(g, node, val, before_type, cast_node->after_type, cast_node); - } - - return val; -} - static void build_label_blocks(CodeGen *g, AstNode *block_node) { assert(block_node->type == NodeTypeBlock); for (int i = 0; i < block_node->data.block.statements.length; i += 1) { @@ -2180,14 +2139,7 @@ static void gen_const_globals(CodeGen *g) { Expr *expr = g->global_const_list.at(i); ConstExprValue *const_val = &expr->const_val; assert(const_val->ok); - TypeTableEntry *type_entry = expr->resolved_type; - - // TODO delete this if when we make implicit casts insert ast nodes - if (type_entry->id == TypeTableEntryIdNumLitFloat || - type_entry->id == TypeTableEntryIdNumLitInt) - { - continue; - } + TypeTableEntry *type_entry = expr->type_entry; if (handle_is_ptr(type_entry)) { LLVMValueRef global_value = LLVMAddGlobal(g->module, type_entry->type_ref, ""); @@ -2330,10 +2282,12 @@ static void do_code_gen(CodeGen *g) { } // allocate structs which are the result of casts - for (int cea_i = 0; cea_i < block_context->cast_expr_alloca_list.length; cea_i += 1) { - Cast *cast_node = block_context->cast_expr_alloca_list.at(cea_i); - add_debug_source_node(g, cast_node->source_node); - cast_node->ptr = LLVMBuildAlloca(g->builder, cast_node->after_type->type_ref, ""); + for (int cea_i = 0; cea_i < block_context->cast_alloca_list.length; cea_i += 1) { + AstNode *fn_call_node = block_context->cast_alloca_list.at(cea_i); + add_debug_source_node(g, fn_call_node); + Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr; + fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder, + expr->type_entry->type_ref, ""); } // allocate structs which are struct value expressions diff --git a/src/parser.cpp b/src/parser.cpp index 7f9d39ea9..90cabdad2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -173,6 +173,7 @@ void ast_print(AstNode *node, int indent) { for (int i = 0; i < indent; i += 1) { fprintf(stderr, " "); } + assert(node->type == NodeTypeRoot || *node->parent_field == node); switch (node->type) { case NodeTypeRoot: @@ -1008,6 +1009,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int *token_index) { *token_index += 1; ast_expect_token(pc, r_paren, TokenIdRParen); + normalize_parent_ptrs(node); return node; } @@ -1059,6 +1061,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { node->data.param_decl.type = ast_parse_prefix_op_expr(pc, token_index, true); + normalize_parent_ptrs(node); return node; } @@ -1175,6 +1178,7 @@ static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bo node->data.array_type.child_type = ast_parse_prefix_op_expr(pc, token_index, true); + normalize_parent_ptrs(node); return node; } @@ -1358,6 +1362,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand ast_expect_token(pc, rparen_tok, TokenIdRParen); *token_index += 1; + normalize_parent_ptrs(node); return node; } @@ -1416,6 +1421,8 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool ast_eat_token(pc, token_index, TokenIdLParen); ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); node->data.fn_call_expr.is_builtin = true; + + normalize_parent_ptrs(node); return node; } else if (token->id == TokenIdSymbol) { *token_index += 1; @@ -1499,6 +1506,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, int *token_index, ast_buf_from_token(pc, field_name_tok, &field_node->data.struct_val_field.name); field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true); + normalize_parent_ptrs(field_node); node->data.container_init_expr.entries.append(field_node); Token *comma_tok = &pc->tokens->at(*token_index); @@ -1545,6 +1553,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, int *token_index, } } + normalize_parent_ptrs(node); prefix_op_expr = node; } else { return prefix_op_expr; @@ -1575,6 +1584,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo node->data.fn_call_expr.fn_ref_expr = primary_expr; ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); + normalize_parent_ptrs(node); primary_expr = node; } else if (first_token->id == TokenIdLBracket) { *token_index += 1; @@ -1599,6 +1609,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo node->data.slice_expr.is_const = true; } + normalize_parent_ptrs(node); primary_expr = node; } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) { *token_index += 1; @@ -1607,6 +1618,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo node->data.array_access_expr.array_ref_expr = primary_expr; node->data.array_access_expr.subscript = expr_node; + normalize_parent_ptrs(node); primary_expr = node; } else { ast_invalid_token_error(pc, first_token); @@ -1620,6 +1632,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo node->data.field_access_expr.struct_expr = primary_expr; ast_buf_from_token(pc, name_token, &node->data.field_access_expr.field_name); + normalize_parent_ptrs(node); primary_expr = node; } else { return primary_expr; @@ -1677,6 +1690,8 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo node->data.prefix_op_expr.primary_expr = prefix_op_expr; node->data.prefix_op_expr.prefix_op = prefix_op; + normalize_parent_ptrs(node); + normalize_parent_ptrs(parent_node); return parent_node; } @@ -1728,6 +1743,7 @@ static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool man node->data.bin_op_expr.bin_op = mult_op; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -1778,6 +1794,7 @@ static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mand node->data.bin_op_expr.bin_op = add_op; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -1828,6 +1845,7 @@ static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, int *token_index, boo node->data.bin_op_expr.bin_op = bit_shift_op; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -1854,6 +1872,7 @@ static AstNode *ast_parse_bin_and_expr(ParseContext *pc, int *token_index, bool node->data.bin_op_expr.bin_op = BinOpTypeBinAnd; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -1879,6 +1898,7 @@ static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, int *token_index, bool node->data.bin_op_expr.bin_op = BinOpTypeBinXor; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -1904,6 +1924,7 @@ static AstNode *ast_parse_bin_or_expr(ParseContext *pc, int *token_index, bool m node->data.bin_op_expr.bin_op = BinOpTypeBinOr; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -1954,6 +1975,7 @@ static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bo node->data.bin_op_expr.bin_op = cmp_op; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); return node; } @@ -1978,6 +2000,7 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -2043,6 +2066,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda ast_eat_token(pc, token_index, TokenIdRParen); node->data.if_var_expr.then_block = ast_parse_expression(pc, token_index, true); node->data.if_var_expr.else_node = ast_parse_else(pc, token_index, false); + + normalize_parent_ptrs(node); return node; } else { AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_tok); @@ -2050,6 +2075,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda ast_eat_token(pc, token_index, TokenIdRParen); node->data.if_bool_expr.then_block = ast_parse_expression(pc, token_index, true); node->data.if_bool_expr.else_node = ast_parse_else(pc, token_index, false); + + normalize_parent_ptrs(node); return node; } } @@ -2094,6 +2121,8 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token); node->data.return_expr.kind = kind; node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); + + normalize_parent_ptrs(node); return node; } @@ -2156,6 +2185,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token *token_index += 1; if (eq_or_colon->id == TokenIdEq) { node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); + + normalize_parent_ptrs(node); return node; } else if (eq_or_colon->id == TokenIdColon) { node->data.variable_declaration.type = ast_parse_prefix_op_expr(pc, token_index, true); @@ -2165,6 +2196,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); } + + normalize_parent_ptrs(node); return node; } else { ast_invalid_token_error(pc, eq_or_colon); @@ -2192,6 +2225,7 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool node->data.bin_op_expr.bin_op = BinOpTypeBoolOr; node->data.bin_op_expr.op2 = operand_2; + normalize_parent_ptrs(node); operand_1 = node; } } @@ -2220,7 +2254,7 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma node->data.while_expr.body = ast_parse_expression(pc, token_index, true); - + normalize_parent_ptrs(node); return node; } @@ -2262,6 +2296,8 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mand ast_eat_token(pc, token_index, TokenIdRParen); node->data.for_expr.body = ast_parse_expression(pc, token_index, true); + + normalize_parent_ptrs(node); return node; } @@ -2294,6 +2330,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m if (token->id == TokenIdRBrace) { *token_index += 1; + + normalize_parent_ptrs(node); return node; } @@ -2313,6 +2351,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m range_node->data.switch_range.start = expr1; range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true); + + normalize_parent_ptrs(range_node); } else { prong_node->data.switch_prong.items.append(expr1); } @@ -2335,6 +2375,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m ast_eat_token(pc, token_index, TokenIdFatArrow); prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdComma); + + normalize_parent_ptrs(prong_node); } } @@ -2430,6 +2472,7 @@ static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; node->data.bin_op_expr.op2 = rhs; + normalize_parent_ptrs(node); return node; } @@ -2453,6 +2496,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand node->data.bin_op_expr.bin_op = ass_op; node->data.bin_op_expr.op2 = rhs; + normalize_parent_ptrs(node); return node; } @@ -2530,6 +2574,7 @@ static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) { node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token); node->data.container_init_expr.kind = ContainerInitKindArray; buf_init_from_str(&node->data.container_init_expr.type->data.symbol_expr.symbol, "void"); + normalize_parent_ptrs(node); return node; } @@ -2581,6 +2626,8 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato last_token = &pc->tokens->at(*token_index); if (last_token->id == TokenIdRBrace) { *token_index += 1; + + normalize_parent_ptrs(node); return node; } else if (!semicolon_expected) { continue; @@ -2651,6 +2698,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand node->data.fn_proto.return_type = ast_create_void_type_node(pc, next_token); } + normalize_parent_ptrs(node); return node; } @@ -2667,6 +2715,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat ast_eat_token(pc, token_index, TokenIdFatArrow); node->data.fn_def.body = ast_parse_block(pc, token_index, true); + normalize_parent_ptrs(node); return node; } @@ -2683,6 +2732,7 @@ static AstNode *ast_parse_fn_decl(ParseContext *pc, int *token_index) { *token_index += 1; ast_expect_token(pc, semicolon, TokenIdSemicolon); + normalize_parent_ptrs(node); return node; } @@ -2725,6 +2775,8 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool pc->directive_list = nullptr; *token_index += 1; + + normalize_parent_ptrs(node); return node; } else { AstNode *child = ast_parse_fn_decl(pc, token_index); @@ -2768,6 +2820,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b *token_index += 1; ast_expect_token(pc, semicolon, TokenIdSemicolon); + normalize_parent_ptrs(node); return node; } @@ -2795,6 +2848,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index) { node->data.use.directives = pc->directive_list; pc->directive_list = nullptr; + normalize_parent_ptrs(node); return node; } @@ -2895,12 +2949,13 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { } node->data.struct_decl.fields.append(field_node); + normalize_parent_ptrs(field_node); } else { ast_invalid_token_error(pc, token); } } - + normalize_parent_ptrs(node); return node; } @@ -2948,6 +3003,7 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, b node->data.error_value_decl.visib_mod = visib_mod; ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name); + normalize_parent_ptrs(node); return node; } @@ -3026,6 +3082,7 @@ static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { ast_invalid_token_error(pc, &pc->tokens->at(*token_index)); } + normalize_parent_ptrs(node); return node; } @@ -3042,3 +3099,184 @@ AstNode *ast_parse(Buf *buf, ZigList *tokens, ImportTableEntry *owner, pc.root = ast_parse_root(&pc, &token_index); return pc.root; } + +static void set_field(AstNode **field) { + if (*field) { + (*field)->parent_field = field; + } +} + +static void set_list_fields(ZigList *list) { + for (int i = 0; i < list->length; i += 1) { + set_field(&list->at(i)); + } +} + +void normalize_parent_ptrs(AstNode *node) { + switch (node->type) { + case NodeTypeRoot: + set_list_fields(&node->data.root.top_level_decls); + break; + case NodeTypeRootExportDecl: + set_list_fields(node->data.root_export_decl.directives); + break; + case NodeTypeFnProto: + set_field(&node->data.fn_proto.return_type); + set_list_fields(node->data.fn_proto.directives); + set_list_fields(&node->data.fn_proto.params); + break; + case NodeTypeFnDef: + set_field(&node->data.fn_def.fn_proto); + set_field(&node->data.fn_def.body); + break; + case NodeTypeFnDecl: + set_field(&node->data.fn_decl.fn_proto); + break; + case NodeTypeParamDecl: + set_field(&node->data.param_decl.type); + break; + case NodeTypeBlock: + set_list_fields(&node->data.block.statements); + break; + case NodeTypeExternBlock: + set_list_fields(node->data.extern_block.directives); + set_list_fields(&node->data.extern_block.fn_decls); + break; + case NodeTypeDirective: + // none + break; + case NodeTypeReturnExpr: + set_field(&node->data.return_expr.expr); + break; + case NodeTypeVariableDeclaration: + set_field(&node->data.variable_declaration.type); + set_field(&node->data.variable_declaration.expr); + break; + case NodeTypeErrorValueDecl: + // none + break; + case NodeTypeBinOpExpr: + set_field(&node->data.bin_op_expr.op1); + set_field(&node->data.bin_op_expr.op2); + break; + case NodeTypeNumberLiteral: + // none + break; + case NodeTypeStringLiteral: + // none + break; + case NodeTypeCharLiteral: + // none + break; + case NodeTypeErrorLiteral: + // none + break; + case NodeTypeSymbol: + // none + break; + case NodeTypePrefixOpExpr: + set_field(&node->data.prefix_op_expr.primary_expr); + break; + case NodeTypeFnCallExpr: + set_field(&node->data.fn_call_expr.fn_ref_expr); + set_list_fields(&node->data.fn_call_expr.params); + break; + case NodeTypeArrayAccessExpr: + set_field(&node->data.array_access_expr.array_ref_expr); + set_field(&node->data.array_access_expr.subscript); + break; + case NodeTypeSliceExpr: + set_field(&node->data.slice_expr.array_ref_expr); + set_field(&node->data.slice_expr.start); + set_field(&node->data.slice_expr.end); + break; + case NodeTypeFieldAccessExpr: + set_field(&node->data.field_access_expr.struct_expr); + break; + case NodeTypeUse: + set_list_fields(node->data.use.directives); + break; + case NodeTypeBoolLiteral: + // none + break; + case NodeTypeNullLiteral: + // none + break; + case NodeTypeIfBoolExpr: + set_field(&node->data.if_bool_expr.condition); + set_field(&node->data.if_bool_expr.then_block); + set_field(&node->data.if_bool_expr.else_node); + break; + case NodeTypeIfVarExpr: + set_field(&node->data.if_var_expr.var_decl.type); + set_field(&node->data.if_var_expr.var_decl.expr); + set_field(&node->data.if_var_expr.then_block); + set_field(&node->data.if_var_expr.else_node); + break; + case NodeTypeWhileExpr: + set_field(&node->data.while_expr.condition); + set_field(&node->data.while_expr.body); + break; + case NodeTypeForExpr: + set_field(&node->data.for_expr.elem_node); + set_field(&node->data.for_expr.array_expr); + set_field(&node->data.for_expr.index_node); + set_field(&node->data.for_expr.body); + break; + case NodeTypeSwitchExpr: + set_field(&node->data.switch_expr.expr); + set_list_fields(&node->data.switch_expr.prongs); + break; + case NodeTypeSwitchProng: + set_list_fields(&node->data.switch_prong.items); + set_field(&node->data.switch_prong.var_symbol); + set_field(&node->data.switch_prong.expr); + break; + case NodeTypeSwitchRange: + set_field(&node->data.switch_range.start); + set_field(&node->data.switch_range.end); + break; + case NodeTypeLabel: + // none + break; + case NodeTypeGoto: + // none + break; + case NodeTypeBreak: + // none + break; + case NodeTypeContinue: + // none + break; + case NodeTypeAsmExpr: + for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + set_field(&asm_input->expr); + } + for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + set_field(&asm_output->return_type); + } + break; + case NodeTypeStructDecl: + set_list_fields(&node->data.struct_decl.fields); + set_list_fields(&node->data.struct_decl.fns); + set_list_fields(node->data.struct_decl.directives); + break; + case NodeTypeStructField: + set_field(&node->data.struct_field.type); + set_list_fields(node->data.struct_field.directives); + break; + case NodeTypeContainerInitExpr: + set_field(&node->data.container_init_expr.type); + set_list_fields(&node->data.container_init_expr.entries); + break; + case NodeTypeStructValueField: + set_field(&node->data.struct_val_field.expr); + break; + case NodeTypeArrayType: + set_field(&node->data.array_type.size); + set_field(&node->data.array_type.child_type); + break; + } +} diff --git a/src/parser.hpp b/src/parser.hpp index 230ff5c69..6cdff2232 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -24,4 +24,6 @@ const char *node_type_str(NodeType node_type); void ast_print(AstNode *node, int indent); +void normalize_parent_ptrs(AstNode *node); + #endif diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 1c6a78d81..6457395e4 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1317,7 +1317,7 @@ fn f() i32 => { const a = c"a"; a } - )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got '&const u8'"); + )SOURCE", 1, ".tmp_source.zig:4:5: error: expected type 'i32', got '&const u8'"); add_compile_fail_case("if condition is bool, not int", R"SOURCE( fn f() => {