| author | |
| committer | |
| log | 579856e502eab87ea8a73a76dc63a2108e5a8cc8 |
| tree | feb34b0985b29f45073ac81e21982a368e0a3542 |
| parent | 5a479720ec5786145dac6c85deae4e322bd5972e |
| parent | fcedc35551cc6b14756499414e47c33004de3be4 |
15 files changed, 1693 insertions(+), 469 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -41,6 +41,7 @@ set(ZIG_SOURCES |
| 41 | 41 | "${CMAKE_SOURCE_DIR}/src/bignum.cpp" |
| 42 | 42 | "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" |
| 43 | 43 | "${CMAKE_SOURCE_DIR}/src/parser.cpp" |
| 44 | "${CMAKE_SOURCE_DIR}/src/eval.cpp" | |
| 44 | 45 | "${CMAKE_SOURCE_DIR}/src/analyze.cpp" |
| 45 | 46 | "${CMAKE_SOURCE_DIR}/src/codegen.cpp" |
| 46 | 47 | "${CMAKE_SOURCE_DIR}/src/buffer.cpp" |
src/all_types.hpp+38-1| ... | ... | @@ -496,8 +496,8 @@ struct AstNodeWhileExpr { |
| 496 | 496 | }; |
| 497 | 497 | |
| 498 | 498 | struct AstNodeForExpr { |
| 499 | AstNode *elem_node; // always a symbol | |
| 500 | 499 | AstNode *array_expr; |
| 500 | AstNode *elem_node; // always a symbol | |
| 501 | 501 | AstNode *index_node; // always a symbol, might be null |
| 502 | 502 | AstNode *body; |
| 503 | 503 | |
| ... | ... | @@ -960,6 +960,7 @@ struct TypeTableEntry { |
| 960 | 960 | LLVMZigDIType *di_type; |
| 961 | 961 | |
| 962 | 962 | bool zero_bits; |
| 963 | bool deep_const; | |
| 963 | 964 | |
| 964 | 965 | union { |
| 965 | 966 | TypeTableEntryPointer pointer; |
| ... | ... | @@ -1005,6 +1006,13 @@ struct ImportTableEntry { |
| 1005 | 1006 | ZigList<AstNode *> use_decls; |
| 1006 | 1007 | }; |
| 1007 | 1008 | |
| 1009 | enum FnAnalState { | |
| 1010 | FnAnalStateReady, | |
| 1011 | FnAnalStateProbing, | |
| 1012 | FnAnalStateComplete, | |
| 1013 | FnAnalStateSkipped, | |
| 1014 | }; | |
| 1015 | ||
| 1008 | 1016 | struct FnTableEntry { |
| 1009 | 1017 | LLVMValueRef fn_value; |
| 1010 | 1018 | AstNode *proto_node; |
| ... | ... | @@ -1019,7 +1027,9 @@ struct FnTableEntry { |
| 1019 | 1027 | bool internal_linkage; |
| 1020 | 1028 | bool is_extern; |
| 1021 | 1029 | bool is_test; |
| 1030 | bool is_pure; | |
| 1022 | 1031 | BlockContext *parent_block_context; |
| 1032 | FnAnalState anal_state; | |
| 1023 | 1033 | |
| 1024 | 1034 | ZigList<AstNode *> cast_alloca_list; |
| 1025 | 1035 | ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list; |
| ... | ... | @@ -1027,6 +1037,33 @@ struct FnTableEntry { |
| 1027 | 1037 | ZigList<AstNode *> goto_list; |
| 1028 | 1038 | }; |
| 1029 | 1039 | |
| 1040 | struct EvalVar { | |
| 1041 | Buf *name; | |
| 1042 | ConstExprValue value; | |
| 1043 | }; | |
| 1044 | ||
| 1045 | struct EvalScope { | |
| 1046 | BlockContext *block_context; | |
| 1047 | ZigList<EvalVar> vars; | |
| 1048 | }; | |
| 1049 | ||
| 1050 | struct EvalFnRoot { | |
| 1051 | CodeGen *codegen; | |
| 1052 | FnTableEntry *fn; | |
| 1053 | AstNode *call_node; | |
| 1054 | int branch_quota; | |
| 1055 | int branches_used; | |
| 1056 | AstNode *exceeded_quota_node; | |
| 1057 | bool abort; | |
| 1058 | }; | |
| 1059 | ||
| 1060 | struct EvalFn { | |
| 1061 | EvalFnRoot *root; | |
| 1062 | FnTableEntry *fn; | |
| 1063 | ConstExprValue *return_expr; | |
| 1064 | ZigList<EvalScope*> scope_stack; | |
| 1065 | }; | |
| 1066 | ||
| 1030 | 1067 | enum BuiltinFnId { |
| 1031 | 1068 | BuiltinFnIdInvalid, |
| 1032 | 1069 | BuiltinFnIdMemcpy, |
src/analyze.cpp+176-321| ... | ... | @@ -13,6 +13,7 @@ |
| 13 | 13 | #include "parseh.hpp" |
| 14 | 14 | #include "config.h" |
| 15 | 15 | #include "ast_render.hpp" |
| 16 | #include "eval.hpp" | |
| 16 | 17 | |
| 17 | 18 | static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 18 | 19 | TypeTableEntry *expected_type, AstNode *node); |
| ... | ... | @@ -34,7 +35,8 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, |
| 34 | 35 | static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, |
| 35 | 36 | TypeTableEntry *expected_type, uint64_t x); |
| 36 | 37 | static AstNode *find_decl(BlockContext *context, Buf *name); |
| 37 | static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only); | |
| 38 | static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, | |
| 39 | bool pointer_only, BlockContext *block_context); | |
| 38 | 40 | static TopLevelDecl *get_as_top_level_decl(AstNode *node); |
| 39 | 41 | static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, |
| 40 | 42 | BlockContext *context, AstNode *source_node, |
| ... | ... | @@ -206,6 +208,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { |
| 206 | 208 | static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) { |
| 207 | 209 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdGenericFn); |
| 208 | 210 | buf_init_from_str(&entry->name, "(generic function)"); |
| 211 | entry->deep_const = true; | |
| 209 | 212 | entry->zero_bits = true; |
| 210 | 213 | entry->data.generic_fn.decl_node = decl_node; |
| 211 | 214 | return entry; |
| ... | ... | @@ -219,6 +222,8 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool |
| 219 | 222 | } else { |
| 220 | 223 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer); |
| 221 | 224 | |
| 225 | entry->deep_const = is_const && child_type->deep_const; | |
| 226 | ||
| 222 | 227 | const char *const_str = is_const ? "const " : ""; |
| 223 | 228 | buf_resize(&entry->name, 0); |
| 224 | 229 | buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name)); |
| ... | ... | @@ -260,6 +265,8 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { |
| 260 | 265 | assert(child_type->type_ref); |
| 261 | 266 | assert(child_type->di_type); |
| 262 | 267 | |
| 268 | entry->deep_const = child_type->deep_const; | |
| 269 | ||
| 263 | 270 | buf_resize(&entry->name, 0); |
| 264 | 271 | buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name)); |
| 265 | 272 | |
| ... | ... | @@ -343,6 +350,8 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { |
| 343 | 350 | |
| 344 | 351 | entry->data.error.child_type = child_type; |
| 345 | 352 | |
| 353 | entry->deep_const = child_type->deep_const; | |
| 354 | ||
| 346 | 355 | if (!type_has_bits(child_type)) { |
| 347 | 356 | entry->type_ref = g->err_tag_type->type_ref; |
| 348 | 357 | entry->di_type = g->err_tag_type->di_type; |
| ... | ... | @@ -414,6 +423,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t |
| 414 | 423 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); |
| 415 | 424 | entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); |
| 416 | 425 | entry->zero_bits = (array_size == 0) || child_type->zero_bits; |
| 426 | entry->deep_const = child_type->deep_const; | |
| 417 | 427 | |
| 418 | 428 | buf_resize(&entry->name, 0); |
| 419 | 429 | buf_appendf(&entry->name, "[%" PRIu64 "]%s", array_size, buf_ptr(&child_type->name)); |
| ... | ... | @@ -464,6 +474,8 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c |
| 464 | 474 | TypeTableEntry *var_peer = get_slice_type(g, child_type, false); |
| 465 | 475 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct); |
| 466 | 476 | |
| 477 | entry->deep_const = child_type->deep_const; | |
| 478 | ||
| 467 | 479 | buf_resize(&entry->name, 0); |
| 468 | 480 | buf_appendf(&entry->name, "[]const %s", buf_ptr(&child_type->name)); |
| 469 | 481 | |
| ... | ... | @@ -549,6 +561,7 @@ TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry * |
| 549 | 561 | |
| 550 | 562 | buf_init_from_str(&entry->name, name); |
| 551 | 563 | |
| 564 | entry->deep_const = child_type->deep_const; | |
| 552 | 565 | entry->type_ref = child_type->type_ref; |
| 553 | 566 | entry->di_type = child_type->di_type; |
| 554 | 567 | entry->zero_bits = child_type->zero_bits; |
| ... | ... | @@ -572,6 +585,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 572 | 585 | } |
| 573 | 586 | |
| 574 | 587 | TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); |
| 588 | fn_type->deep_const = true; | |
| 575 | 589 | fn_type->data.fn.fn_type_id = *fn_type_id; |
| 576 | 590 | if (fn_type_id->param_info == &fn_type_id->prealloc_param_info[0]) { |
| 577 | 591 | fn_type->data.fn.fn_type_id.param_info = &fn_type->data.fn.fn_type_id.prealloc_param_info[0]; |
| ... | ... | @@ -944,6 +958,19 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 944 | 958 | add_node_error(g, directive_node, |
| 945 | 959 | buf_sprintf("#condition valid only on exported symbols")); |
| 946 | 960 | } |
| 961 | } else if (buf_eql_str(name, "static_eval_enable")) { | |
| 962 | if (fn_table_entry->is_extern) { | |
| 963 | add_node_error(g, directive_node, | |
| 964 | buf_sprintf("#static_val_enable invalid on extern functions")); | |
| 965 | } else { | |
| 966 | bool enable; | |
| 967 | bool ok = resolve_const_expr_bool(g, import, import->block_context, | |
| 968 | &directive_node->data.directive.expr, &enable); | |
| 969 | if (!enable || !ok) { | |
| 970 | fn_table_entry->is_pure = false; | |
| 971 | } | |
| 972 | // TODO cause compile error if enable is true and impure fn | |
| 973 | } | |
| 947 | 974 | } else { |
| 948 | 975 | add_node_error(g, directive_node, |
| 949 | 976 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); |
| ... | ... | @@ -1037,6 +1064,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt |
| 1037 | 1064 | |
| 1038 | 1065 | assert(enum_type->di_type); |
| 1039 | 1066 | |
| 1067 | enum_type->deep_const = true; | |
| 1068 | ||
| 1040 | 1069 | uint32_t field_count = decl_node->data.struct_decl.fields.length; |
| 1041 | 1070 | |
| 1042 | 1071 | enum_type->data.enumeration.field_count = field_count; |
| ... | ... | @@ -1064,6 +1093,10 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt |
| 1064 | 1093 | type_enum_field->type_entry = field_type; |
| 1065 | 1094 | type_enum_field->value = i; |
| 1066 | 1095 | |
| 1096 | if (!field_type->deep_const) { | |
| 1097 | enum_type->deep_const = false; | |
| 1098 | } | |
| 1099 | ||
| 1067 | 1100 | |
| 1068 | 1101 | di_enumerators[i] = LLVMZigCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i); |
| 1069 | 1102 | |
| ... | ... | @@ -1224,6 +1257,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE |
| 1224 | 1257 | |
| 1225 | 1258 | assert(struct_type->di_type); |
| 1226 | 1259 | |
| 1260 | struct_type->deep_const = true; | |
| 1261 | ||
| 1227 | 1262 | int field_count = decl_node->data.struct_decl.fields.length; |
| 1228 | 1263 | |
| 1229 | 1264 | struct_type->data.structure.src_field_count = field_count; |
| ... | ... | @@ -1247,6 +1282,10 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE |
| 1247 | 1282 | type_struct_field->src_index = i; |
| 1248 | 1283 | type_struct_field->gen_index = -1; |
| 1249 | 1284 | |
| 1285 | if (!field_type->deep_const) { | |
| 1286 | struct_type->deep_const = false; | |
| 1287 | } | |
| 1288 | ||
| 1250 | 1289 | if (field_type->id == TypeTableEntryIdStruct) { |
| 1251 | 1290 | resolve_struct_type(g, import, field_type); |
| 1252 | 1291 | } else if (field_type->id == TypeTableEntryIdEnum) { |
| ... | ... | @@ -1374,6 +1413,7 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN |
| 1374 | 1413 | fn_table_entry->proto_node = proto_node; |
| 1375 | 1414 | fn_table_entry->fn_def_node = fn_def_node; |
| 1376 | 1415 | fn_table_entry->is_extern = is_extern; |
| 1416 | fn_table_entry->is_pure = !is_extern; | |
| 1377 | 1417 | |
| 1378 | 1418 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); |
| 1379 | 1419 | |
| ... | ... | @@ -2201,9 +2241,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry |
| 2201 | 2241 | const_val->ok = false; |
| 2202 | 2242 | } |
| 2203 | 2243 | } |
| 2204 | if (!const_val->ok) { | |
| 2205 | context->fn_entry->struct_val_expr_alloca_list.append(codegen); | |
| 2206 | } | |
| 2244 | } | |
| 2245 | if (!const_val->ok) { | |
| 2246 | context->fn_entry->struct_val_expr_alloca_list.append(codegen); | |
| 2207 | 2247 | } |
| 2208 | 2248 | |
| 2209 | 2249 | for (int i = 0; i < actual_field_count; i += 1) { |
| ... | ... | @@ -2355,7 +2395,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 2355 | 2395 | AstNode *decl_node = entry ? entry->value : nullptr; |
| 2356 | 2396 | if (decl_node) { |
| 2357 | 2397 | bool pointer_only = false; |
| 2358 | return analyze_decl_ref(g, node, decl_node, pointer_only); | |
| 2398 | return analyze_decl_ref(g, node, decl_node, pointer_only, context); | |
| 2359 | 2399 | } else { |
| 2360 | 2400 | add_node_error(g, node, |
| 2361 | 2401 | buf_sprintf("container '%s' has no member called '%s'", |
| ... | ... | @@ -2382,7 +2422,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i |
| 2382 | 2422 | add_error_note(g, msg, decl_node, buf_sprintf("declared here")); |
| 2383 | 2423 | } |
| 2384 | 2424 | bool pointer_only = false; |
| 2385 | return analyze_decl_ref(g, node, decl_node, pointer_only); | |
| 2425 | return analyze_decl_ref(g, node, decl_node, pointer_only, context); | |
| 2386 | 2426 | } else { |
| 2387 | 2427 | const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)"; |
| 2388 | 2428 | add_node_error(g, node, |
| ... | ... | @@ -2443,6 +2483,12 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, |
| 2443 | 2483 | return return_type; |
| 2444 | 2484 | } |
| 2445 | 2485 | |
| 2486 | static void mark_impure_fn(BlockContext *context) { | |
| 2487 | if (context->fn_entry) { | |
| 2488 | context->fn_entry->is_pure = false; | |
| 2489 | } | |
| 2490 | } | |
| 2491 | ||
| 2446 | 2492 | static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2447 | 2493 | AstNode *node) |
| 2448 | 2494 | { |
| ... | ... | @@ -2607,26 +2653,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo |
| 2607 | 2653 | return g->builtin_types.entry_num_lit_float; |
| 2608 | 2654 | } |
| 2609 | 2655 | |
| 2610 | static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node, | |
| 2611 | bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2, | |
| 2612 | TypeTableEntry *resolved_type) | |
| 2613 | { | |
| 2614 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 2615 | ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val; | |
| 2616 | ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val; | |
| 2617 | ||
| 2618 | const_val->ok = true; | |
| 2619 | ||
| 2620 | if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { | |
| 2621 | add_node_error(g, node, | |
| 2622 | buf_sprintf("value cannot be represented in any integer type")); | |
| 2623 | } else { | |
| 2624 | num_lit_fits_in_other_type(g, node, resolved_type); | |
| 2625 | } | |
| 2626 | ||
| 2627 | return resolved_type; | |
| 2628 | } | |
| 2629 | ||
| 2630 | 2656 | static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, |
| 2631 | 2657 | BlockContext *context, AstNode *node, Buf *err_name) |
| 2632 | 2658 | { |
| ... | ... | @@ -2642,8 +2668,21 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry * |
| 2642 | 2668 | return g->builtin_types.entry_invalid; |
| 2643 | 2669 | } |
| 2644 | 2670 | |
| 2645 | static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var) { | |
| 2671 | static bool var_is_pure(VariableTableEntry *var, BlockContext *context) { | |
| 2672 | if (var->block_context->fn_entry == context->fn_entry) { | |
| 2673 | // variable was declared in the current function, so it's OK. | |
| 2674 | return true; | |
| 2675 | } | |
| 2676 | return var->is_const && var->type->deep_const; | |
| 2677 | } | |
| 2678 | ||
| 2679 | static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var, | |
| 2680 | BlockContext *context) | |
| 2681 | { | |
| 2646 | 2682 | get_resolved_expr(source_node)->variable = var; |
| 2683 | if (!var_is_pure(var, context)) { | |
| 2684 | mark_impure_fn(context); | |
| 2685 | } | |
| 2647 | 2686 | if (var->is_const && var->val_node) { |
| 2648 | 2687 | ConstExprValue *other_const_val = &get_resolved_expr(var->val_node)->const_val; |
| 2649 | 2688 | if (other_const_val->ok) { |
| ... | ... | @@ -2654,7 +2693,7 @@ static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, Variabl |
| 2654 | 2693 | } |
| 2655 | 2694 | |
| 2656 | 2695 | static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, |
| 2657 | bool pointer_only) | |
| 2696 | bool pointer_only, BlockContext *block_context) | |
| 2658 | 2697 | { |
| 2659 | 2698 | resolve_top_level_decl(g, decl_node, pointer_only); |
| 2660 | 2699 | TopLevelDecl *tld = get_as_top_level_decl(decl_node); |
| ... | ... | @@ -2664,7 +2703,7 @@ static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNod |
| 2664 | 2703 | |
| 2665 | 2704 | if (decl_node->type == NodeTypeVariableDeclaration) { |
| 2666 | 2705 | VariableTableEntry *var = decl_node->data.variable_declaration.variable; |
| 2667 | return analyze_var_ref(g, source_node, var); | |
| 2706 | return analyze_var_ref(g, source_node, var, block_context); | |
| 2668 | 2707 | } else if (decl_node->type == NodeTypeFnProto) { |
| 2669 | 2708 | if (decl_node->data.fn_proto.generic_params.length > 0) { |
| 2670 | 2709 | TypeTableEntry *type_entry = decl_node->data.fn_proto.generic_fn_type; |
| ... | ... | @@ -2700,12 +2739,13 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, |
| 2700 | 2739 | |
| 2701 | 2740 | VariableTableEntry *var = find_variable(g, context, variable_name); |
| 2702 | 2741 | if (var) { |
| 2703 | return analyze_var_ref(g, node, var); | |
| 2742 | TypeTableEntry *var_type = analyze_var_ref(g, node, var, context); | |
| 2743 | return var_type; | |
| 2704 | 2744 | } |
| 2705 | 2745 | |
| 2706 | 2746 | AstNode *decl_node = find_decl(context, variable_name); |
| 2707 | 2747 | if (decl_node) { |
| 2708 | return analyze_decl_ref(g, node, decl_node, pointer_only); | |
| 2748 | return analyze_decl_ref(g, node, decl_node, pointer_only, context); | |
| 2709 | 2749 | } |
| 2710 | 2750 | |
| 2711 | 2751 | if (import->any_imports_failed) { |
| ... | ... | @@ -2840,71 +2880,6 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc |
| 2840 | 2880 | return expected_rhs_type; |
| 2841 | 2881 | } |
| 2842 | 2882 | |
| 2843 | static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) { | |
| 2844 | if (bin_op == BinOpTypeBoolOr) { | |
| 2845 | return a || b; | |
| 2846 | } else if (bin_op == BinOpTypeBoolAnd) { | |
| 2847 | return a && b; | |
| 2848 | } else { | |
| 2849 | zig_unreachable(); | |
| 2850 | } | |
| 2851 | } | |
| 2852 | ||
| 2853 | static bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) { | |
| 2854 | switch (type_entry->id) { | |
| 2855 | case TypeTableEntryIdEnum: | |
| 2856 | { | |
| 2857 | ConstEnumValue *enum1 = &a->data.x_enum; | |
| 2858 | ConstEnumValue *enum2 = &b->data.x_enum; | |
| 2859 | if (enum1->tag == enum2->tag) { | |
| 2860 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum1->tag]; | |
| 2861 | if (type_has_bits(enum_field->type_entry)) { | |
| 2862 | zig_panic("TODO const expr analyze enum special value for equality"); | |
| 2863 | } else { | |
| 2864 | return true; | |
| 2865 | } | |
| 2866 | } | |
| 2867 | return false; | |
| 2868 | } | |
| 2869 | case TypeTableEntryIdMetaType: | |
| 2870 | return a->data.x_type == b->data.x_type; | |
| 2871 | case TypeTableEntryIdVoid: | |
| 2872 | return true; | |
| 2873 | case TypeTableEntryIdPureError: | |
| 2874 | return a->data.x_err.err == b->data.x_err.err; | |
| 2875 | case TypeTableEntryIdFn: | |
| 2876 | return a->data.x_fn == b->data.x_fn; | |
| 2877 | case TypeTableEntryIdBool: | |
| 2878 | return a->data.x_bool == b->data.x_bool; | |
| 2879 | case TypeTableEntryIdInt: | |
| 2880 | case TypeTableEntryIdFloat: | |
| 2881 | case TypeTableEntryIdNumLitFloat: | |
| 2882 | case TypeTableEntryIdNumLitInt: | |
| 2883 | return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum); | |
| 2884 | case TypeTableEntryIdPointer: | |
| 2885 | zig_panic("TODO"); | |
| 2886 | case TypeTableEntryIdArray: | |
| 2887 | zig_panic("TODO"); | |
| 2888 | case TypeTableEntryIdStruct: | |
| 2889 | zig_panic("TODO"); | |
| 2890 | case TypeTableEntryIdUndefLit: | |
| 2891 | zig_panic("TODO"); | |
| 2892 | case TypeTableEntryIdMaybe: | |
| 2893 | zig_panic("TODO"); | |
| 2894 | case TypeTableEntryIdErrorUnion: | |
| 2895 | zig_panic("TODO"); | |
| 2896 | case TypeTableEntryIdTypeDecl: | |
| 2897 | zig_panic("TODO"); | |
| 2898 | case TypeTableEntryIdNamespace: | |
| 2899 | zig_panic("TODO"); | |
| 2900 | case TypeTableEntryIdGenericFn: | |
| 2901 | case TypeTableEntryIdInvalid: | |
| 2902 | case TypeTableEntryIdUnreachable: | |
| 2903 | zig_unreachable(); | |
| 2904 | } | |
| 2905 | zig_unreachable(); | |
| 2906 | } | |
| 2907 | ||
| 2908 | 2883 | static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2909 | 2884 | AstNode *node) |
| 2910 | 2885 | { |
| ... | ... | @@ -2944,39 +2919,11 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im |
| 2944 | 2919 | return g->builtin_types.entry_bool; |
| 2945 | 2920 | } |
| 2946 | 2921 | |
| 2947 | bool answer; | |
| 2948 | if (type_can_gt_lt_cmp) { | |
| 2949 | bool (*bignum_cmp)(BigNum *, BigNum *); | |
| 2950 | if (bin_op_type == BinOpTypeCmpEq) { | |
| 2951 | bignum_cmp = bignum_cmp_eq; | |
| 2952 | } else if (bin_op_type == BinOpTypeCmpNotEq) { | |
| 2953 | bignum_cmp = bignum_cmp_neq; | |
| 2954 | } else if (bin_op_type == BinOpTypeCmpLessThan) { | |
| 2955 | bignum_cmp = bignum_cmp_lt; | |
| 2956 | } else if (bin_op_type == BinOpTypeCmpGreaterThan) { | |
| 2957 | bignum_cmp = bignum_cmp_gt; | |
| 2958 | } else if (bin_op_type == BinOpTypeCmpLessOrEq) { | |
| 2959 | bignum_cmp = bignum_cmp_lte; | |
| 2960 | } else if (bin_op_type == BinOpTypeCmpGreaterOrEq) { | |
| 2961 | bignum_cmp = bignum_cmp_gte; | |
| 2962 | } else { | |
| 2963 | zig_unreachable(); | |
| 2964 | } | |
| 2965 | 2922 | |
| 2966 | answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum); | |
| 2967 | } else { | |
| 2968 | bool are_equal = const_values_equal(op1_val, op2_val, resolved_type); | |
| 2969 | if (bin_op_type == BinOpTypeCmpEq) { | |
| 2970 | answer = are_equal; | |
| 2971 | } else if (bin_op_type == BinOpTypeCmpNotEq) { | |
| 2972 | answer = !are_equal; | |
| 2973 | } else { | |
| 2974 | zig_unreachable(); | |
| 2975 | } | |
| 2976 | } | |
| 2923 | ConstExprValue *out_val = &get_resolved_expr(node)->const_val; | |
| 2924 | eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val); | |
| 2925 | return g->builtin_types.entry_bool; | |
| 2977 | 2926 | |
| 2978 | bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; | |
| 2979 | return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var); | |
| 2980 | 2927 | } |
| 2981 | 2928 | |
| 2982 | 2929 | static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| ... | ... | @@ -3002,9 +2949,9 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i |
| 3002 | 2949 | return g->builtin_types.entry_bool; |
| 3003 | 2950 | } |
| 3004 | 2951 | |
| 3005 | bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool); | |
| 3006 | bool depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; | |
| 3007 | return resolve_expr_const_val_as_bool(g, node, answer, depends_on_compile_var); | |
| 2952 | ConstExprValue *out_val = &get_resolved_expr(node)->const_val; | |
| 2953 | eval_const_expr_bin_op(op1_val, op1_type, bin_op_type, op2_val, op2_type, out_val); | |
| 2954 | return g->builtin_types.entry_bool; | |
| 3008 | 2955 | } |
| 3009 | 2956 | |
| 3010 | 2957 | static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| ... | ... | @@ -3041,6 +2988,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3041 | 2988 | } |
| 3042 | 2989 | |
| 3043 | 2990 | analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); |
| 2991 | // not const ok because expression has side effects | |
| 3044 | 2992 | return g->builtin_types.entry_void; |
| 3045 | 2993 | } |
| 3046 | 2994 | case BinOpTypeBoolOr: |
| ... | ... | @@ -3106,37 +3054,23 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3106 | 3054 | return resolved_type; |
| 3107 | 3055 | } |
| 3108 | 3056 | |
| 3109 | if (bin_op_type == BinOpTypeAdd) { | |
| 3110 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, *op1, *op2, resolved_type); | |
| 3111 | } else if (bin_op_type == BinOpTypeSub) { | |
| 3112 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, *op1, *op2, resolved_type); | |
| 3113 | } else if (bin_op_type == BinOpTypeMult) { | |
| 3114 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, *op1, *op2, resolved_type); | |
| 3115 | } else if (bin_op_type == BinOpTypeDiv) { | |
| 3116 | ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; | |
| 3117 | if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || | |
| 3118 | (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) | |
| 3119 | { | |
| 3057 | ConstExprValue *out_val = &get_resolved_expr(node)->const_val; | |
| 3058 | int err; | |
| 3059 | if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type, | |
| 3060 | op2_val, resolved_type, out_val))) | |
| 3061 | { | |
| 3062 | if (err == ErrorDivByZero) { | |
| 3120 | 3063 | add_node_error(g, node, buf_sprintf("division by zero is undefined")); |
| 3121 | 3064 | return g->builtin_types.entry_invalid; |
| 3122 | } else { | |
| 3123 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, *op1, *op2, resolved_type); | |
| 3065 | } else if (err == ErrorOverflow) { | |
| 3066 | add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type")); | |
| 3067 | return g->builtin_types.entry_invalid; | |
| 3124 | 3068 | } |
| 3125 | } else if (bin_op_type == BinOpTypeMod) { | |
| 3126 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, *op1, *op2, resolved_type); | |
| 3127 | } else if (bin_op_type == BinOpTypeBinOr) { | |
| 3128 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, *op1, *op2, resolved_type); | |
| 3129 | } else if (bin_op_type == BinOpTypeBinAnd) { | |
| 3130 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, *op1, *op2, resolved_type); | |
| 3131 | } else if (bin_op_type == BinOpTypeBinXor) { | |
| 3132 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, *op1, *op2, resolved_type); | |
| 3133 | } else if (bin_op_type == BinOpTypeBitShiftLeft) { | |
| 3134 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, *op1, *op2, resolved_type); | |
| 3135 | } else if (bin_op_type == BinOpTypeBitShiftRight) { | |
| 3136 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, *op1, *op2, resolved_type); | |
| 3137 | } else { | |
| 3138 | zig_unreachable(); | |
| 3069 | return g->builtin_types.entry_invalid; | |
| 3139 | 3070 | } |
| 3071 | ||
| 3072 | num_lit_fits_in_other_type(g, node, resolved_type); | |
| 3073 | return resolved_type; | |
| 3140 | 3074 | } |
| 3141 | 3075 | case BinOpTypeUnwrapMaybe: |
| 3142 | 3076 | { |
| ... | ... | @@ -3720,6 +3654,10 @@ static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import |
| 3720 | 3654 | } |
| 3721 | 3655 | |
| 3722 | 3656 | ConstExprValue *cond_val = &get_resolved_expr(*cond)->const_val; |
| 3657 | if (cond_val->undef) { | |
| 3658 | add_node_error(g, first_executing_node(*cond), buf_sprintf("branch on undefined value")); | |
| 3659 | return cond_type; | |
| 3660 | } | |
| 3723 | 3661 | if (cond_val->ok && !cond_val->depends_on_compile_var) { |
| 3724 | 3662 | const char *str_val = cond_val->data.x_bool ? "true" : "false"; |
| 3725 | 3663 | add_node_error(g, first_executing_node(*cond), |
| ... | ... | @@ -3762,17 +3700,6 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, |
| 3762 | 3700 | node, then_node, else_node, cond_is_const, cond_bool_val); |
| 3763 | 3701 | } |
| 3764 | 3702 | |
| 3765 | static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) { | |
| 3766 | assert(int_type->id == TypeTableEntryIdInt); | |
| 3767 | ||
| 3768 | for (int i = 0; i < CIntTypeCount; i += 1) { | |
| 3769 | if (int_type == g->builtin_types.entry_c_int[i]) { | |
| 3770 | return true; | |
| 3771 | } | |
| 3772 | } | |
| 3773 | return false; | |
| 3774 | } | |
| 3775 | ||
| 3776 | 3703 | static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 3777 | 3704 | AstNode *node, const char *err_format, bool is_max) |
| 3778 | 3705 | { |
| ... | ... | @@ -3781,67 +3708,15 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor |
| 3781 | 3708 | |
| 3782 | 3709 | AstNode *type_node = node->data.fn_call_expr.params.at(0); |
| 3783 | 3710 | TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node); |
| 3711 | ||
| 3784 | 3712 | if (type_entry->id == TypeTableEntryIdInvalid) { |
| 3785 | 3713 | return g->builtin_types.entry_invalid; |
| 3786 | } else if (type_entry->id == TypeTableEntryIdInt) { | |
| 3787 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 3788 | const_val->ok = true; | |
| 3789 | const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry); | |
| 3790 | if (is_max) { | |
| 3791 | if (type_entry->data.integral.is_signed) { | |
| 3792 | int64_t val; | |
| 3793 | if (type_entry->data.integral.bit_count == 64) { | |
| 3794 | val = INT64_MAX; | |
| 3795 | } else if (type_entry->data.integral.bit_count == 32) { | |
| 3796 | val = INT32_MAX; | |
| 3797 | } else if (type_entry->data.integral.bit_count == 16) { | |
| 3798 | val = INT16_MAX; | |
| 3799 | } else if (type_entry->data.integral.bit_count == 8) { | |
| 3800 | val = INT8_MAX; | |
| 3801 | } else { | |
| 3802 | zig_unreachable(); | |
| 3803 | } | |
| 3804 | bignum_init_signed(&const_val->data.x_bignum, val); | |
| 3805 | } else { | |
| 3806 | uint64_t val; | |
| 3807 | if (type_entry->data.integral.bit_count == 64) { | |
| 3808 | val = UINT64_MAX; | |
| 3809 | } else if (type_entry->data.integral.bit_count == 32) { | |
| 3810 | val = UINT32_MAX; | |
| 3811 | } else if (type_entry->data.integral.bit_count == 16) { | |
| 3812 | val = UINT16_MAX; | |
| 3813 | } else if (type_entry->data.integral.bit_count == 8) { | |
| 3814 | val = UINT8_MAX; | |
| 3815 | } else { | |
| 3816 | zig_unreachable(); | |
| 3817 | } | |
| 3818 | bignum_init_unsigned(&const_val->data.x_bignum, val); | |
| 3819 | } | |
| 3820 | } else { | |
| 3821 | if (type_entry->data.integral.is_signed) { | |
| 3822 | int64_t val; | |
| 3823 | if (type_entry->data.integral.bit_count == 64) { | |
| 3824 | val = INT64_MIN; | |
| 3825 | } else if (type_entry->data.integral.bit_count == 32) { | |
| 3826 | val = INT32_MIN; | |
| 3827 | } else if (type_entry->data.integral.bit_count == 16) { | |
| 3828 | val = INT16_MIN; | |
| 3829 | } else if (type_entry->data.integral.bit_count == 8) { | |
| 3830 | val = INT8_MIN; | |
| 3831 | } else { | |
| 3832 | zig_unreachable(); | |
| 3833 | } | |
| 3834 | bignum_init_signed(&const_val->data.x_bignum, val); | |
| 3835 | } else { | |
| 3836 | bignum_init_unsigned(&const_val->data.x_bignum, 0); | |
| 3837 | } | |
| 3838 | } | |
| 3839 | return type_entry; | |
| 3840 | } else if (type_entry->id == TypeTableEntryIdFloat) { | |
| 3841 | zig_panic("TODO analyze_min_max_value float"); | |
| 3714 | } else if (type_entry->id == TypeTableEntryIdInt || | |
| 3715 | type_entry->id == TypeTableEntryIdFloat || | |
| 3716 | type_entry->id == TypeTableEntryIdBool) | |
| 3717 | { | |
| 3718 | eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max); | |
| 3842 | 3719 | return type_entry; |
| 3843 | } else if (type_entry->id == TypeTableEntryIdBool) { | |
| 3844 | return resolve_expr_const_val_as_bool(g, node, is_max, false); | |
| 3845 | 3720 | } else { |
| 3846 | 3721 | add_node_error(g, node, |
| 3847 | 3722 | buf_sprintf(err_format, buf_ptr(&type_entry->name))); |
| ... | ... | @@ -3849,92 +3724,19 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor |
| 3849 | 3724 | } |
| 3850 | 3725 | } |
| 3851 | 3726 | |
| 3852 | static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *expr_node) { | |
| 3853 | assert(node->type == NodeTypeFnCallExpr); | |
| 3854 | ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; | |
| 3855 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 3856 | if (!other_val->ok) { | |
| 3857 | return; | |
| 3858 | } | |
| 3859 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; | |
| 3860 | const_val->undef = other_val->undef; | |
| 3861 | ||
| 3862 | assert(other_val != const_val); | |
| 3863 | switch (node->data.fn_call_expr.cast_op) { | |
| 3864 | case CastOpNoCast: | |
| 3865 | zig_unreachable(); | |
| 3866 | case CastOpNoop: | |
| 3867 | case CastOpWidenOrShorten: | |
| 3868 | case CastOpPointerReinterpret: | |
| 3869 | *const_val = *other_val; | |
| 3870 | break; | |
| 3871 | case CastOpPtrToInt: | |
| 3872 | case CastOpIntToPtr: | |
| 3873 | // can't do it | |
| 3874 | break; | |
| 3875 | case CastOpToUnknownSizeArray: | |
| 3876 | { | |
| 3877 | TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; | |
| 3878 | assert(other_type->id == TypeTableEntryIdArray); | |
| 3879 | ||
| 3880 | ConstExprValue *all_fields = allocate<ConstExprValue>(2); | |
| 3881 | ConstExprValue *ptr_field = &all_fields[0]; | |
| 3882 | ConstExprValue *len_field = &all_fields[1]; | |
| 3883 | ||
| 3884 | const_val->data.x_struct.fields = allocate<ConstExprValue*>(2); | |
| 3885 | const_val->data.x_struct.fields[0] = ptr_field; | |
| 3886 | const_val->data.x_struct.fields[1] = len_field; | |
| 3887 | ||
| 3888 | ptr_field->ok = true; | |
| 3889 | ptr_field->data.x_ptr.ptr = other_val->data.x_array.fields; | |
| 3890 | ptr_field->data.x_ptr.len = other_type->data.array.len; | |
| 3891 | ||
| 3892 | len_field->ok = true; | |
| 3893 | bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len); | |
| 3894 | ||
| 3895 | const_val->ok = true; | |
| 3896 | break; | |
| 3897 | } | |
| 3898 | case CastOpMaybeWrap: | |
| 3899 | const_val->data.x_maybe = other_val; | |
| 3900 | const_val->ok = true; | |
| 3901 | break; | |
| 3902 | case CastOpErrorWrap: | |
| 3903 | const_val->data.x_err.err = nullptr; | |
| 3904 | const_val->data.x_err.payload = other_val; | |
| 3905 | const_val->ok = true; | |
| 3906 | break; | |
| 3907 | case CastOpPureErrorWrap: | |
| 3908 | const_val->data.x_err.err = other_val->data.x_err.err; | |
| 3909 | const_val->ok = true; | |
| 3910 | break; | |
| 3911 | case CastOpErrToInt: | |
| 3912 | { | |
| 3913 | uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; | |
| 3914 | bignum_init_unsigned(&const_val->data.x_bignum, value); | |
| 3915 | const_val->ok = true; | |
| 3916 | break; | |
| 3917 | } | |
| 3918 | case CastOpIntToFloat: | |
| 3919 | bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); | |
| 3920 | const_val->ok = true; | |
| 3921 | break; | |
| 3922 | case CastOpFloatToInt: | |
| 3923 | bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum); | |
| 3924 | const_val->ok = true; | |
| 3925 | break; | |
| 3926 | case CastOpBoolToInt: | |
| 3927 | bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0); | |
| 3928 | const_val->ok = true; | |
| 3929 | break; | |
| 3930 | } | |
| 3931 | } | |
| 3932 | ||
| 3933 | 3727 | static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node, |
| 3934 | 3728 | AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca) |
| 3935 | 3729 | { |
| 3936 | 3730 | node->data.fn_call_expr.cast_op = op; |
| 3937 | eval_const_expr_implicit_cast(g, node, expr_node); | |
| 3731 | ||
| 3732 | ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val; | |
| 3733 | TypeTableEntry *other_type = get_resolved_expr(expr_node)->type_entry; | |
| 3734 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 3735 | if (other_val->ok) { | |
| 3736 | eval_const_expr_implicit_cast(node->data.fn_call_expr.cast_op, other_val, other_type, | |
| 3737 | const_val, wanted_type); | |
| 3738 | } | |
| 3739 | ||
| 3938 | 3740 | if (need_alloca) { |
| 3939 | 3741 | if (context->fn_entry) { |
| 3940 | 3742 | context->fn_entry->cast_alloca_list.append(node); |
| ... | ... | @@ -4630,13 +4432,15 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4630 | 4432 | case BuiltinFnIdErrName: |
| 4631 | 4433 | return analyze_err_name(g, import, context, node); |
| 4632 | 4434 | case BuiltinFnIdBreakpoint: |
| 4435 | mark_impure_fn(context); | |
| 4633 | 4436 | return g->builtin_types.entry_void; |
| 4634 | 4437 | } |
| 4635 | 4438 | zig_unreachable(); |
| 4636 | 4439 | } |
| 4637 | 4440 | |
| 4638 | 4441 | static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4639 | TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, TypeTableEntry *struct_type) | |
| 4442 | TypeTableEntry *expected_type, AstNode *node, TypeTableEntry *fn_type, | |
| 4443 | AstNode *struct_node) | |
| 4640 | 4444 | { |
| 4641 | 4445 | assert(node->type == NodeTypeFnCallExpr); |
| 4642 | 4446 | |
| ... | ... | @@ -4648,31 +4452,49 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 4648 | 4452 | int src_param_count = fn_type->data.fn.fn_type_id.param_count; |
| 4649 | 4453 | int actual_param_count = node->data.fn_call_expr.params.length; |
| 4650 | 4454 | |
| 4651 | if (struct_type) { | |
| 4455 | if (struct_node) { | |
| 4652 | 4456 | actual_param_count += 1; |
| 4653 | 4457 | } |
| 4654 | 4458 | |
| 4459 | bool ok_invocation = true; | |
| 4460 | ||
| 4655 | 4461 | if (fn_type->data.fn.fn_type_id.is_var_args) { |
| 4656 | 4462 | if (actual_param_count < src_param_count) { |
| 4463 | ok_invocation = false; | |
| 4657 | 4464 | add_node_error(g, node, |
| 4658 | 4465 | buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count)); |
| 4659 | 4466 | } |
| 4660 | 4467 | } else if (src_param_count != actual_param_count) { |
| 4468 | ok_invocation = false; | |
| 4661 | 4469 | add_node_error(g, node, |
| 4662 | 4470 | buf_sprintf("expected %d arguments, got %d", src_param_count, actual_param_count)); |
| 4663 | 4471 | } |
| 4664 | 4472 | |
| 4473 | bool all_args_const_expr = true; | |
| 4474 | ||
| 4475 | if (struct_node) { | |
| 4476 | ConstExprValue *struct_const_val = &get_resolved_expr(struct_node)->const_val; | |
| 4477 | if (!struct_const_val->ok) { | |
| 4478 | all_args_const_expr = false; | |
| 4479 | } | |
| 4480 | } | |
| 4481 | ||
| 4665 | 4482 | // analyze each parameter. in the case of a method, we already analyzed the |
| 4666 | 4483 | // first parameter in order to figure out which struct we were calling a method on. |
| 4667 | 4484 | for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { |
| 4668 | AstNode *child = node->data.fn_call_expr.params.at(i); | |
| 4485 | AstNode **child = &node->data.fn_call_expr.params.at(i); | |
| 4669 | 4486 | // determine the expected type for each parameter |
| 4670 | 4487 | TypeTableEntry *expected_param_type = nullptr; |
| 4671 | int fn_proto_i = i + (struct_type ? 1 : 0); | |
| 4488 | int fn_proto_i = i + (struct_node ? 1 : 0); | |
| 4672 | 4489 | if (fn_proto_i < src_param_count) { |
| 4673 | 4490 | expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type; |
| 4674 | 4491 | } |
| 4675 | analyze_expression(g, import, context, expected_param_type, child); | |
| 4492 | analyze_expression(g, import, context, expected_param_type, *child); | |
| 4493 | ||
| 4494 | ConstExprValue *const_arg_val = &get_resolved_expr(*child)->const_val; | |
| 4495 | if (!const_arg_val->ok) { | |
| 4496 | all_args_const_expr = false; | |
| 4497 | } | |
| 4676 | 4498 | } |
| 4677 | 4499 | |
| 4678 | 4500 | TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; |
| ... | ... | @@ -4681,6 +4503,27 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 4681 | 4503 | return return_type; |
| 4682 | 4504 | } |
| 4683 | 4505 | |
| 4506 | FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; | |
| 4507 | if (ok_invocation && fn_table_entry && fn_table_entry->is_pure) { | |
| 4508 | if (fn_table_entry->anal_state == FnAnalStateReady) { | |
| 4509 | analyze_fn_body(g, fn_table_entry); | |
| 4510 | } | |
| 4511 | if (all_args_const_expr) { | |
| 4512 | if (fn_table_entry->is_pure && fn_table_entry->anal_state == FnAnalStateComplete) { | |
| 4513 | ConstExprValue *result_val = &get_resolved_expr(node)->const_val; | |
| 4514 | if (eval_fn(g, node, fn_table_entry, result_val, 1000, struct_node)) { | |
| 4515 | // function evaluation generated an error | |
| 4516 | return g->builtin_types.entry_invalid; | |
| 4517 | } | |
| 4518 | return return_type; | |
| 4519 | } | |
| 4520 | } | |
| 4521 | } | |
| 4522 | if (!ok_invocation || !fn_table_entry || !fn_table_entry->is_pure) { | |
| 4523 | // calling an impure fn is impure | |
| 4524 | mark_impure_fn(context); | |
| 4525 | } | |
| 4526 | ||
| 4684 | 4527 | if (handle_is_ptr(return_type)) { |
| 4685 | 4528 | context->fn_entry->cast_alloca_list.append(node); |
| 4686 | 4529 | } |
| ... | ... | @@ -4689,13 +4532,13 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, |
| 4689 | 4532 | } |
| 4690 | 4533 | |
| 4691 | 4534 | static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 4692 | TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, TypeTableEntry *struct_type) | |
| 4535 | TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, AstNode *struct_node) | |
| 4693 | 4536 | { |
| 4694 | 4537 | assert(node->type == NodeTypeFnCallExpr); |
| 4695 | 4538 | |
| 4696 | 4539 | node->data.fn_call_expr.fn_entry = fn_table_entry; |
| 4697 | 4540 | |
| 4698 | return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_type); | |
| 4541 | return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_node); | |
| 4699 | 4542 | } |
| 4700 | 4543 | |
| 4701 | 4544 | static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, |
| ... | ... | @@ -4721,7 +4564,7 @@ static TypeTableEntry *analyze_generic_fn_call(CodeGen *g, ImportTableEntry *imp |
| 4721 | 4564 | generic_fn_type_id->generic_param_count = actual_param_count; |
| 4722 | 4565 | generic_fn_type_id->generic_params = allocate<GenericParamValue>(actual_param_count); |
| 4723 | 4566 | |
| 4724 | BlockContext *child_context = import->block_context; | |
| 4567 | BlockContext *child_context = decl_node->owner->block_context; | |
| 4725 | 4568 | for (int i = 0; i < actual_param_count; i += 1) { |
| 4726 | 4569 | AstNode *generic_param_decl_node = decl_node->data.fn_proto.generic_params.at(i); |
| 4727 | 4570 | assert(generic_param_decl_node->type == NodeTypeParamDecl); |
| ... | ... | @@ -4861,17 +4704,17 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 4861 | 4704 | return analyze_cast_expr(g, import, context, node); |
| 4862 | 4705 | } |
| 4863 | 4706 | } else if (invoke_type_entry->id == TypeTableEntryIdFn) { |
| 4864 | TypeTableEntry *bare_struct_type; | |
| 4707 | AstNode *struct_node; | |
| 4865 | 4708 | if (fn_ref_expr->type == NodeTypeFieldAccessExpr && |
| 4866 | 4709 | fn_ref_expr->data.field_access_expr.is_member_fn) |
| 4867 | 4710 | { |
| 4868 | bare_struct_type = fn_ref_expr->data.field_access_expr.bare_struct_type; | |
| 4711 | struct_node = fn_ref_expr->data.field_access_expr.struct_expr; | |
| 4869 | 4712 | } else { |
| 4870 | bare_struct_type = nullptr; | |
| 4713 | struct_node = nullptr; | |
| 4871 | 4714 | } |
| 4872 | 4715 | |
| 4873 | 4716 | return analyze_fn_call_raw(g, import, context, expected_type, node, |
| 4874 | const_val->data.x_fn, bare_struct_type); | |
| 4717 | const_val->data.x_fn, struct_node); | |
| 4875 | 4718 | } else if (invoke_type_entry->id == TypeTableEntryIdGenericFn) { |
| 4876 | 4719 | return analyze_generic_fn_call(g, import, context, expected_type, node, const_val->data.x_type); |
| 4877 | 4720 | } else { |
| ... | ... | @@ -5421,6 +5264,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, |
| 5421 | 5264 | static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 5422 | 5265 | TypeTableEntry *expected_type, AstNode *node) |
| 5423 | 5266 | { |
| 5267 | mark_impure_fn(context); | |
| 5268 | ||
| 5424 | 5269 | node->data.asm_expr.return_count = 0; |
| 5425 | 5270 | TypeTableEntry *return_type = g->builtin_types.entry_void; |
| 5426 | 5271 | for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { |
| ... | ... | @@ -5641,8 +5486,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 5641 | 5486 | if (fn_proto_node->data.fn_proto.skip) { |
| 5642 | 5487 | // we detected an error with this function definition which prevents us |
| 5643 | 5488 | // from further analyzing it. |
| 5489 | fn_table_entry->anal_state = FnAnalStateSkipped; | |
| 5644 | 5490 | return; |
| 5645 | 5491 | } |
| 5492 | fn_table_entry->anal_state = FnAnalStateProbing; | |
| 5646 | 5493 | |
| 5647 | 5494 | BlockContext *context = node->data.fn_def.block_context; |
| 5648 | 5495 | |
| ... | ... | @@ -5676,6 +5523,10 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 5676 | 5523 | param_decl_node->data.param_decl.variable = var; |
| 5677 | 5524 | |
| 5678 | 5525 | var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; |
| 5526 | ||
| 5527 | if (!type->deep_const) { | |
| 5528 | fn_table_entry->is_pure = false; | |
| 5529 | } | |
| 5679 | 5530 | } |
| 5680 | 5531 | |
| 5681 | 5532 | TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type; |
| ... | ... | @@ -5697,6 +5548,8 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 5697 | 5548 | buf_ptr(&label->decl_node->data.label.name))); |
| 5698 | 5549 | } |
| 5699 | 5550 | } |
| 5551 | ||
| 5552 | fn_table_entry->anal_state = FnAnalStateComplete; | |
| 5700 | 5553 | } |
| 5701 | 5554 | |
| 5702 | 5555 | static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, |
| ... | ... | @@ -6005,7 +5858,9 @@ void semantic_analyze(CodeGen *g) { |
| 6005 | 5858 | |
| 6006 | 5859 | for (int i = 0; i < g->fn_defs.length; i += 1) { |
| 6007 | 5860 | FnTableEntry *fn_entry = g->fn_defs.at(i); |
| 6008 | analyze_fn_body(g, fn_entry); | |
| 5861 | if (fn_entry->anal_state == FnAnalStateReady) { | |
| 5862 | analyze_fn_body(g, fn_entry); | |
| 5863 | } | |
| 6009 | 5864 | } |
| 6010 | 5865 | } |
| 6011 | 5866 |
src/ast_render.cpp+6-2| ... | ... | @@ -326,9 +326,9 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 326 | 326 | AstNode *statement = node->data.block.statements.at(i); |
| 327 | 327 | print_indent(ar); |
| 328 | 328 | render_node(ar, statement); |
| 329 | fprintf(ar->f, ";\n"); | |
| 329 | 330 | } |
| 330 | 331 | ar->indent -= ar->indent_size; |
| 331 | fprintf(ar->f, "\n"); | |
| 332 | 332 | print_indent(ar); |
| 333 | 333 | fprintf(ar->f, "}"); |
| 334 | 334 | break; |
| ... | ... | @@ -438,7 +438,11 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 438 | 438 | fprintf(ar->f, ")"); |
| 439 | 439 | break; |
| 440 | 440 | case NodeTypeArrayAccessExpr: |
| 441 | zig_panic("TODO"); | |
| 441 | render_node(ar, node->data.array_access_expr.array_ref_expr); | |
| 442 | fprintf(ar->f, "["); | |
| 443 | render_node(ar, node->data.array_access_expr.subscript); | |
| 444 | fprintf(ar->f, "]"); | |
| 445 | break; | |
| 442 | 446 | case NodeTypeSliceExpr: |
| 443 | 447 | zig_panic("TODO"); |
| 444 | 448 | case NodeTypeFieldAccessExpr: |
src/bignum.cpp+5| ... | ... | @@ -71,6 +71,11 @@ bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) { |
| 71 | 71 | } |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | void bignum_truncate(BigNum *bn, int bit_count) { | |
| 75 | assert(bn->kind == BigNumKindInt); | |
| 76 | bn->data.x_uint &= (1LL << bit_count) - 1; | |
| 77 | } | |
| 78 | ||
| 74 | 79 | uint64_t bignum_to_twos_complement(BigNum *bn) { |
| 75 | 80 | assert(bn->kind == BigNumKindInt); |
| 76 | 81 |
src/bignum.hpp+2| ... | ... | @@ -47,6 +47,8 @@ void bignum_negate(BigNum *dest, BigNum *op); |
| 47 | 47 | void bignum_cast_to_float(BigNum *dest, BigNum *op); |
| 48 | 48 | void bignum_cast_to_int(BigNum *dest, BigNum *op); |
| 49 | 49 | |
| 50 | void bignum_truncate(BigNum *dest, int bit_count); | |
| 51 | ||
| 50 | 52 | // returns the result of the comparison |
| 51 | 53 | bool bignum_cmp_eq(BigNum *op1, BigNum *op2); |
| 52 | 54 | bool bignum_cmp_neq(BigNum *op1, BigNum *op2); |
src/codegen.cpp+24-7| ... | ... | @@ -1074,15 +1074,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva |
| 1074 | 1074 | |
| 1075 | 1075 | AstNode *struct_expr = node->data.field_access_expr.struct_expr; |
| 1076 | 1076 | TypeTableEntry *struct_type = get_expr_type(struct_expr); |
| 1077 | Buf *name = &node->data.field_access_expr.field_name; | |
| 1078 | 1077 | |
| 1079 | 1078 | if (struct_type->id == TypeTableEntryIdArray) { |
| 1080 | if (buf_eql_str(name, "len")) { | |
| 1081 | return LLVMConstInt(g->builtin_types.entry_isize->type_ref, | |
| 1082 | struct_type->data.array.len, false); | |
| 1083 | } else { | |
| 1084 | zig_panic("gen_field_access_expr bad array field"); | |
| 1085 | } | |
| 1079 | Buf *name = &node->data.field_access_expr.field_name; | |
| 1080 | assert(buf_eql_str(name, "len")); | |
| 1081 | return LLVMConstInt(g->builtin_types.entry_isize->type_ref, | |
| 1082 | struct_type->data.array.len, false); | |
| 1086 | 1083 | } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && |
| 1087 | 1084 | struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) |
| 1088 | 1085 | { |
| ... | ... | @@ -3036,6 +3033,7 @@ static void gen_const_globals(CodeGen *g) { |
| 3036 | 3033 | } else { |
| 3037 | 3034 | expr->const_llvm_val = gen_const_val(g, type_entry, const_val); |
| 3038 | 3035 | } |
| 3036 | assert(expr->const_llvm_val); | |
| 3039 | 3037 | } |
| 3040 | 3038 | } |
| 3041 | 3039 | |
| ... | ... | @@ -3466,23 +3464,27 @@ static void define_builtin_types(CodeGen *g) { |
| 3466 | 3464 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNamespace); |
| 3467 | 3465 | buf_init_from_str(&entry->name, "(namespace)"); |
| 3468 | 3466 | entry->zero_bits = true; |
| 3467 | entry->deep_const = true; | |
| 3469 | 3468 | g->builtin_types.entry_namespace = entry; |
| 3470 | 3469 | } |
| 3471 | 3470 | { |
| 3472 | 3471 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat); |
| 3473 | 3472 | buf_init_from_str(&entry->name, "(float literal)"); |
| 3474 | 3473 | entry->zero_bits = true; |
| 3474 | entry->deep_const = true; | |
| 3475 | 3475 | g->builtin_types.entry_num_lit_float = entry; |
| 3476 | 3476 | } |
| 3477 | 3477 | { |
| 3478 | 3478 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt); |
| 3479 | 3479 | buf_init_from_str(&entry->name, "(integer literal)"); |
| 3480 | 3480 | entry->zero_bits = true; |
| 3481 | entry->deep_const = true; | |
| 3481 | 3482 | g->builtin_types.entry_num_lit_int = entry; |
| 3482 | 3483 | } |
| 3483 | 3484 | { |
| 3484 | 3485 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUndefLit); |
| 3485 | 3486 | buf_init_from_str(&entry->name, "(undefined)"); |
| 3487 | entry->deep_const = true; | |
| 3486 | 3488 | g->builtin_types.entry_undef = entry; |
| 3487 | 3489 | } |
| 3488 | 3490 | |
| ... | ... | @@ -3492,6 +3494,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3492 | 3494 | for (;;) { |
| 3493 | 3495 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 3494 | 3496 | entry->type_ref = LLVMIntType(size_in_bits); |
| 3497 | entry->deep_const = true; | |
| 3495 | 3498 | |
| 3496 | 3499 | const char u_or_i = is_signed ? 'i' : 'u'; |
| 3497 | 3500 | buf_resize(&entry->name, 0); |
| ... | ... | @@ -3537,6 +3540,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3537 | 3540 | |
| 3538 | 3541 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 3539 | 3542 | entry->type_ref = LLVMIntType(size_in_bits); |
| 3543 | entry->deep_const = true; | |
| 3540 | 3544 | |
| 3541 | 3545 | buf_init_from_str(&entry->name, info->name); |
| 3542 | 3546 | |
| ... | ... | @@ -3556,6 +3560,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3556 | 3560 | { |
| 3557 | 3561 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); |
| 3558 | 3562 | entry->type_ref = LLVMInt1Type(); |
| 3563 | entry->deep_const = true; | |
| 3559 | 3564 | buf_init_from_str(&entry->name, "bool"); |
| 3560 | 3565 | uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); |
| 3561 | 3566 | uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref); |
| ... | ... | @@ -3568,6 +3573,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3568 | 3573 | } |
| 3569 | 3574 | { |
| 3570 | 3575 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 3576 | entry->deep_const = true; | |
| 3571 | 3577 | entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); |
| 3572 | 3578 | buf_init_from_str(&entry->name, "isize"); |
| 3573 | 3579 | entry->data.integral.is_signed = true; |
| ... | ... | @@ -3584,6 +3590,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3584 | 3590 | } |
| 3585 | 3591 | { |
| 3586 | 3592 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 3593 | entry->deep_const = true; | |
| 3587 | 3594 | entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); |
| 3588 | 3595 | buf_init_from_str(&entry->name, "usize"); |
| 3589 | 3596 | entry->data.integral.is_signed = false; |
| ... | ... | @@ -3600,6 +3607,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3600 | 3607 | } |
| 3601 | 3608 | { |
| 3602 | 3609 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); |
| 3610 | entry->deep_const = true; | |
| 3603 | 3611 | entry->type_ref = LLVMFloatType(); |
| 3604 | 3612 | buf_init_from_str(&entry->name, "f32"); |
| 3605 | 3613 | entry->data.floating.bit_count = 32; |
| ... | ... | @@ -3615,6 +3623,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3615 | 3623 | } |
| 3616 | 3624 | { |
| 3617 | 3625 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); |
| 3626 | entry->deep_const = true; | |
| 3618 | 3627 | entry->type_ref = LLVMDoubleType(); |
| 3619 | 3628 | buf_init_from_str(&entry->name, "f64"); |
| 3620 | 3629 | entry->data.floating.bit_count = 64; |
| ... | ... | @@ -3630,6 +3639,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3630 | 3639 | } |
| 3631 | 3640 | { |
| 3632 | 3641 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); |
| 3642 | entry->deep_const = true; | |
| 3633 | 3643 | entry->type_ref = LLVMX86FP80Type(); |
| 3634 | 3644 | buf_init_from_str(&entry->name, "c_long_double"); |
| 3635 | 3645 | entry->data.floating.bit_count = 80; |
| ... | ... | @@ -3645,6 +3655,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3645 | 3655 | } |
| 3646 | 3656 | { |
| 3647 | 3657 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); |
| 3658 | entry->deep_const = true; | |
| 3648 | 3659 | entry->type_ref = LLVMVoidType(); |
| 3649 | 3660 | entry->zero_bits = true; |
| 3650 | 3661 | buf_init_from_str(&entry->name, "void"); |
| ... | ... | @@ -3657,6 +3668,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3657 | 3668 | } |
| 3658 | 3669 | { |
| 3659 | 3670 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable); |
| 3671 | entry->deep_const = true; | |
| 3660 | 3672 | entry->type_ref = LLVMVoidType(); |
| 3661 | 3673 | entry->zero_bits = true; |
| 3662 | 3674 | buf_init_from_str(&entry->name, "unreachable"); |
| ... | ... | @@ -3666,6 +3678,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3666 | 3678 | } |
| 3667 | 3679 | { |
| 3668 | 3680 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType); |
| 3681 | entry->deep_const = true; | |
| 3669 | 3682 | buf_init_from_str(&entry->name, "type"); |
| 3670 | 3683 | entry->zero_bits = true; |
| 3671 | 3684 | g->builtin_types.entry_type = entry; |
| ... | ... | @@ -3688,6 +3701,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3688 | 3701 | |
| 3689 | 3702 | { |
| 3690 | 3703 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError); |
| 3704 | entry->deep_const = true; | |
| 3691 | 3705 | buf_init_from_str(&entry->name, "error"); |
| 3692 | 3706 | |
| 3693 | 3707 | // TODO allow overriding this type and keep track of max value and emit an |
| ... | ... | @@ -3703,6 +3717,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3703 | 3717 | |
| 3704 | 3718 | { |
| 3705 | 3719 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 3720 | entry->deep_const = true; | |
| 3706 | 3721 | entry->zero_bits = true; // only allowed at compile time |
| 3707 | 3722 | buf_init_from_str(&entry->name, "@OS"); |
| 3708 | 3723 | uint32_t field_count = target_os_count(); |
| ... | ... | @@ -3728,6 +3743,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3728 | 3743 | |
| 3729 | 3744 | { |
| 3730 | 3745 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 3746 | entry->deep_const = true; | |
| 3731 | 3747 | entry->zero_bits = true; // only allowed at compile time |
| 3732 | 3748 | buf_init_from_str(&entry->name, "@Arch"); |
| 3733 | 3749 | uint32_t field_count = target_arch_count(); |
| ... | ... | @@ -3759,6 +3775,7 @@ static void define_builtin_types(CodeGen *g) { |
| 3759 | 3775 | |
| 3760 | 3776 | { |
| 3761 | 3777 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); |
| 3778 | entry->deep_const = true; | |
| 3762 | 3779 | entry->zero_bits = true; // only allowed at compile time |
| 3763 | 3780 | buf_init_from_str(&entry->name, "@Environ"); |
| 3764 | 3781 | uint32_t field_count = target_environ_count(); |
src/error.cpp+2| ... | ... | @@ -12,6 +12,8 @@ const char *err_str(int err) { |
| 12 | 12 | case ErrorFileNotFound: return "file not found"; |
| 13 | 13 | case ErrorFileSystem: return "file system error"; |
| 14 | 14 | case ErrorFileTooBig: return "file too big"; |
| 15 | case ErrorDivByZero: return "division by zero"; | |
| 16 | case ErrorOverflow: return "overflow"; | |
| 15 | 17 | } |
| 16 | 18 | return "(invalid error)"; |
| 17 | 19 | } |
src/error.hpp+2| ... | ... | @@ -19,6 +19,8 @@ enum Error { |
| 19 | 19 | ErrorFileNotFound, |
| 20 | 20 | ErrorFileSystem, |
| 21 | 21 | ErrorFileTooBig, |
| 22 | ErrorDivByZero, | |
| 23 | ErrorOverflow | |
| 22 | 24 | }; |
| 23 | 25 | |
| 24 | 26 | const char *err_str(int err); |
src/eval.cpp created+1191| ... | ... | @@ -0,0 +1,1191 @@ |
| 1 | #include "eval.hpp" | |
| 2 | #include "analyze.hpp" | |
| 3 | #include "error.hpp" | |
| 4 | ||
| 5 | static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val); | |
| 6 | ||
| 7 | bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry) { | |
| 8 | switch (type_entry->id) { | |
| 9 | case TypeTableEntryIdEnum: | |
| 10 | { | |
| 11 | ConstEnumValue *enum1 = &a->data.x_enum; | |
| 12 | ConstEnumValue *enum2 = &b->data.x_enum; | |
| 13 | if (enum1->tag == enum2->tag) { | |
| 14 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum1->tag]; | |
| 15 | if (type_has_bits(enum_field->type_entry)) { | |
| 16 | zig_panic("TODO const expr analyze enum special value for equality"); | |
| 17 | } else { | |
| 18 | return true; | |
| 19 | } | |
| 20 | } | |
| 21 | return false; | |
| 22 | } | |
| 23 | case TypeTableEntryIdMetaType: | |
| 24 | return a->data.x_type == b->data.x_type; | |
| 25 | case TypeTableEntryIdVoid: | |
| 26 | return true; | |
| 27 | case TypeTableEntryIdPureError: | |
| 28 | return a->data.x_err.err == b->data.x_err.err; | |
| 29 | case TypeTableEntryIdFn: | |
| 30 | return a->data.x_fn == b->data.x_fn; | |
| 31 | case TypeTableEntryIdBool: | |
| 32 | return a->data.x_bool == b->data.x_bool; | |
| 33 | case TypeTableEntryIdInt: | |
| 34 | case TypeTableEntryIdFloat: | |
| 35 | case TypeTableEntryIdNumLitFloat: | |
| 36 | case TypeTableEntryIdNumLitInt: | |
| 37 | return bignum_cmp_eq(&a->data.x_bignum, &b->data.x_bignum); | |
| 38 | case TypeTableEntryIdPointer: | |
| 39 | zig_panic("TODO"); | |
| 40 | case TypeTableEntryIdArray: | |
| 41 | zig_panic("TODO"); | |
| 42 | case TypeTableEntryIdStruct: | |
| 43 | zig_panic("TODO"); | |
| 44 | case TypeTableEntryIdUndefLit: | |
| 45 | zig_panic("TODO"); | |
| 46 | case TypeTableEntryIdMaybe: | |
| 47 | zig_panic("TODO"); | |
| 48 | case TypeTableEntryIdErrorUnion: | |
| 49 | zig_panic("TODO"); | |
| 50 | case TypeTableEntryIdTypeDecl: | |
| 51 | zig_panic("TODO"); | |
| 52 | case TypeTableEntryIdNamespace: | |
| 53 | zig_panic("TODO"); | |
| 54 | case TypeTableEntryIdGenericFn: | |
| 55 | case TypeTableEntryIdInvalid: | |
| 56 | case TypeTableEntryIdUnreachable: | |
| 57 | zig_unreachable(); | |
| 58 | } | |
| 59 | zig_unreachable(); | |
| 60 | } | |
| 61 | ||
| 62 | ||
| 63 | static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out); | |
| 64 | ||
| 65 | static bool eval_block(EvalFn *ef, AstNode *node, ConstExprValue *out) { | |
| 66 | assert(node->type == NodeTypeBlock); | |
| 67 | ||
| 68 | EvalScope *my_scope = allocate<EvalScope>(1); | |
| 69 | my_scope->block_context = node->block_context; | |
| 70 | ef->scope_stack.append(my_scope); | |
| 71 | ||
| 72 | for (int i = 0; i < node->data.block.statements.length; i += 1) { | |
| 73 | AstNode *child = node->data.block.statements.at(i); | |
| 74 | memset(out, 0, sizeof(ConstExprValue)); | |
| 75 | if (eval_expr(ef, child, out)) return true; | |
| 76 | } | |
| 77 | ||
| 78 | ef->scope_stack.pop(); | |
| 79 | ||
| 80 | return false; | |
| 81 | } | |
| 82 | ||
| 83 | static bool eval_return(EvalFn *ef, AstNode *node, ConstExprValue *out) { | |
| 84 | assert(node->type == NodeTypeReturnExpr); | |
| 85 | ||
| 86 | eval_expr(ef, node->data.return_expr.expr, ef->return_expr); | |
| 87 | return true; | |
| 88 | } | |
| 89 | ||
| 90 | static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) { | |
| 91 | if (bin_op == BinOpTypeBoolOr) { | |
| 92 | return a || b; | |
| 93 | } else if (bin_op == BinOpTypeBoolAnd) { | |
| 94 | return a && b; | |
| 95 | } else { | |
| 96 | zig_unreachable(); | |
| 97 | } | |
| 98 | } | |
| 99 | ||
| 100 | static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, | |
| 101 | ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *)) | |
| 102 | { | |
| 103 | bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); | |
| 104 | if (overflow) { | |
| 105 | return ErrorOverflow; | |
| 106 | } | |
| 107 | ||
| 108 | out_val->ok = true; | |
| 109 | out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; | |
| 110 | return 0; | |
| 111 | } | |
| 112 | ||
| 113 | int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, | |
| 114 | BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val) | |
| 115 | { | |
| 116 | assert(op1_val->ok); | |
| 117 | assert(op2_val->ok); | |
| 118 | ||
| 119 | switch (bin_op) { | |
| 120 | case BinOpTypeAssign: | |
| 121 | case BinOpTypeAssignTimes: | |
| 122 | case BinOpTypeAssignDiv: | |
| 123 | case BinOpTypeAssignMod: | |
| 124 | case BinOpTypeAssignPlus: | |
| 125 | case BinOpTypeAssignMinus: | |
| 126 | case BinOpTypeAssignBitShiftLeft: | |
| 127 | case BinOpTypeAssignBitShiftRight: | |
| 128 | case BinOpTypeAssignBitAnd: | |
| 129 | case BinOpTypeAssignBitXor: | |
| 130 | case BinOpTypeAssignBitOr: | |
| 131 | case BinOpTypeAssignBoolAnd: | |
| 132 | case BinOpTypeAssignBoolOr: | |
| 133 | out_val->ok = true; | |
| 134 | return 0; | |
| 135 | case BinOpTypeBoolOr: | |
| 136 | case BinOpTypeBoolAnd: | |
| 137 | assert(op1_type->id == TypeTableEntryIdBool); | |
| 138 | assert(op2_type->id == TypeTableEntryIdBool); | |
| 139 | out_val->data.x_bool = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op, op2_val->data.x_bool); | |
| 140 | out_val->ok = true; | |
| 141 | out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; | |
| 142 | return 0; | |
| 143 | case BinOpTypeCmpEq: | |
| 144 | case BinOpTypeCmpNotEq: | |
| 145 | case BinOpTypeCmpLessThan: | |
| 146 | case BinOpTypeCmpGreaterThan: | |
| 147 | case BinOpTypeCmpLessOrEq: | |
| 148 | case BinOpTypeCmpGreaterOrEq: | |
| 149 | { | |
| 150 | bool type_can_gt_lt_cmp = (op1_type->id == TypeTableEntryIdNumLitFloat || | |
| 151 | op1_type->id == TypeTableEntryIdNumLitInt || | |
| 152 | op1_type->id == TypeTableEntryIdFloat || | |
| 153 | op1_type->id == TypeTableEntryIdInt); | |
| 154 | bool answer; | |
| 155 | if (type_can_gt_lt_cmp) { | |
| 156 | bool (*bignum_cmp)(BigNum *, BigNum *); | |
| 157 | if (bin_op == BinOpTypeCmpEq) { | |
| 158 | bignum_cmp = bignum_cmp_eq; | |
| 159 | } else if (bin_op == BinOpTypeCmpNotEq) { | |
| 160 | bignum_cmp = bignum_cmp_neq; | |
| 161 | } else if (bin_op == BinOpTypeCmpLessThan) { | |
| 162 | bignum_cmp = bignum_cmp_lt; | |
| 163 | } else if (bin_op == BinOpTypeCmpGreaterThan) { | |
| 164 | bignum_cmp = bignum_cmp_gt; | |
| 165 | } else if (bin_op == BinOpTypeCmpLessOrEq) { | |
| 166 | bignum_cmp = bignum_cmp_lte; | |
| 167 | } else if (bin_op == BinOpTypeCmpGreaterOrEq) { | |
| 168 | bignum_cmp = bignum_cmp_gte; | |
| 169 | } else { | |
| 170 | zig_unreachable(); | |
| 171 | } | |
| 172 | ||
| 173 | answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum); | |
| 174 | } else { | |
| 175 | bool are_equal = const_values_equal(op1_val, op2_val, op1_type); | |
| 176 | if (bin_op == BinOpTypeCmpEq) { | |
| 177 | answer = are_equal; | |
| 178 | } else if (bin_op == BinOpTypeCmpNotEq) { | |
| 179 | answer = !are_equal; | |
| 180 | } else { | |
| 181 | zig_unreachable(); | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | out_val->depends_on_compile_var = | |
| 186 | op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; | |
| 187 | out_val->data.x_bool = answer; | |
| 188 | out_val->ok = true; | |
| 189 | return 0; | |
| 190 | } | |
| 191 | case BinOpTypeAdd: | |
| 192 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add); | |
| 193 | case BinOpTypeBinOr: | |
| 194 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_or); | |
| 195 | case BinOpTypeBinXor: | |
| 196 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_xor); | |
| 197 | case BinOpTypeBinAnd: | |
| 198 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_and); | |
| 199 | case BinOpTypeBitShiftLeft: | |
| 200 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl); | |
| 201 | case BinOpTypeBitShiftRight: | |
| 202 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shr); | |
| 203 | case BinOpTypeSub: | |
| 204 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub); | |
| 205 | case BinOpTypeMult: | |
| 206 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul); | |
| 207 | case BinOpTypeDiv: | |
| 208 | { | |
| 209 | bool is_int = false; | |
| 210 | bool is_float = false; | |
| 211 | if (op1_type->id == TypeTableEntryIdInt || | |
| 212 | op1_type->id == TypeTableEntryIdNumLitInt) | |
| 213 | { | |
| 214 | is_int = true; | |
| 215 | } else if (op1_type->id == TypeTableEntryIdFloat || | |
| 216 | op1_type->id == TypeTableEntryIdNumLitFloat) | |
| 217 | { | |
| 218 | is_float = true; | |
| 219 | } | |
| 220 | if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || | |
| 221 | (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) | |
| 222 | { | |
| 223 | return ErrorDivByZero; | |
| 224 | } else { | |
| 225 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div); | |
| 226 | } | |
| 227 | } | |
| 228 | case BinOpTypeMod: | |
| 229 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod); | |
| 230 | case BinOpTypeUnwrapMaybe: | |
| 231 | zig_panic("TODO"); | |
| 232 | case BinOpTypeStrCat: | |
| 233 | zig_panic("TODO"); | |
| 234 | case BinOpTypeInvalid: | |
| 235 | zig_unreachable(); | |
| 236 | } | |
| 237 | zig_unreachable(); | |
| 238 | } | |
| 239 | ||
| 240 | static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 241 | assert(node->type == NodeTypeBinOpExpr); | |
| 242 | ||
| 243 | AstNode *op1 = node->data.bin_op_expr.op1; | |
| 244 | AstNode *op2 = node->data.bin_op_expr.op2; | |
| 245 | ||
| 246 | TypeTableEntry *op1_type = get_resolved_expr(op1)->type_entry; | |
| 247 | TypeTableEntry *op2_type = get_resolved_expr(op2)->type_entry; | |
| 248 | ||
| 249 | ConstExprValue op1_val = {0}; | |
| 250 | if (eval_expr(ef, op1, &op1_val)) return true; | |
| 251 | ||
| 252 | ConstExprValue op2_val = {0}; | |
| 253 | if (eval_expr(ef, op2, &op2_val)) return true; | |
| 254 | ||
| 255 | BinOpType bin_op = node->data.bin_op_expr.bin_op; | |
| 256 | ||
| 257 | int err; | |
| 258 | if ((err = eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val))) { | |
| 259 | ef->root->abort = true; | |
| 260 | if (err == ErrorDivByZero) { | |
| 261 | ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, | |
| 262 | buf_sprintf("function evaluation caused division by zero")); | |
| 263 | add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); | |
| 264 | add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here")); | |
| 265 | } else if (err == ErrorOverflow) { | |
| 266 | ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, | |
| 267 | buf_sprintf("function evaluation caused overflow")); | |
| 268 | add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); | |
| 269 | add_error_note(ef->root->codegen, msg, node, buf_sprintf("overflow occurred here")); | |
| 270 | } else { | |
| 271 | zig_unreachable(); | |
| 272 | } | |
| 273 | return true; | |
| 274 | } | |
| 275 | ||
| 276 | assert(out_val->ok); | |
| 277 | ||
| 278 | return false; | |
| 279 | } | |
| 280 | ||
| 281 | static EvalVar *find_var(EvalFn *ef, Buf *name) { | |
| 282 | int scope_index = ef->scope_stack.length - 1; | |
| 283 | while (scope_index >= 0) { | |
| 284 | EvalScope *scope = ef->scope_stack.at(scope_index); | |
| 285 | for (int var_i = 0; var_i < scope->vars.length; var_i += 1) { | |
| 286 | EvalVar *var = &scope->vars.at(var_i); | |
| 287 | if (buf_eql_buf(var->name, name)) { | |
| 288 | return var; | |
| 289 | } | |
| 290 | } | |
| 291 | scope_index -= 1; | |
| 292 | } | |
| 293 | ||
| 294 | return nullptr; | |
| 295 | } | |
| 296 | ||
| 297 | static bool eval_symbol_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 298 | assert(node->type == NodeTypeSymbol); | |
| 299 | ||
| 300 | Buf *name = &node->data.symbol_expr.symbol; | |
| 301 | EvalVar *var = find_var(ef, name); | |
| 302 | assert(var); | |
| 303 | ||
| 304 | *out_val = var->value; | |
| 305 | ||
| 306 | return false; | |
| 307 | } | |
| 308 | ||
| 309 | static TypeTableEntry *resolve_expr_type(AstNode *node) { | |
| 310 | Expr *expr = get_resolved_expr(node); | |
| 311 | TypeTableEntry *type_entry = expr->type_entry; | |
| 312 | assert(type_entry->id == TypeTableEntryIdMetaType); | |
| 313 | ConstExprValue *const_val = &expr->const_val; | |
| 314 | assert(const_val->ok); | |
| 315 | return const_val->data.x_type; | |
| 316 | } | |
| 317 | ||
| 318 | static bool eval_container_init_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 319 | assert(node->type == NodeTypeContainerInitExpr); | |
| 320 | ||
| 321 | AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; | |
| 322 | ContainerInitKind kind = container_init_expr->kind; | |
| 323 | TypeTableEntry *container_type = resolve_expr_type(container_init_expr->type); | |
| 324 | out_val->ok = true; | |
| 325 | ||
| 326 | if (container_type->id == TypeTableEntryIdStruct && | |
| 327 | !container_type->data.structure.is_unknown_size_array && | |
| 328 | kind == ContainerInitKindStruct) | |
| 329 | { | |
| 330 | int expr_field_count = container_init_expr->entries.length; | |
| 331 | int actual_field_count = container_type->data.structure.src_field_count; | |
| 332 | assert(expr_field_count == actual_field_count); | |
| 333 | ||
| 334 | out_val->data.x_struct.fields = allocate<ConstExprValue*>(actual_field_count); | |
| 335 | ||
| 336 | for (int i = 0; i < expr_field_count; i += 1) { | |
| 337 | AstNode *val_field_node = container_init_expr->entries.at(i); | |
| 338 | assert(val_field_node->type == NodeTypeStructValueField); | |
| 339 | ||
| 340 | TypeStructField *type_field = val_field_node->data.struct_val_field.type_struct_field; | |
| 341 | int field_index = type_field->src_index; | |
| 342 | ||
| 343 | ConstExprValue src_field_val = {0}; | |
| 344 | if (eval_expr(ef, val_field_node->data.struct_val_field.expr, &src_field_val)) return true; | |
| 345 | ||
| 346 | ConstExprValue *dest_field_val = allocate<ConstExprValue>(1); | |
| 347 | *dest_field_val = src_field_val; | |
| 348 | ||
| 349 | out_val->data.x_struct.fields[field_index] = dest_field_val; | |
| 350 | out_val->depends_on_compile_var = out_val->depends_on_compile_var || | |
| 351 | src_field_val.depends_on_compile_var; | |
| 352 | } | |
| 353 | } else if (container_type->id == TypeTableEntryIdVoid) { | |
| 354 | return false; | |
| 355 | } else if (container_type->id == TypeTableEntryIdUnreachable) { | |
| 356 | ef->root->abort = true; | |
| 357 | ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, | |
| 358 | buf_sprintf("function evaluation reached unreachable expression")); | |
| 359 | add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); | |
| 360 | add_error_note(ef->root->codegen, msg, node, buf_sprintf("unreachable expression here")); | |
| 361 | return true; | |
| 362 | } else if (container_type->id == TypeTableEntryIdStruct && | |
| 363 | container_type->data.structure.is_unknown_size_array && | |
| 364 | kind == ContainerInitKindArray) | |
| 365 | { | |
| 366 | ||
| 367 | int elem_count = container_init_expr->entries.length; | |
| 368 | ||
| 369 | out_val->ok = true; | |
| 370 | out_val->data.x_array.fields = allocate<ConstExprValue*>(elem_count); | |
| 371 | ||
| 372 | for (int i = 0; i < elem_count; i += 1) { | |
| 373 | AstNode *elem_node = container_init_expr->entries.at(i); | |
| 374 | ||
| 375 | ConstExprValue *elem_val = allocate<ConstExprValue>(1); | |
| 376 | if (eval_expr(ef, elem_node, elem_val)) return true; | |
| 377 | ||
| 378 | assert(elem_val->ok); | |
| 379 | ||
| 380 | out_val->data.x_array.fields[i] = elem_val; | |
| 381 | out_val->depends_on_compile_var = out_val->depends_on_compile_var || | |
| 382 | elem_val->depends_on_compile_var; | |
| 383 | } | |
| 384 | } else { | |
| 385 | zig_panic("TODO"); | |
| 386 | } | |
| 387 | ||
| 388 | ||
| 389 | return false; | |
| 390 | } | |
| 391 | ||
| 392 | static bool eval_if_bool_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 393 | assert(node->type == NodeTypeIfBoolExpr); | |
| 394 | ||
| 395 | ConstExprValue cond_val = {0}; | |
| 396 | if (eval_expr(ef, node->data.if_bool_expr.condition, &cond_val)) return true; | |
| 397 | ||
| 398 | AstNode *exec_node = cond_val.data.x_bool ? | |
| 399 | node->data.if_bool_expr.then_block : node->data.if_bool_expr.else_node; | |
| 400 | ||
| 401 | if (exec_node) { | |
| 402 | if (eval_expr(ef, exec_node, out_val)) return true; | |
| 403 | } | |
| 404 | out_val->ok = true; | |
| 405 | return false; | |
| 406 | } | |
| 407 | ||
| 408 | void eval_const_expr_implicit_cast(CastOp cast_op, | |
| 409 | ConstExprValue *other_val, TypeTableEntry *other_type, | |
| 410 | ConstExprValue *const_val, TypeTableEntry *new_type) | |
| 411 | { | |
| 412 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; | |
| 413 | const_val->undef = other_val->undef; | |
| 414 | ||
| 415 | assert(other_val != const_val); | |
| 416 | switch (cast_op) { | |
| 417 | case CastOpNoCast: | |
| 418 | zig_unreachable(); | |
| 419 | case CastOpNoop: | |
| 420 | case CastOpWidenOrShorten: | |
| 421 | *const_val = *other_val; | |
| 422 | break; | |
| 423 | case CastOpPointerReinterpret: | |
| 424 | if (other_type->id == TypeTableEntryIdPointer && | |
| 425 | new_type->id == TypeTableEntryIdPointer) | |
| 426 | { | |
| 427 | TypeTableEntry *other_child_type = other_type->data.pointer.child_type; | |
| 428 | TypeTableEntry *new_child_type = new_type->data.pointer.child_type; | |
| 429 | ||
| 430 | if ((other_child_type->id == TypeTableEntryIdInt || | |
| 431 | other_child_type->id == TypeTableEntryIdFloat) && | |
| 432 | (new_child_type->id == TypeTableEntryIdInt || | |
| 433 | new_child_type->id == TypeTableEntryIdFloat)) | |
| 434 | { | |
| 435 | ConstExprValue **ptr_val = allocate<ConstExprValue*>(1); | |
| 436 | *ptr_val = other_val->data.x_ptr.ptr[0]; | |
| 437 | const_val->data.x_ptr.ptr = ptr_val; | |
| 438 | const_val->data.x_ptr.len = 1; | |
| 439 | const_val->ok = true; | |
| 440 | const_val->undef = other_val->undef; | |
| 441 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; | |
| 442 | } else { | |
| 443 | zig_panic("TODO"); | |
| 444 | } | |
| 445 | } else if (other_type->id == TypeTableEntryIdMaybe && | |
| 446 | new_type->id == TypeTableEntryIdMaybe) | |
| 447 | { | |
| 448 | if (!other_val->data.x_maybe) { | |
| 449 | *const_val = *other_val; | |
| 450 | break; | |
| 451 | } | |
| 452 | ||
| 453 | TypeTableEntry *other_ptr_type = other_type->data.maybe.child_type; | |
| 454 | TypeTableEntry *new_ptr_type = new_type->data.maybe.child_type; | |
| 455 | ||
| 456 | if (other_ptr_type->id == TypeTableEntryIdPointer && | |
| 457 | new_ptr_type->id == TypeTableEntryIdPointer) | |
| 458 | { | |
| 459 | TypeTableEntry *other_child_type = other_ptr_type->data.pointer.child_type; | |
| 460 | TypeTableEntry *new_child_type = new_ptr_type->data.pointer.child_type; | |
| 461 | ||
| 462 | if ((other_child_type->id == TypeTableEntryIdInt || | |
| 463 | other_child_type->id == TypeTableEntryIdFloat) && | |
| 464 | (new_child_type->id == TypeTableEntryIdInt || | |
| 465 | new_child_type->id == TypeTableEntryIdFloat)) | |
| 466 | { | |
| 467 | ConstExprValue *ptr_parent = allocate<ConstExprValue>(1); | |
| 468 | ConstExprValue **ptr_val = allocate<ConstExprValue*>(1); | |
| 469 | *ptr_val = other_val->data.x_maybe->data.x_ptr.ptr[0]; | |
| 470 | ptr_parent->data.x_ptr.ptr = ptr_val; | |
| 471 | ptr_parent->data.x_ptr.len = 1; | |
| 472 | ptr_parent->ok = true; | |
| 473 | ||
| 474 | const_val->data.x_maybe = ptr_parent; | |
| 475 | const_val->ok = true; | |
| 476 | const_val->undef = other_val->undef; | |
| 477 | const_val->depends_on_compile_var = other_val->depends_on_compile_var; | |
| 478 | } else { | |
| 479 | zig_panic("TODO"); | |
| 480 | } | |
| 481 | } else { | |
| 482 | zig_panic("TODO"); | |
| 483 | } | |
| 484 | } | |
| 485 | break; | |
| 486 | case CastOpPtrToInt: | |
| 487 | case CastOpIntToPtr: | |
| 488 | // can't do it | |
| 489 | break; | |
| 490 | case CastOpToUnknownSizeArray: | |
| 491 | { | |
| 492 | assert(other_type->id == TypeTableEntryIdArray); | |
| 493 | ||
| 494 | ConstExprValue *all_fields = allocate<ConstExprValue>(2); | |
| 495 | ConstExprValue *ptr_field = &all_fields[0]; | |
| 496 | ConstExprValue *len_field = &all_fields[1]; | |
| 497 | ||
| 498 | const_val->data.x_struct.fields = allocate<ConstExprValue*>(2); | |
| 499 | const_val->data.x_struct.fields[0] = ptr_field; | |
| 500 | const_val->data.x_struct.fields[1] = len_field; | |
| 501 | ||
| 502 | ptr_field->ok = true; | |
| 503 | ptr_field->data.x_ptr.ptr = other_val->data.x_array.fields; | |
| 504 | ptr_field->data.x_ptr.len = other_type->data.array.len; | |
| 505 | ||
| 506 | len_field->ok = true; | |
| 507 | bignum_init_unsigned(&len_field->data.x_bignum, other_type->data.array.len); | |
| 508 | ||
| 509 | const_val->ok = true; | |
| 510 | break; | |
| 511 | } | |
| 512 | case CastOpMaybeWrap: | |
| 513 | const_val->data.x_maybe = other_val; | |
| 514 | const_val->ok = true; | |
| 515 | break; | |
| 516 | case CastOpErrorWrap: | |
| 517 | const_val->data.x_err.err = nullptr; | |
| 518 | const_val->data.x_err.payload = other_val; | |
| 519 | const_val->ok = true; | |
| 520 | break; | |
| 521 | case CastOpPureErrorWrap: | |
| 522 | const_val->data.x_err.err = other_val->data.x_err.err; | |
| 523 | const_val->ok = true; | |
| 524 | break; | |
| 525 | case CastOpErrToInt: | |
| 526 | { | |
| 527 | uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; | |
| 528 | bignum_init_unsigned(&const_val->data.x_bignum, value); | |
| 529 | const_val->ok = true; | |
| 530 | break; | |
| 531 | } | |
| 532 | case CastOpIntToFloat: | |
| 533 | bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); | |
| 534 | const_val->ok = true; | |
| 535 | break; | |
| 536 | case CastOpFloatToInt: | |
| 537 | bignum_cast_to_int(&const_val->data.x_bignum, &other_val->data.x_bignum); | |
| 538 | const_val->ok = true; | |
| 539 | break; | |
| 540 | case CastOpBoolToInt: | |
| 541 | bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_bool ? 1 : 0); | |
| 542 | const_val->ok = true; | |
| 543 | break; | |
| 544 | } | |
| 545 | } | |
| 546 | ||
| 547 | static bool int_type_depends_on_compile_var(CodeGen *g, TypeTableEntry *int_type) { | |
| 548 | assert(int_type->id == TypeTableEntryIdInt); | |
| 549 | ||
| 550 | for (int i = 0; i < CIntTypeCount; i += 1) { | |
| 551 | if (int_type == g->builtin_types.entry_c_int[i]) { | |
| 552 | return true; | |
| 553 | } | |
| 554 | } | |
| 555 | return false; | |
| 556 | } | |
| 557 | ||
| 558 | void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max) { | |
| 559 | if (type_entry->id == TypeTableEntryIdInt) { | |
| 560 | const_val->ok = true; | |
| 561 | const_val->depends_on_compile_var = int_type_depends_on_compile_var(g, type_entry); | |
| 562 | if (is_max) { | |
| 563 | if (type_entry->data.integral.is_signed) { | |
| 564 | int64_t val; | |
| 565 | if (type_entry->data.integral.bit_count == 64) { | |
| 566 | val = INT64_MAX; | |
| 567 | } else if (type_entry->data.integral.bit_count == 32) { | |
| 568 | val = INT32_MAX; | |
| 569 | } else if (type_entry->data.integral.bit_count == 16) { | |
| 570 | val = INT16_MAX; | |
| 571 | } else if (type_entry->data.integral.bit_count == 8) { | |
| 572 | val = INT8_MAX; | |
| 573 | } else { | |
| 574 | zig_unreachable(); | |
| 575 | } | |
| 576 | bignum_init_signed(&const_val->data.x_bignum, val); | |
| 577 | } else { | |
| 578 | uint64_t val; | |
| 579 | if (type_entry->data.integral.bit_count == 64) { | |
| 580 | val = UINT64_MAX; | |
| 581 | } else if (type_entry->data.integral.bit_count == 32) { | |
| 582 | val = UINT32_MAX; | |
| 583 | } else if (type_entry->data.integral.bit_count == 16) { | |
| 584 | val = UINT16_MAX; | |
| 585 | } else if (type_entry->data.integral.bit_count == 8) { | |
| 586 | val = UINT8_MAX; | |
| 587 | } else { | |
| 588 | zig_unreachable(); | |
| 589 | } | |
| 590 | bignum_init_unsigned(&const_val->data.x_bignum, val); | |
| 591 | } | |
| 592 | } else { | |
| 593 | if (type_entry->data.integral.is_signed) { | |
| 594 | int64_t val; | |
| 595 | if (type_entry->data.integral.bit_count == 64) { | |
| 596 | val = INT64_MIN; | |
| 597 | } else if (type_entry->data.integral.bit_count == 32) { | |
| 598 | val = INT32_MIN; | |
| 599 | } else if (type_entry->data.integral.bit_count == 16) { | |
| 600 | val = INT16_MIN; | |
| 601 | } else if (type_entry->data.integral.bit_count == 8) { | |
| 602 | val = INT8_MIN; | |
| 603 | } else { | |
| 604 | zig_unreachable(); | |
| 605 | } | |
| 606 | bignum_init_signed(&const_val->data.x_bignum, val); | |
| 607 | } else { | |
| 608 | bignum_init_unsigned(&const_val->data.x_bignum, 0); | |
| 609 | } | |
| 610 | } | |
| 611 | } else if (type_entry->id == TypeTableEntryIdFloat) { | |
| 612 | zig_panic("TODO analyze_min_max_value float"); | |
| 613 | } else if (type_entry->id == TypeTableEntryIdBool) { | |
| 614 | const_val->ok = true; | |
| 615 | const_val->data.x_bool = is_max; | |
| 616 | } else { | |
| 617 | zig_unreachable(); | |
| 618 | } | |
| 619 | } | |
| 620 | ||
| 621 | static bool eval_min_max(EvalFn *ef, AstNode *node, ConstExprValue *out_val, bool is_max) { | |
| 622 | assert(node->type == NodeTypeFnCallExpr); | |
| 623 | AstNode *type_node = node->data.fn_call_expr.params.at(0); | |
| 624 | TypeTableEntry *type_entry = resolve_expr_type(type_node); | |
| 625 | eval_min_max_value(ef->root->codegen, type_entry, out_val, is_max); | |
| 626 | return false; | |
| 627 | } | |
| 628 | ||
| 629 | static bool eval_fn_with_overflow(EvalFn *ef, AstNode *node, ConstExprValue *out_val, | |
| 630 | bool (*bignum_fn)(BigNum *dest, BigNum *op1, BigNum *op2)) | |
| 631 | { | |
| 632 | assert(node->type == NodeTypeFnCallExpr); | |
| 633 | ||
| 634 | AstNode *type_node = node->data.fn_call_expr.params.at(0); | |
| 635 | TypeTableEntry *int_type = resolve_expr_type(type_node); | |
| 636 | assert(int_type->id == TypeTableEntryIdInt); | |
| 637 | ||
| 638 | AstNode *op1_node = node->data.fn_call_expr.params.at(1); | |
| 639 | AstNode *op2_node = node->data.fn_call_expr.params.at(2); | |
| 640 | AstNode *result_node = node->data.fn_call_expr.params.at(3); | |
| 641 | ||
| 642 | ConstExprValue op1_val = {0}; | |
| 643 | if (eval_expr(ef, op1_node, &op1_val)) return true; | |
| 644 | ||
| 645 | ConstExprValue op2_val = {0}; | |
| 646 | if (eval_expr(ef, op2_node, &op2_val)) return true; | |
| 647 | ||
| 648 | ConstExprValue result_ptr_val = {0}; | |
| 649 | if (eval_expr(ef, result_node, &result_ptr_val)) return true; | |
| 650 | ||
| 651 | ConstExprValue *result_val = result_ptr_val.data.x_ptr.ptr[0]; | |
| 652 | ||
| 653 | out_val->ok = true; | |
| 654 | bool overflow = bignum_fn(&result_val->data.x_bignum, &op1_val.data.x_bignum, &op2_val.data.x_bignum); | |
| 655 | ||
| 656 | overflow = overflow || !bignum_fits_in_bits(&result_val->data.x_bignum, | |
| 657 | int_type->data.integral.bit_count, int_type->data.integral.is_signed); | |
| 658 | ||
| 659 | out_val->data.x_bool = overflow; | |
| 660 | ||
| 661 | if (overflow) { | |
| 662 | bignum_truncate(&result_val->data.x_bignum, int_type->data.integral.bit_count); | |
| 663 | } | |
| 664 | ||
| 665 | return false; | |
| 666 | } | |
| 667 | ||
| 668 | static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 669 | assert(node->type == NodeTypeFnCallExpr); | |
| 670 | ||
| 671 | BuiltinFnEntry *builtin_fn = node->data.fn_call_expr.builtin_fn; | |
| 672 | switch (builtin_fn->id) { | |
| 673 | case BuiltinFnIdMaxValue: | |
| 674 | return eval_min_max(ef, node, out_val, true); | |
| 675 | case BuiltinFnIdMinValue: | |
| 676 | return eval_min_max(ef, node, out_val, false); | |
| 677 | case BuiltinFnIdMulWithOverflow: | |
| 678 | return eval_fn_with_overflow(ef, node, out_val, bignum_mul); | |
| 679 | case BuiltinFnIdAddWithOverflow: | |
| 680 | return eval_fn_with_overflow(ef, node, out_val, bignum_add); | |
| 681 | case BuiltinFnIdSubWithOverflow: | |
| 682 | return eval_fn_with_overflow(ef, node, out_val, bignum_sub); | |
| 683 | case BuiltinFnIdMemcpy: | |
| 684 | case BuiltinFnIdMemset: | |
| 685 | case BuiltinFnIdSizeof: | |
| 686 | case BuiltinFnIdAlignof: | |
| 687 | case BuiltinFnIdMemberCount: | |
| 688 | case BuiltinFnIdTypeof: | |
| 689 | case BuiltinFnIdCInclude: | |
| 690 | case BuiltinFnIdCDefine: | |
| 691 | case BuiltinFnIdCUndef: | |
| 692 | case BuiltinFnIdCompileVar: | |
| 693 | case BuiltinFnIdConstEval: | |
| 694 | case BuiltinFnIdCtz: | |
| 695 | case BuiltinFnIdClz: | |
| 696 | case BuiltinFnIdImport: | |
| 697 | case BuiltinFnIdCImport: | |
| 698 | case BuiltinFnIdErrName: | |
| 699 | zig_panic("TODO"); | |
| 700 | case BuiltinFnIdBreakpoint: | |
| 701 | case BuiltinFnIdInvalid: | |
| 702 | zig_unreachable(); | |
| 703 | } | |
| 704 | ||
| 705 | return false; | |
| 706 | } | |
| 707 | ||
| 708 | static bool eval_fn_call_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 709 | assert(node->type == NodeTypeFnCallExpr); | |
| 710 | ||
| 711 | AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; | |
| 712 | CastOp cast_op = node->data.fn_call_expr.cast_op; | |
| 713 | if (node->data.fn_call_expr.is_builtin) { | |
| 714 | return eval_fn_call_builtin(ef, node, out_val); | |
| 715 | } else if (cast_op != CastOpNoCast) { | |
| 716 | TypeTableEntry *new_type = resolve_expr_type(fn_ref_expr); | |
| 717 | AstNode *param_node = node->data.fn_call_expr.params.at(0); | |
| 718 | TypeTableEntry *old_type = get_resolved_expr(param_node)->type_entry; | |
| 719 | ConstExprValue param_val = {0}; | |
| 720 | if (eval_expr(ef, param_node, &param_val)) return true; | |
| 721 | eval_const_expr_implicit_cast(cast_op, &param_val, old_type, out_val, new_type); | |
| 722 | return false; | |
| 723 | } | |
| 724 | ||
| 725 | if (node->data.fn_call_expr.enum_type) { | |
| 726 | zig_panic("TODO"); | |
| 727 | } | |
| 728 | ||
| 729 | FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry; | |
| 730 | ||
| 731 | if (fn_ref_expr->type == NodeTypeFieldAccessExpr && | |
| 732 | fn_ref_expr->data.field_access_expr.is_member_fn) | |
| 733 | { | |
| 734 | zig_panic("TODO"); | |
| 735 | } | |
| 736 | ||
| 737 | if (!fn_table_entry) { | |
| 738 | ConstExprValue fn_val = {0}; | |
| 739 | if (eval_expr(ef, fn_ref_expr, &fn_val)) return true; | |
| 740 | fn_table_entry = fn_val.data.x_fn; | |
| 741 | } | |
| 742 | ||
| 743 | int param_count = node->data.fn_call_expr.params.length; | |
| 744 | ConstExprValue *args = allocate<ConstExprValue>(param_count); | |
| 745 | for (int i = 0; i < param_count; i += 1) { | |
| 746 | AstNode *param_expr_node = node->data.fn_call_expr.params.at(i); | |
| 747 | ConstExprValue *param_val = &args[i]; | |
| 748 | if (eval_expr(ef, param_expr_node, param_val)) return true; | |
| 749 | } | |
| 750 | ||
| 751 | ef->root->branches_used += 1; | |
| 752 | ||
| 753 | eval_fn_args(ef->root, fn_table_entry, args, out_val); | |
| 754 | return false; | |
| 755 | } | |
| 756 | ||
| 757 | static bool eval_field_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 758 | assert(node->type == NodeTypeFieldAccessExpr); | |
| 759 | ||
| 760 | AstNode *struct_expr = node->data.field_access_expr.struct_expr; | |
| 761 | TypeTableEntry *struct_type = get_resolved_expr(struct_expr)->type_entry; | |
| 762 | ||
| 763 | if (struct_type->id == TypeTableEntryIdArray) { | |
| 764 | Buf *name = &node->data.field_access_expr.field_name; | |
| 765 | assert(buf_eql_str(name, "len")); | |
| 766 | zig_panic("TODO"); | |
| 767 | } else if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && | |
| 768 | struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) | |
| 769 | { | |
| 770 | TypeStructField *tsf = node->data.field_access_expr.type_struct_field; | |
| 771 | assert(tsf); | |
| 772 | if (struct_type->id == TypeTableEntryIdStruct) { | |
| 773 | ConstExprValue struct_val = {0}; | |
| 774 | if (eval_expr(ef, struct_expr, &struct_val)) return true; | |
| 775 | ConstExprValue *field_value = struct_val.data.x_struct.fields[tsf->src_index]; | |
| 776 | *out_val = *field_value; | |
| 777 | assert(out_val->ok); | |
| 778 | } else { | |
| 779 | zig_panic("TODO"); | |
| 780 | } | |
| 781 | } else if (struct_type->id == TypeTableEntryIdMetaType) { | |
| 782 | TypeTableEntry *child_type = resolve_expr_type(struct_expr); | |
| 783 | if (child_type->id == TypeTableEntryIdPureError) { | |
| 784 | *out_val = get_resolved_expr(node)->const_val; | |
| 785 | } else { | |
| 786 | zig_panic("TODO"); | |
| 787 | } | |
| 788 | } else if (struct_type->id == TypeTableEntryIdNamespace) { | |
| 789 | zig_panic("TODO"); | |
| 790 | } else { | |
| 791 | zig_unreachable(); | |
| 792 | } | |
| 793 | ||
| 794 | return false; | |
| 795 | } | |
| 796 | ||
| 797 | static bool eval_for_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 798 | assert(node->type == NodeTypeForExpr); | |
| 799 | ||
| 800 | AstNode *array_node = node->data.for_expr.array_expr; | |
| 801 | AstNode *elem_node = node->data.for_expr.elem_node; | |
| 802 | AstNode *index_node = node->data.for_expr.index_node; | |
| 803 | AstNode *body_node = node->data.for_expr.body; | |
| 804 | ||
| 805 | TypeTableEntry *array_type = get_resolved_expr(array_node)->type_entry; | |
| 806 | ||
| 807 | ConstExprValue array_val = {0}; | |
| 808 | if (eval_expr(ef, array_node, &array_val)) return true; | |
| 809 | ||
| 810 | assert(elem_node->type == NodeTypeSymbol); | |
| 811 | Buf *elem_var_name = &elem_node->data.symbol_expr.symbol; | |
| 812 | ||
| 813 | Buf *index_var_name = nullptr; | |
| 814 | if (index_node) { | |
| 815 | assert(index_node->type == NodeTypeSymbol); | |
| 816 | index_var_name = &index_node->data.symbol_expr.symbol; | |
| 817 | } | |
| 818 | ||
| 819 | uint64_t it_index = 0; | |
| 820 | uint64_t array_len; | |
| 821 | ConstExprValue **array_ptr_val; | |
| 822 | if (array_type->id == TypeTableEntryIdArray) { | |
| 823 | array_len = array_type->data.array.len; | |
| 824 | array_ptr_val = array_val.data.x_array.fields; | |
| 825 | } else if (array_type->id == TypeTableEntryIdStruct) { | |
| 826 | ConstExprValue *len_field_val = array_val.data.x_struct.fields[1]; | |
| 827 | array_len = len_field_val->data.x_bignum.data.x_uint; | |
| 828 | array_ptr_val = array_val.data.x_struct.fields[0]->data.x_ptr.ptr; | |
| 829 | } else { | |
| 830 | zig_unreachable(); | |
| 831 | } | |
| 832 | ||
| 833 | EvalScope *my_scope = allocate<EvalScope>(1); | |
| 834 | my_scope->block_context = body_node->block_context; | |
| 835 | ef->scope_stack.append(my_scope); | |
| 836 | ||
| 837 | for (; it_index < array_len; it_index += 1) { | |
| 838 | my_scope->vars.resize(0); | |
| 839 | ||
| 840 | if (index_var_name) { | |
| 841 | my_scope->vars.add_one(); | |
| 842 | EvalVar *index_var = &my_scope->vars.last(); | |
| 843 | index_var->name = index_var_name; | |
| 844 | memset(&index_var->value, 0, sizeof(ConstExprValue)); | |
| 845 | index_var->value.ok = true; | |
| 846 | bignum_init_unsigned(&index_var->value.data.x_bignum, it_index); | |
| 847 | } | |
| 848 | { | |
| 849 | my_scope->vars.add_one(); | |
| 850 | EvalVar *elem_var = &my_scope->vars.last(); | |
| 851 | elem_var->name = elem_var_name; | |
| 852 | elem_var->value = *array_ptr_val[it_index]; | |
| 853 | } | |
| 854 | ||
| 855 | ConstExprValue body_val = {0}; | |
| 856 | if (eval_expr(ef, body_node, &body_val)) return true; | |
| 857 | ||
| 858 | ef->root->branches_used += 1; | |
| 859 | } | |
| 860 | ||
| 861 | ef->scope_stack.pop(); | |
| 862 | ||
| 863 | return false; | |
| 864 | } | |
| 865 | ||
| 866 | static bool eval_array_access_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 867 | assert(node->type == NodeTypeArrayAccessExpr); | |
| 868 | ||
| 869 | AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; | |
| 870 | AstNode *index_node = node->data.array_access_expr.subscript; | |
| 871 | ||
| 872 | TypeTableEntry *array_type = get_resolved_expr(array_ref_node)->type_entry; | |
| 873 | ||
| 874 | ConstExprValue array_val = {0}; | |
| 875 | if (eval_expr(ef, array_ref_node, &array_val)) return true; | |
| 876 | ||
| 877 | ConstExprValue index_val = {0}; | |
| 878 | if (eval_expr(ef, index_node, &index_val)) return true; | |
| 879 | uint64_t index_int = index_val.data.x_bignum.data.x_uint; | |
| 880 | ||
| 881 | if (array_type->id == TypeTableEntryIdPointer) { | |
| 882 | if (index_int >= array_val.data.x_ptr.len) { | |
| 883 | zig_panic("TODO"); | |
| 884 | } | |
| 885 | *out_val = *array_val.data.x_ptr.ptr[index_int]; | |
| 886 | } else if (array_type->id == TypeTableEntryIdStruct) { | |
| 887 | assert(array_type->data.structure.is_unknown_size_array); | |
| 888 | ||
| 889 | ConstExprValue *len_value = array_val.data.x_struct.fields[1]; | |
| 890 | uint64_t len_int = len_value->data.x_bignum.data.x_uint; | |
| 891 | if (index_int >= len_int) { | |
| 892 | zig_panic("TODO"); | |
| 893 | } | |
| 894 | ||
| 895 | ConstExprValue *ptr_value = array_val.data.x_struct.fields[0]; | |
| 896 | *out_val = *ptr_value->data.x_ptr.ptr[index_int]; | |
| 897 | } else if (array_type->id == TypeTableEntryIdArray) { | |
| 898 | uint64_t array_len = array_type->data.array.len; | |
| 899 | if (index_int >= array_len) { | |
| 900 | zig_panic("TODO"); | |
| 901 | } | |
| 902 | *out_val = *array_val.data.x_array.fields[index_int]; | |
| 903 | } else { | |
| 904 | zig_unreachable(); | |
| 905 | } | |
| 906 | ||
| 907 | return false; | |
| 908 | } | |
| 909 | ||
| 910 | static bool eval_bool_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 911 | assert(node->type == NodeTypeBoolLiteral); | |
| 912 | ||
| 913 | out_val->ok = true; | |
| 914 | out_val->data.x_bool = node->data.bool_literal.value; | |
| 915 | ||
| 916 | return false; | |
| 917 | } | |
| 918 | ||
| 919 | static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 920 | assert(node->type == NodeTypePrefixOpExpr); | |
| 921 | ||
| 922 | PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; | |
| 923 | AstNode *expr_node = node->data.prefix_op_expr.primary_expr; | |
| 924 | ||
| 925 | ConstExprValue expr_val = {0}; | |
| 926 | if (eval_expr(ef, expr_node, &expr_val)) return true; | |
| 927 | ||
| 928 | TypeTableEntry *expr_type = get_resolved_expr(expr_node)->type_entry; | |
| 929 | ||
| 930 | switch (prefix_op) { | |
| 931 | case PrefixOpBoolNot: | |
| 932 | *out_val = expr_val; | |
| 933 | out_val->data.x_bool = !out_val->data.x_bool; | |
| 934 | break; | |
| 935 | case PrefixOpDereference: | |
| 936 | assert(expr_type->id == TypeTableEntryIdPointer); | |
| 937 | *out_val = *expr_val.data.x_ptr.ptr[0]; | |
| 938 | break; | |
| 939 | case PrefixOpAddressOf: | |
| 940 | case PrefixOpConstAddressOf: | |
| 941 | { | |
| 942 | ConstExprValue *child_val = allocate<ConstExprValue>(1); | |
| 943 | *child_val = expr_val; | |
| 944 | ||
| 945 | ConstExprValue **ptr_val = allocate<ConstExprValue*>(1); | |
| 946 | *ptr_val = child_val; | |
| 947 | ||
| 948 | out_val->data.x_ptr.ptr = ptr_val; | |
| 949 | out_val->data.x_ptr.len = 1; | |
| 950 | out_val->ok = true; | |
| 951 | break; | |
| 952 | } | |
| 953 | case PrefixOpBinNot: | |
| 954 | case PrefixOpNegation: | |
| 955 | case PrefixOpMaybe: | |
| 956 | case PrefixOpError: | |
| 957 | case PrefixOpUnwrapError: | |
| 958 | case PrefixOpUnwrapMaybe: | |
| 959 | zig_panic("TODO"); | |
| 960 | case PrefixOpInvalid: | |
| 961 | zig_unreachable(); | |
| 962 | } | |
| 963 | ||
| 964 | return false; | |
| 965 | } | |
| 966 | ||
| 967 | static bool eval_var_decl_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 968 | assert(node->type == NodeTypeVariableDeclaration); | |
| 969 | ||
| 970 | assert(node->data.variable_declaration.expr); | |
| 971 | ||
| 972 | EvalScope *my_scope = ef->scope_stack.at(ef->scope_stack.length - 1); | |
| 973 | ||
| 974 | my_scope->vars.add_one(); | |
| 975 | EvalVar *var = &my_scope->vars.last(); | |
| 976 | var->name = &node->data.variable_declaration.symbol; | |
| 977 | ||
| 978 | if (eval_expr(ef, node->data.variable_declaration.expr, &var->value)) return true; | |
| 979 | ||
| 980 | out_val->ok = true; | |
| 981 | ||
| 982 | return false; | |
| 983 | } | |
| 984 | ||
| 985 | static bool eval_number_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 986 | assert(node->type == NodeTypeNumberLiteral); | |
| 987 | assert(!node->data.number_literal.overflow); | |
| 988 | ||
| 989 | out_val->ok = true; | |
| 990 | if (node->data.number_literal.kind == NumLitUInt) { | |
| 991 | bignum_init_unsigned(&out_val->data.x_bignum, node->data.number_literal.data.x_uint); | |
| 992 | } else if (node->data.number_literal.kind == NumLitFloat) { | |
| 993 | bignum_init_float(&out_val->data.x_bignum, node->data.number_literal.data.x_float); | |
| 994 | } else { | |
| 995 | zig_unreachable(); | |
| 996 | } | |
| 997 | ||
| 998 | return false; | |
| 999 | } | |
| 1000 | ||
| 1001 | static bool eval_char_literal_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 1002 | assert(node->type == NodeTypeCharLiteral); | |
| 1003 | ||
| 1004 | out_val->ok = true; | |
| 1005 | bignum_init_unsigned(&out_val->data.x_bignum, node->data.char_literal.value); | |
| 1006 | ||
| 1007 | return false; | |
| 1008 | } | |
| 1009 | ||
| 1010 | static bool eval_while_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) { | |
| 1011 | assert(node->type == NodeTypeWhileExpr); | |
| 1012 | ||
| 1013 | AstNode *cond_node = node->data.while_expr.condition; | |
| 1014 | AstNode *body_node = node->data.while_expr.body; | |
| 1015 | ||
| 1016 | EvalScope *my_scope = allocate<EvalScope>(1); | |
| 1017 | my_scope->block_context = body_node->block_context; | |
| 1018 | ef->scope_stack.append(my_scope); | |
| 1019 | ||
| 1020 | for (;;) { | |
| 1021 | my_scope->vars.resize(0); | |
| 1022 | ||
| 1023 | ConstExprValue cond_val = {0}; | |
| 1024 | if (eval_expr(ef, cond_node, &cond_val)) return true; | |
| 1025 | ||
| 1026 | if (!cond_val.data.x_bool) break; | |
| 1027 | ||
| 1028 | ConstExprValue body_val = {0}; | |
| 1029 | if (eval_expr(ef, body_node, &body_val)) return true; | |
| 1030 | ||
| 1031 | ef->root->branches_used += 1; | |
| 1032 | } | |
| 1033 | ||
| 1034 | ef->scope_stack.pop(); | |
| 1035 | ||
| 1036 | return false; | |
| 1037 | } | |
| 1038 | ||
| 1039 | static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { | |
| 1040 | if (ef->root->branches_used > ef->root->branch_quota) { | |
| 1041 | ef->root->exceeded_quota_node = node; | |
| 1042 | return true; | |
| 1043 | } | |
| 1044 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 1045 | if (const_val->ok) { | |
| 1046 | *out = *const_val; | |
| 1047 | return false; | |
| 1048 | } | |
| 1049 | switch (node->type) { | |
| 1050 | case NodeTypeBlock: | |
| 1051 | return eval_block(ef, node, out); | |
| 1052 | case NodeTypeReturnExpr: | |
| 1053 | return eval_return(ef, node, out); | |
| 1054 | case NodeTypeBinOpExpr: | |
| 1055 | return eval_bin_op_expr(ef, node, out); | |
| 1056 | case NodeTypeSymbol: | |
| 1057 | return eval_symbol_expr(ef, node, out); | |
| 1058 | case NodeTypeContainerInitExpr: | |
| 1059 | return eval_container_init_expr(ef, node, out); | |
| 1060 | case NodeTypeIfBoolExpr: | |
| 1061 | return eval_if_bool_expr(ef, node, out); | |
| 1062 | case NodeTypeFnCallExpr: | |
| 1063 | return eval_fn_call_expr(ef, node, out); | |
| 1064 | case NodeTypeFieldAccessExpr: | |
| 1065 | return eval_field_access_expr(ef, node, out); | |
| 1066 | case NodeTypeForExpr: | |
| 1067 | return eval_for_expr(ef, node, out); | |
| 1068 | case NodeTypeArrayAccessExpr: | |
| 1069 | return eval_array_access_expr(ef, node, out); | |
| 1070 | case NodeTypeBoolLiteral: | |
| 1071 | return eval_bool_literal_expr(ef, node, out); | |
| 1072 | case NodeTypePrefixOpExpr: | |
| 1073 | return eval_prefix_op_expr(ef, node, out); | |
| 1074 | case NodeTypeVariableDeclaration: | |
| 1075 | return eval_var_decl_expr(ef, node, out); | |
| 1076 | case NodeTypeNumberLiteral: | |
| 1077 | return eval_number_literal_expr(ef, node, out); | |
| 1078 | case NodeTypeCharLiteral: | |
| 1079 | return eval_char_literal_expr(ef, node, out); | |
| 1080 | case NodeTypeWhileExpr: | |
| 1081 | return eval_while_expr(ef, node, out); | |
| 1082 | case NodeTypeDefer: | |
| 1083 | case NodeTypeErrorValueDecl: | |
| 1084 | case NodeTypeUnwrapErrorExpr: | |
| 1085 | case NodeTypeStringLiteral: | |
| 1086 | case NodeTypeSliceExpr: | |
| 1087 | case NodeTypeNullLiteral: | |
| 1088 | case NodeTypeUndefinedLiteral: | |
| 1089 | case NodeTypeIfVarExpr: | |
| 1090 | case NodeTypeSwitchExpr: | |
| 1091 | case NodeTypeSwitchProng: | |
| 1092 | case NodeTypeSwitchRange: | |
| 1093 | case NodeTypeLabel: | |
| 1094 | case NodeTypeGoto: | |
| 1095 | case NodeTypeBreak: | |
| 1096 | case NodeTypeContinue: | |
| 1097 | case NodeTypeStructDecl: | |
| 1098 | case NodeTypeStructField: | |
| 1099 | case NodeTypeStructValueField: | |
| 1100 | case NodeTypeArrayType: | |
| 1101 | case NodeTypeErrorType: | |
| 1102 | case NodeTypeTypeLiteral: | |
| 1103 | zig_panic("TODO"); | |
| 1104 | case NodeTypeRoot: | |
| 1105 | case NodeTypeFnProto: | |
| 1106 | case NodeTypeFnDef: | |
| 1107 | case NodeTypeFnDecl: | |
| 1108 | case NodeTypeUse: | |
| 1109 | case NodeTypeAsmExpr: | |
| 1110 | case NodeTypeParamDecl: | |
| 1111 | case NodeTypeDirective: | |
| 1112 | case NodeTypeTypeDecl: | |
| 1113 | zig_unreachable(); | |
| 1114 | } | |
| 1115 | } | |
| 1116 | ||
| 1117 | static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val) { | |
| 1118 | EvalFn ef = {0}; | |
| 1119 | ef.root = efr; | |
| 1120 | ef.fn = fn; | |
| 1121 | ef.return_expr = out_val; | |
| 1122 | ||
| 1123 | EvalScope *root_scope = allocate<EvalScope>(1); | |
| 1124 | root_scope->block_context = fn->fn_def_node->data.fn_def.body->block_context; | |
| 1125 | ef.scope_stack.append(root_scope); | |
| 1126 | ||
| 1127 | int param_count = fn->type_entry->data.fn.fn_type_id.param_count; | |
| 1128 | for (int i = 0; i < param_count; i += 1) { | |
| 1129 | AstNode *decl_param_node = fn->proto_node->data.fn_proto.params.at(i); | |
| 1130 | assert(decl_param_node->type == NodeTypeParamDecl); | |
| 1131 | ||
| 1132 | ConstExprValue *src_const_val = &args[i]; | |
| 1133 | assert(src_const_val->ok); | |
| 1134 | ||
| 1135 | root_scope->vars.add_one(); | |
| 1136 | EvalVar *eval_var = &root_scope->vars.last(); | |
| 1137 | eval_var->name = &decl_param_node->data.param_decl.name; | |
| 1138 | eval_var->value = *src_const_val; | |
| 1139 | } | |
| 1140 | ||
| 1141 | return eval_expr(&ef, fn->fn_def_node->data.fn_def.body, out_val); | |
| 1142 | ||
| 1143 | } | |
| 1144 | ||
| 1145 | bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val, | |
| 1146 | int branch_quota, AstNode *struct_node) | |
| 1147 | { | |
| 1148 | assert(node->type == NodeTypeFnCallExpr); | |
| 1149 | ||
| 1150 | EvalFnRoot efr = {0}; | |
| 1151 | efr.codegen = g; | |
| 1152 | efr.fn = fn; | |
| 1153 | efr.call_node = node; | |
| 1154 | efr.branch_quota = branch_quota; | |
| 1155 | ||
| 1156 | int call_param_count = node->data.fn_call_expr.params.length; | |
| 1157 | int type_param_count = fn->type_entry->data.fn.fn_type_id.param_count; | |
| 1158 | ConstExprValue *args = allocate<ConstExprValue>(type_param_count); | |
| 1159 | int next_arg_index = 0; | |
| 1160 | if (struct_node) { | |
| 1161 | ConstExprValue *struct_val = &get_resolved_expr(struct_node)->const_val; | |
| 1162 | assert(struct_val->ok); | |
| 1163 | args[next_arg_index] = *struct_val; | |
| 1164 | next_arg_index += 1; | |
| 1165 | } | |
| 1166 | for (int call_index = 0; call_index < call_param_count; call_index += 1) { | |
| 1167 | AstNode *call_param_node = node->data.fn_call_expr.params.at(call_index); | |
| 1168 | ConstExprValue *src_const_val = &get_resolved_expr(call_param_node)->const_val; | |
| 1169 | assert(src_const_val->ok); | |
| 1170 | args[next_arg_index] = *src_const_val; | |
| 1171 | next_arg_index += 1; | |
| 1172 | } | |
| 1173 | eval_fn_args(&efr, fn, args, out_val); | |
| 1174 | ||
| 1175 | if (efr.exceeded_quota_node) { | |
| 1176 | ErrorMsg *msg = add_node_error(g, fn->fn_def_node, | |
| 1177 | buf_sprintf("function evaluation exceeded %d branches", efr.branch_quota)); | |
| 1178 | ||
| 1179 | add_error_note(g, msg, efr.call_node, buf_sprintf("called from here")); | |
| 1180 | add_error_note(g, msg, efr.exceeded_quota_node, buf_sprintf("quota exceeded here")); | |
| 1181 | return true; | |
| 1182 | } | |
| 1183 | ||
| 1184 | if (efr.abort) { | |
| 1185 | return true; | |
| 1186 | } | |
| 1187 | ||
| 1188 | assert(out_val->ok); | |
| 1189 | return false; | |
| 1190 | } | |
| 1191 |
src/eval.hpp created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | /* | |
| 2 | * Copyright (c) 2016 Andrew Kelley | |
| 3 | * | |
| 4 | * This file is part of zig, which is MIT licensed. | |
| 5 | * See http://opensource.org/licenses/MIT | |
| 6 | */ | |
| 7 | ||
| 8 | #ifndef ZIG_EVAL_HPP | |
| 9 | #define ZIG_EVAL_HPP | |
| 10 | ||
| 11 | #include "all_types.hpp" | |
| 12 | ||
| 13 | bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_val, int branch_quota, | |
| 14 | AstNode *struct_node); | |
| 15 | ||
| 16 | bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry); | |
| 17 | int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, | |
| 18 | BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val); | |
| 19 | ||
| 20 | void eval_const_expr_implicit_cast(CastOp cast_op, | |
| 21 | ConstExprValue *other_val, TypeTableEntry *other_type, | |
| 22 | ConstExprValue *const_val, TypeTableEntry *new_type); | |
| 23 | ||
| 24 | void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val, bool is_max); | |
| 25 | ||
| 26 | #endif |
src/parser.cpp+15-7| ... | ... | @@ -2927,6 +2927,7 @@ static void clone_subtree_list(ZigList<AstNode *> *dest, ZigList<AstNode *> *src |
| 2927 | 2927 | dest->resize(src->length); |
| 2928 | 2928 | for (int i = 0; i < src->length; i += 1) { |
| 2929 | 2929 | dest->at(i) = ast_clone_subtree(src->at(i), next_node_index); |
| 2930 | dest->at(i)->parent_field = &dest->at(i); | |
| 2930 | 2931 | } |
| 2931 | 2932 | } |
| 2932 | 2933 | |
| ... | ... | @@ -2958,11 +2959,12 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { |
| 2958 | 2959 | memcpy(new_node, old_node, sizeof(AstNode)); |
| 2959 | 2960 | new_node->create_index = *next_node_index; |
| 2960 | 2961 | *next_node_index += 1; |
| 2962 | new_node->parent_field = nullptr; | |
| 2961 | 2963 | |
| 2962 | 2964 | switch (new_node->type) { |
| 2963 | 2965 | case NodeTypeRoot: |
| 2964 | clone_subtree_list(&new_node->data.root.top_level_decls, &old_node->data.root.top_level_decls, | |
| 2965 | next_node_index); | |
| 2966 | clone_subtree_list(&new_node->data.root.top_level_decls, | |
| 2967 | &old_node->data.root.top_level_decls, next_node_index); | |
| 2966 | 2968 | break; |
| 2967 | 2969 | case NodeTypeFnProto: |
| 2968 | 2970 | clone_subtree_tld(&new_node->data.fn_proto.top_level_decl, &old_node->data.fn_proto.top_level_decl, |
| ... | ... | @@ -3036,15 +3038,21 @@ AstNode *ast_clone_subtree(AstNode *old_node, uint32_t *next_node_index) { |
| 3036 | 3038 | // none |
| 3037 | 3039 | break; |
| 3038 | 3040 | case NodeTypePrefixOpExpr: |
| 3039 | clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr, old_node->data.prefix_op_expr.primary_expr, next_node_index); | |
| 3041 | clone_subtree_field(&new_node->data.prefix_op_expr.primary_expr, | |
| 3042 | old_node->data.prefix_op_expr.primary_expr, next_node_index); | |
| 3040 | 3043 | break; |
| 3041 | 3044 | case NodeTypeFnCallExpr: |
| 3042 | clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr, old_node->data.fn_call_expr.fn_ref_expr, next_node_index); | |
| 3043 | clone_subtree_list(&new_node->data.fn_call_expr.params, &old_node->data.fn_call_expr.params, next_node_index); | |
| 3045 | assert(!old_node->data.fn_call_expr.resolved_expr.has_global_const); | |
| 3046 | clone_subtree_field(&new_node->data.fn_call_expr.fn_ref_expr, | |
| 3047 | old_node->data.fn_call_expr.fn_ref_expr, next_node_index); | |
| 3048 | clone_subtree_list(&new_node->data.fn_call_expr.params, | |
| 3049 | &old_node->data.fn_call_expr.params, next_node_index); | |
| 3044 | 3050 | break; |
| 3045 | 3051 | case NodeTypeArrayAccessExpr: |
| 3046 | clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr, old_node->data.array_access_expr.array_ref_expr, next_node_index); | |
| 3047 | clone_subtree_field(&new_node->data.array_access_expr.subscript, old_node->data.array_access_expr.subscript, next_node_index); | |
| 3052 | clone_subtree_field(&new_node->data.array_access_expr.array_ref_expr, | |
| 3053 | old_node->data.array_access_expr.array_ref_expr, next_node_index); | |
| 3054 | clone_subtree_field(&new_node->data.array_access_expr.subscript, | |
| 3055 | old_node->data.array_access_expr.subscript, next_node_index); | |
| 3048 | 3056 | break; |
| 3049 | 3057 | case NodeTypeSliceExpr: |
| 3050 | 3058 | clone_subtree_field(&new_node->data.slice_expr.array_ref_expr, old_node->data.slice_expr.array_ref_expr, next_node_index); |
std/rand.zig+1| ... | ... | @@ -7,6 +7,7 @@ pub struct Rand { |
| 7 | 7 | index: isize, |
| 8 | 8 | |
| 9 | 9 | /// Initialize random state with the given seed. |
| 10 | #static_eval_enable(false) | |
| 10 | 11 | pub fn init(seed: u32) -> Rand { |
| 11 | 12 | var r: Rand = undefined; |
| 12 | 13 | r.index = 0; |
test/run_tests.cpp+29-122| ... | ... | @@ -218,6 +218,7 @@ pub fn bar_function() { |
| 218 | 218 | )SOURCE"); |
| 219 | 219 | |
| 220 | 220 | add_source_file(tc, "other.zig", R"SOURCE( |
| 221 | #static_eval_enable(false) | |
| 221 | 222 | pub fn foo_function() -> bool { |
| 222 | 223 | // this one conflicts with the one from foo |
| 223 | 224 | return true; |
| ... | ... | @@ -406,20 +407,6 @@ pub fn main(args: [][]u8) -> %void { |
| 406 | 407 | } |
| 407 | 408 | )SOURCE", "loop\nloop\nloop\nloop\n"); |
| 408 | 409 | |
| 409 | add_simple_case("implicit cast after unreachable", R"SOURCE( | |
| 410 | const io = @import("std").io; | |
| 411 | pub fn main(args: [][]u8) -> %void { | |
| 412 | const x = outer(); | |
| 413 | if (x == 1234) { | |
| 414 | %%io.stdout.printf("OK\n"); | |
| 415 | } | |
| 416 | } | |
| 417 | fn inner() -> i32 { 1234 } | |
| 418 | fn outer() -> isize { | |
| 419 | return inner(); | |
| 420 | } | |
| 421 | )SOURCE", "OK\n"); | |
| 422 | ||
| 423 | 410 | add_simple_case("@sizeof() and @typeof()", R"SOURCE( |
| 424 | 411 | const io = @import("std").io; |
| 425 | 412 | const x: u16 = 13; |
| ... | ... | @@ -431,23 +418,6 @@ pub fn main(args: [][]u8) -> %void { |
| 431 | 418 | } |
| 432 | 419 | )SOURCE", "2\n"); |
| 433 | 420 | |
| 434 | add_simple_case("member functions", R"SOURCE( | |
| 435 | const io = @import("std").io; | |
| 436 | struct Rand { | |
| 437 | seed: u32, | |
| 438 | pub fn get_seed(r: Rand) -> u32 { | |
| 439 | r.seed | |
| 440 | } | |
| 441 | } | |
| 442 | pub fn main(args: [][]u8) -> %void { | |
| 443 | const r = Rand {.seed = 1234}; | |
| 444 | if (r.get_seed() != 1234) { | |
| 445 | %%io.stdout.printf("BAD seed\n"); | |
| 446 | } | |
| 447 | %%io.stdout.printf("OK\n"); | |
| 448 | } | |
| 449 | )SOURCE", "OK\n"); | |
| 450 | ||
| 451 | 421 | add_simple_case("pointer dereferencing", R"SOURCE( |
| 452 | 422 | const io = @import("std").io; |
| 453 | 423 | |
| ... | ... | @@ -565,24 +535,6 @@ pub fn main(args: [][]u8) -> %void { |
| 565 | 535 | "min i64: -9223372036854775808\n"); |
| 566 | 536 | |
| 567 | 537 | |
| 568 | add_simple_case("else if expression", R"SOURCE( | |
| 569 | const io = @import("std").io; | |
| 570 | pub fn main(args: [][]u8) -> %void { | |
| 571 | if (f(1) == 1) { | |
| 572 | %%io.stdout.printf("OK\n"); | |
| 573 | } | |
| 574 | } | |
| 575 | fn f(c: u8) -> u8 { | |
| 576 | if (c == 0) { | |
| 577 | 0 | |
| 578 | } else if (c == 1) { | |
| 579 | 1 | |
| 580 | } else { | |
| 581 | 2 | |
| 582 | } | |
| 583 | } | |
| 584 | )SOURCE", "OK\n"); | |
| 585 | ||
| 586 | 538 | add_simple_case("overflow intrinsics", R"SOURCE( |
| 587 | 539 | const io = @import("std").io; |
| 588 | 540 | pub fn main(args: [][]u8) -> %void { |
| ... | ... | @@ -730,29 +682,6 @@ pub fn main(args: [][]u8) -> %void { |
| 730 | 682 | } |
| 731 | 683 | )SOURCE", "OK\n"); |
| 732 | 684 | |
| 733 | add_simple_case("%% binary operator", R"SOURCE( | |
| 734 | const io = @import("std").io; | |
| 735 | error ItBroke; | |
| 736 | fn g(x: bool) -> %isize { | |
| 737 | if (x) { | |
| 738 | error.ItBroke | |
| 739 | } else { | |
| 740 | 10 | |
| 741 | } | |
| 742 | } | |
| 743 | pub fn main(args: [][]u8) -> %void { | |
| 744 | const a = g(true) %% 3; | |
| 745 | const b = g(false) %% 3; | |
| 746 | if (a != 3) { | |
| 747 | %%io.stdout.printf("BAD\n"); | |
| 748 | } | |
| 749 | if (b != 10) { | |
| 750 | %%io.stdout.printf("BAD\n"); | |
| 751 | } | |
| 752 | %%io.stdout.printf("OK\n"); | |
| 753 | } | |
| 754 | )SOURCE", "OK\n"); | |
| 755 | ||
| 756 | 685 | add_simple_case("string concatenation", R"SOURCE( |
| 757 | 686 | const io = @import("std").io; |
| 758 | 687 | pub fn main(args: [][]u8) -> %void { |
| ... | ... | @@ -808,54 +737,6 @@ pub fn main(args: [][]u8) -> %void { |
| 808 | 737 | } |
| 809 | 738 | )SOURCE", "OK\n"); |
| 810 | 739 | |
| 811 | add_simple_case("unwrap simple value from error", R"SOURCE( | |
| 812 | const io = @import("std").io; | |
| 813 | fn do() -> %isize { | |
| 814 | 13 | |
| 815 | } | |
| 816 | ||
| 817 | pub fn main(args: [][]u8) -> %void { | |
| 818 | const i = %%do(); | |
| 819 | if (i != 13) { | |
| 820 | %%io.stdout.printf("BAD\n"); | |
| 821 | } | |
| 822 | %%io.stdout.printf("OK\n"); | |
| 823 | } | |
| 824 | )SOURCE", "OK\n"); | |
| 825 | ||
| 826 | add_simple_case("store member function in variable", R"SOURCE( | |
| 827 | const io = @import("std").io; | |
| 828 | struct Foo { | |
| 829 | x: i32, | |
| 830 | fn member(foo: Foo) -> i32 { foo.x } | |
| 831 | } | |
| 832 | pub fn main(args: [][]u8) -> %void { | |
| 833 | const instance = Foo { .x = 1234, }; | |
| 834 | const member_fn = Foo.member; | |
| 835 | const result = member_fn(instance); | |
| 836 | if (result != 1234) { | |
| 837 | %%io.stdout.printf("BAD\n"); | |
| 838 | } | |
| 839 | %%io.stdout.printf("OK\n"); | |
| 840 | } | |
| 841 | )SOURCE", "OK\n"); | |
| 842 | ||
| 843 | add_simple_case("call member function directly", R"SOURCE( | |
| 844 | const io = @import("std").io; | |
| 845 | struct Foo { | |
| 846 | x: i32, | |
| 847 | fn member(foo: Foo) -> i32 { foo.x } | |
| 848 | } | |
| 849 | pub fn main(args: [][]u8) -> %void { | |
| 850 | const instance = Foo { .x = 1234, }; | |
| 851 | const result = Foo.member(instance); | |
| 852 | if (result != 1234) { | |
| 853 | %%io.stdout.printf("BAD\n"); | |
| 854 | } | |
| 855 | %%io.stdout.printf("OK\n"); | |
| 856 | } | |
| 857 | )SOURCE", "OK\n"); | |
| 858 | ||
| 859 | 740 | add_simple_case("call result of if else expression", R"SOURCE( |
| 860 | 741 | const io = @import("std").io; |
| 861 | 742 | fn a() -> []u8 { "a\n" } |
| ... | ... | @@ -1496,7 +1377,8 @@ fn a(x: i32) { |
| 1496 | 1377 | struct Foo { |
| 1497 | 1378 | y: [get()]u8, |
| 1498 | 1379 | } |
| 1499 | fn get() -> isize { 1 } | |
| 1380 | var global_var: isize = 1; | |
| 1381 | fn get() -> isize { global_var } | |
| 1500 | 1382 | )SOURCE", 1, ".tmp_source.zig:3:9: error: unable to evaluate constant expression"); |
| 1501 | 1383 | |
| 1502 | 1384 | |
| ... | ... | @@ -1599,6 +1481,31 @@ fn foo() { |
| 1599 | 1481 | const pointer = &array[0]; |
| 1600 | 1482 | } |
| 1601 | 1483 | )SOURCE", 1, ".tmp_source.zig:4:27: error: out of bounds array access"); |
| 1484 | ||
| 1485 | add_compile_fail_case("compile time division by zero", R"SOURCE( | |
| 1486 | const x = foo(0); | |
| 1487 | fn foo(x: i32) -> i32 { | |
| 1488 | 1 / x | |
| 1489 | } | |
| 1490 | )SOURCE", 3, | |
| 1491 | ".tmp_source.zig:3:1: error: function evaluation caused division by zero", | |
| 1492 | ".tmp_source.zig:2:14: note: called from here", | |
| 1493 | ".tmp_source.zig:4:7: note: division by zero here"); | |
| 1494 | ||
| 1495 | add_compile_fail_case("branch on undefined value", R"SOURCE( | |
| 1496 | const x = if (undefined) true else false; | |
| 1497 | )SOURCE", 1, ".tmp_source.zig:2:15: error: branch on undefined value"); | |
| 1498 | ||
| 1499 | ||
| 1500 | add_compile_fail_case("endless loop in function evaluation", R"SOURCE( | |
| 1501 | const seventh_fib_number = fibbonaci(7); | |
| 1502 | fn fibbonaci(x: i32) -> i32 { | |
| 1503 | return fibbonaci(x - 1) + fibbonaci(x - 2); | |
| 1504 | } | |
| 1505 | )SOURCE", 3, | |
| 1506 | ".tmp_source.zig:3:1: error: function evaluation exceeded 1000 branches", | |
| 1507 | ".tmp_source.zig:2:37: note: called from here", | |
| 1508 | ".tmp_source.zig:4:40: note: quota exceeded here"); | |
| 1602 | 1509 | } |
| 1603 | 1510 | |
| 1604 | 1511 | ////////////////////////////////////////////////////////////////////////////// |
| ... | ... | @@ -1760,7 +1667,7 @@ extern void (*fn_ptr)(void); |
| 1760 | 1667 | )SOURCE", 2, |
| 1761 | 1668 | "pub extern var fn_ptr: ?extern fn();", |
| 1762 | 1669 | R"SOURCE(pub inline fn foo() { |
| 1763 | (??fn_ptr)() | |
| 1670 | (??fn_ptr)(); | |
| 1764 | 1671 | })SOURCE"); |
| 1765 | 1672 | |
| 1766 | 1673 |
test/self_hosted.zig+175-9| ... | ... | @@ -123,18 +123,18 @@ fn short_circuit() { |
| 123 | 123 | var hit_3 = false; |
| 124 | 124 | var hit_4 = false; |
| 125 | 125 | |
| 126 | if (true || { assert(false); false }) { | |
| 126 | if (true || {assert_runtime(false); false}) { | |
| 127 | 127 | hit_1 = true; |
| 128 | 128 | } |
| 129 | 129 | if (false || { hit_2 = true; false }) { |
| 130 | assert(false); | |
| 130 | assert_runtime(false); | |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | 133 | if (true && { hit_3 = true; false }) { |
| 134 | %%io.stdout.printf("BAD 3\n"); | |
| 134 | assert_runtime(false); | |
| 135 | 135 | } |
| 136 | if (false && { assert(false); false }) { | |
| 137 | assert(false); | |
| 136 | if (false && {assert_runtime(false); false}) { | |
| 137 | assert_runtime(false); | |
| 138 | 138 | } else { |
| 139 | 139 | hit_4 = true; |
| 140 | 140 | } |
| ... | ... | @@ -144,6 +144,11 @@ fn short_circuit() { |
| 144 | 144 | assert(hit_4); |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | #static_eval_enable(false) | |
| 148 | fn assert_runtime(b: bool) { | |
| 149 | if (!b) unreachable{} | |
| 150 | } | |
| 151 | ||
| 147 | 152 | #attribute("test") |
| 148 | 153 | fn modify_operators() { |
| 149 | 154 | var i : i32 = 0; |
| ... | ... | @@ -410,9 +415,7 @@ error err2; |
| 410 | 415 | |
| 411 | 416 | #attribute("test") |
| 412 | 417 | fn fn_call_of_struct_field() { |
| 413 | if (call_struct_field(Foo {.ptr = a_func,}) != 13) { | |
| 414 | unreachable{}; | |
| 415 | } | |
| 418 | assert(call_struct_field(Foo {.ptr = a_func,}) == 13); | |
| 416 | 419 | } |
| 417 | 420 | |
| 418 | 421 | struct Foo { |
| ... | ... | @@ -509,6 +512,7 @@ enum Fruit { |
| 509 | 512 | Orange, |
| 510 | 513 | Banana, |
| 511 | 514 | } |
| 515 | #static_eval_enable(false) | |
| 512 | 516 | fn non_const_switch_on_enum(fruit: Fruit) { |
| 513 | 517 | switch (fruit) { |
| 514 | 518 | Apple => unreachable{}, |
| ... | ... | @@ -521,6 +525,7 @@ fn non_const_switch_on_enum(fruit: Fruit) { |
| 521 | 525 | fn switch_statement() { |
| 522 | 526 | non_const_switch(SwitchStatmentFoo.C); |
| 523 | 527 | } |
| 528 | #static_eval_enable(false) | |
| 524 | 529 | fn non_const_switch(foo: SwitchStatmentFoo) { |
| 525 | 530 | const val: i32 = switch (foo) { |
| 526 | 531 | A => 1, |
| ... | ... | @@ -549,6 +554,7 @@ enum SwitchProngWithVarEnum { |
| 549 | 554 | Two: f32, |
| 550 | 555 | Meh, |
| 551 | 556 | } |
| 557 | #static_eval_enable(false) | |
| 552 | 558 | fn switch_prong_with_var_fn(a: SwitchProngWithVarEnum) { |
| 553 | 559 | switch(a) { |
| 554 | 560 | One => |x| { |
| ... | ... | @@ -569,6 +575,7 @@ fn err_return_in_assignment() { |
| 569 | 575 | %%do_err_return_in_assignment(); |
| 570 | 576 | } |
| 571 | 577 | |
| 578 | #static_eval_enable(false) | |
| 572 | 579 | fn do_err_return_in_assignment() -> %void { |
| 573 | 580 | var x : i32 = undefined; |
| 574 | 581 | x = %return make_a_non_err(); |
| ... | ... | @@ -608,7 +615,7 @@ fn explicit_cast_maybe_pointers() { |
| 608 | 615 | |
| 609 | 616 | #attribute("test") |
| 610 | 617 | fn const_expr_eval_on_single_expr_blocks() { |
| 611 | if (const_expr_eval_on_single_expr_blocks_fn(1, true) != 3) unreachable{} | |
| 618 | assert(const_expr_eval_on_single_expr_blocks_fn(1, true) == 3); | |
| 612 | 619 | } |
| 613 | 620 | |
| 614 | 621 | fn const_expr_eval_on_single_expr_blocks_fn(x: i32, b: bool) -> i32 { |
| ... | ... | @@ -736,6 +743,7 @@ fn generic_malloc_free() { |
| 736 | 743 | mem_free(u8)(a); |
| 737 | 744 | } |
| 738 | 745 | const some_mem : [100]u8 = undefined; |
| 746 | #static_eval_enable(false) | |
| 739 | 747 | fn mem_alloc(T: type)(n: isize) -> %[]T { |
| 740 | 748 | return (&T)(&some_mem[0])[0...n]; |
| 741 | 749 | } |
| ... | ... | @@ -789,6 +797,7 @@ var goto_counter: i32 = 0; |
| 789 | 797 | fn goto_leave_defer_scope() { |
| 790 | 798 | test_goto_leave_defer_scope(true); |
| 791 | 799 | } |
| 800 | #static_eval_enable(false) | |
| 792 | 801 | fn test_goto_leave_defer_scope(b: bool) { |
| 793 | 802 | var it_worked = false; |
| 794 | 803 | |
| ... | ... | @@ -820,3 +829,160 @@ fn cast_small_unsigned_to_larger_signed() { |
| 820 | 829 | } |
| 821 | 830 | fn cast_small_unsigned_to_larger_signed_1(x: u8) -> i16 { x } |
| 822 | 831 | fn cast_small_unsigned_to_larger_signed_2(x: u16) -> isize { x } |
| 832 | ||
| 833 | ||
| 834 | #attribute("test") | |
| 835 | fn implicit_cast_after_unreachable() { | |
| 836 | assert(outer() == 1234); | |
| 837 | } | |
| 838 | fn inner() -> i32 { 1234 } | |
| 839 | fn outer() -> isize { | |
| 840 | return inner(); | |
| 841 | } | |
| 842 | ||
| 843 | ||
| 844 | #attribute("test") | |
| 845 | fn else_if_expression() { | |
| 846 | assert(else_if_expression_f(1) == 1); | |
| 847 | } | |
| 848 | fn else_if_expression_f(c: u8) -> u8 { | |
| 849 | if (c == 0) { | |
| 850 | 0 | |
| 851 | } else if (c == 1) { | |
| 852 | 1 | |
| 853 | } else { | |
| 854 | 2 | |
| 855 | } | |
| 856 | } | |
| 857 | ||
| 858 | #attribute("test") | |
| 859 | fn err_binary_operator() { | |
| 860 | const a = err_binary_operator_g(true) %% 3; | |
| 861 | const b = err_binary_operator_g(false) %% 3; | |
| 862 | assert(a == 3); | |
| 863 | assert(b == 10); | |
| 864 | } | |
| 865 | error ItBroke; | |
| 866 | fn err_binary_operator_g(x: bool) -> %isize { | |
| 867 | if (x) { | |
| 868 | error.ItBroke | |
| 869 | } else { | |
| 870 | 10 | |
| 871 | } | |
| 872 | } | |
| 873 | ||
| 874 | #attribute("test") | |
| 875 | fn unwrap_simple_value_from_error() { | |
| 876 | const i = %%unwrap_simple_value_from_error_do(); | |
| 877 | assert(i == 13); | |
| 878 | } | |
| 879 | fn unwrap_simple_value_from_error_do() -> %isize { 13 } | |
| 880 | ||
| 881 | ||
| 882 | #attribute("test") | |
| 883 | fn store_member_function_in_variable() { | |
| 884 | const instance = MemberFnTestFoo { .x = 1234, }; | |
| 885 | const member_fn = MemberFnTestFoo.member; | |
| 886 | const result = member_fn(instance); | |
| 887 | assert(result == 1234); | |
| 888 | } | |
| 889 | struct MemberFnTestFoo { | |
| 890 | x: i32, | |
| 891 | fn member(foo: MemberFnTestFoo) -> i32 { foo.x } | |
| 892 | } | |
| 893 | ||
| 894 | #attribute("test") | |
| 895 | fn call_member_function_directly() { | |
| 896 | const instance = MemberFnTestFoo { .x = 1234, }; | |
| 897 | const result = MemberFnTestFoo.member(instance); | |
| 898 | assert(result == 1234); | |
| 899 | } | |
| 900 | ||
| 901 | #attribute("test") | |
| 902 | fn member_functions() { | |
| 903 | const r = MemberFnRand {.seed = 1234}; | |
| 904 | assert(r.get_seed() == 1234); | |
| 905 | } | |
| 906 | struct MemberFnRand { | |
| 907 | seed: u32, | |
| 908 | pub fn get_seed(r: MemberFnRand) -> u32 { | |
| 909 | r.seed | |
| 910 | } | |
| 911 | } | |
| 912 | ||
| 913 | #attribute("test") | |
| 914 | fn static_function_evaluation() { | |
| 915 | assert(statically_added_number == 3); | |
| 916 | } | |
| 917 | const statically_added_number = static_add(1, 2); | |
| 918 | fn static_add(a: i32, b: i32) -> i32 { a + b } | |
| 919 | ||
| 920 | ||
| 921 | #attribute("test") | |
| 922 | fn statically_initalized_list() { | |
| 923 | assert(static_point_list[0].x == 1); | |
| 924 | assert(static_point_list[0].y == 2); | |
| 925 | assert(static_point_list[1].x == 3); | |
| 926 | assert(static_point_list[1].y == 4); | |
| 927 | } | |
| 928 | struct Point { | |
| 929 | x: i32, | |
| 930 | y: i32, | |
| 931 | } | |
| 932 | const static_point_list = []Point { make_point(1, 2), make_point(3, 4) }; | |
| 933 | fn make_point(x: i32, y: i32) -> Point { | |
| 934 | return Point { | |
| 935 | .x = x, | |
| 936 | .y = y, | |
| 937 | }; | |
| 938 | } | |
| 939 | ||
| 940 | ||
| 941 | #attribute("test") | |
| 942 | fn static_eval_recursive() { | |
| 943 | assert(seventh_fib_number == 21); | |
| 944 | } | |
| 945 | const seventh_fib_number = fibbonaci(7); | |
| 946 | fn fibbonaci(x: i32) -> i32 { | |
| 947 | if (x <= 1) return 1; | |
| 948 | return fibbonaci(x - 1) + fibbonaci(x - 2); | |
| 949 | } | |
| 950 | ||
| 951 | #attribute("test") | |
| 952 | fn static_eval_while() { | |
| 953 | assert(static_eval_while_number == 1); | |
| 954 | } | |
| 955 | const static_eval_while_number = static_while_loop_1(); | |
| 956 | fn static_while_loop_1() -> i32 { | |
| 957 | return while_loop_2(); | |
| 958 | } | |
| 959 | fn static_while_loop_2() -> i32 { | |
| 960 | while (true) { | |
| 961 | return 1; | |
| 962 | } | |
| 963 | } | |
| 964 | ||
| 965 | #attribute("test") | |
| 966 | fn static_eval_list_init() { | |
| 967 | assert(static_vec3.data[2] == 1.0); | |
| 968 | } | |
| 969 | const static_vec3 = vec3(0.0, 0.0, 1.0); | |
| 970 | pub struct Vec3 { | |
| 971 | data: [3]f32, | |
| 972 | } | |
| 973 | pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { | |
| 974 | Vec3 { | |
| 975 | .data = []f32 { x, y, z, }, | |
| 976 | } | |
| 977 | } | |
| 978 | ||
| 979 | ||
| 980 | #attribute("test") | |
| 981 | fn generic_fn_with_implicit_cast() { | |
| 982 | assert(get_first_byte(u8)([]u8 {13}) == 13); | |
| 983 | assert(get_first_byte(u16)([]u16 {0, 13}) == 0); | |
| 984 | } | |
| 985 | fn get_byte(ptr: ?&u8) -> u8 {*??ptr} | |
| 986 | fn get_first_byte(T: type)(mem: []T) -> u8 { | |
| 987 | get_byte((&u8)(&mem[0])) | |
| 988 | } |