diff --git a/doc/langref.md b/doc/langref.md index b982ec0197..7cb02eef6d 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -3,9 +3,11 @@ ## Grammar ``` -Root = many(TopLevelDecl) "EOF" +Root = many(TopLevelItem) "EOF" -TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | ContainerDecl | GlobalVarDecl | ErrorValueDecl | TypeDecl | UseDecl) +TopLevelItem = ErrorValueDecl | Block | TopLevelDecl + +TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | ContainerDecl | GlobalVarDecl | TypeDecl | UseDecl) TypeDecl = "type" Symbol "=" TypeExpr ";" @@ -17,7 +19,7 @@ VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) "=" Expressi ContainerDecl = ("struct" | "enum" | "union") Symbol option(ParamDeclList) "{" many(StructMember) "}" -StructMember = many(Directive) option(VisibleMod) (StructField | FnDef | GlobalVarDecl | ContainerDecl) +StructMember = (StructField | FnDef | GlobalVarDecl | ContainerDecl) StructField = Symbol option(":" Expression) ",") @@ -25,9 +27,7 @@ UseDecl = "use" Expression ";" ExternDecl = "extern" (FnProto | VariableDeclaration) ";" -FnProto = "fn" option(Symbol) ParamDeclList option("->" TypeExpr) - -Directive = "#" Symbol "(" Expression ")" +FnProto = option("coldcc" | "nakedcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) VisibleMod = "pub" | "export" diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 02eacef28c..119d33bc4f 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -8,7 +8,7 @@ if exists("b:current_syntax") endif let b:current_syntax = "zig" -syn keyword zigStorage const var extern export pub noalias inline noinline +syn keyword zigStorage const var extern export pub noalias inline nakedcc coldcc syn keyword zigStructure struct enum union syn keyword zigStatement goto break return continue asm defer syn keyword zigConditional if else switch diff --git a/src/all_types.hpp b/src/all_types.hpp index d1826299e4..0f67f371ea 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -129,7 +129,6 @@ enum TldResolution { struct TopLevelDecl { // populated by parser Buf *name; - ZigList *directives; VisibMod visib_mod; // populated by semantic analyzer @@ -153,7 +152,6 @@ enum NodeType { NodeTypeFnDecl, NodeTypeParamDecl, NodeTypeBlock, - NodeTypeDirective, NodeTypeReturnExpr, NodeTypeDefer, NodeTypeVariableDeclaration, @@ -210,6 +208,8 @@ struct AstNodeFnProto { bool is_var_args; bool is_extern; bool is_inline; + bool is_coldcc; + bool is_nakedcc; // populated by semantic analyzer: @@ -459,11 +459,6 @@ struct AstNodeFieldAccessExpr { AstNode *container_init_expr_node; }; -struct AstNodeDirective { - Buf *name; - AstNode *expr; -}; - enum PrefixOp { PrefixOpInvalid, PrefixOpBoolNot, @@ -802,7 +797,6 @@ struct AstNode { AstNodeErrorValueDecl error_value_decl; AstNodeBinOpExpr bin_op_expr; AstNodeUnwrapErrorExpr unwrap_err_expr; - AstNodeDirective directive; AstNodePrefixOpExpr prefix_op_expr; AstNodeFnCallExpr fn_call_expr; AstNodeArrayAccessExpr array_access_expr; @@ -1109,10 +1103,14 @@ struct FnTableEntry { WantPure want_pure; AstNode *want_pure_attr_node; AstNode *want_pure_return_type; - bool safety_off; FnInline fn_inline; FnAnalState anal_state; + AstNode *fn_no_inline_set_node; + AstNode *fn_export_set_node; + AstNode *fn_test_set_node; + AstNode *fn_static_eval_set_node; + ZigList cast_alloca_list; ZigList struct_val_expr_alloca_list; ZigList variable_list; @@ -1154,6 +1152,11 @@ enum BuiltinFnId { BuiltinFnIdTruncate, BuiltinFnIdIntType, BuiltinFnIdUnreachable, + BuiltinFnIdSetFnTest, + BuiltinFnIdSetFnVisible, + BuiltinFnIdSetFnStaticEval, + BuiltinFnIdSetFnNoInline, + BuiltinFnIdSetDebugSafety, }; struct BuiltinFnEntry { @@ -1373,6 +1376,7 @@ struct BlockContext { bool codegen_excluded; bool safety_off; + AstNode *safety_set_node; }; enum AtomicOrder { diff --git a/src/analyze.cpp b/src/analyze.cpp index 93786ed15a..21f82c68ec 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -75,7 +75,6 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: - case NodeTypeDirective: case NodeTypeReturnExpr: case NodeTypeDefer: case NodeTypeVariableDeclaration: @@ -1123,6 +1122,28 @@ static bool resolve_const_expr_bool(CodeGen *g, ImportTableEntry *import, BlockC return true; } +static FnTableEntry *resolve_const_expr_fn(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode **node) +{ + TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr, *node); + + if (resolved_type->id == TypeTableEntryIdInvalid) { + return nullptr; + } else if (resolved_type->id == TypeTableEntryIdFn) { + ConstExprValue *const_val = &get_resolved_expr(*node)->const_val; + + if (!const_val->ok) { + add_node_error(g, *node, buf_sprintf("unable to evaluate constant expression")); + return nullptr; + } + + return const_val->data.x_fn; + } else { + add_node_error(g, *node, buf_sprintf("expected function, got '%s'", buf_ptr(&resolved_type->name))); + return nullptr; + } +} + static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, ImportTableEntry *import, BlockContext *containing_context) { @@ -1133,85 +1154,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t return; } - bool is_cold = false; - bool is_naked = false; - bool is_test = false; - bool is_noinline = false; - - if (fn_proto->top_level_decl.directives) { - for (size_t i = 0; i < fn_proto->top_level_decl.directives->length; i += 1) { - AstNode *directive_node = fn_proto->top_level_decl.directives->at(i); - Buf *name = directive_node->data.directive.name; - - if (buf_eql_str(name, "attribute")) { - if (fn_table_entry->fn_def_node) { - Buf *attr_name = resolve_const_expr_str(g, import, import->block_context, - &directive_node->data.directive.expr); - if (attr_name) { - if (buf_eql_str(attr_name, "naked")) { - is_naked = true; - } else if (buf_eql_str(attr_name, "noinline")) { - is_noinline = true; - } else if (buf_eql_str(attr_name, "cold")) { - is_cold = true; - } else if (buf_eql_str(attr_name, "test")) { - is_test = true; - g->test_fn_count += 1; - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); - } - } - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); - } - } else if (buf_eql_str(name, "debug_safety")) { - if (!fn_table_entry->fn_def_node) { - add_node_error(g, directive_node, - buf_sprintf("#debug_safety valid only on function definitions")); - } else { - bool enable; - bool ok = resolve_const_expr_bool(g, import, import->block_context, - &directive_node->data.directive.expr, &enable); - if (ok && !enable) { - fn_table_entry->safety_off = true; - } - } - } else if (buf_eql_str(name, "condition")) { - if (fn_proto->top_level_decl.visib_mod == VisibModExport) { - bool include; - bool ok = resolve_const_expr_bool(g, import, import->block_context, - &directive_node->data.directive.expr, &include); - if (ok && !include) { - fn_proto->top_level_decl.visib_mod = VisibModPub; - } - } else { - add_node_error(g, directive_node, - buf_sprintf("#condition valid only on exported symbols")); - } - } else if (buf_eql_str(name, "static_eval_enable")) { - if (!fn_table_entry->fn_def_node) { - add_node_error(g, directive_node, - buf_sprintf("#static_val_enable valid only on function definitions")); - } else { - bool enable; - bool ok = resolve_const_expr_bool(g, import, import->block_context, - &directive_node->data.directive.expr, &enable); - if (!ok || !enable) { - fn_table_entry->want_pure = WantPureFalse; - } else if (ok && enable) { - fn_table_entry->want_pure = WantPureTrue; - fn_table_entry->want_pure_attr_node = directive_node->data.directive.expr; - } - } - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); - } - } - } - bool is_internal = (fn_proto->top_level_decl.visib_mod != VisibModExport); bool is_c_compat = !is_internal || fn_proto->is_extern; fn_table_entry->internal_linkage = !is_c_compat; @@ -1219,24 +1161,17 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, containing_context, nullptr, node, - is_naked, is_cold, fn_table_entry); + fn_proto->is_nakedcc, fn_proto->is_coldcc, fn_table_entry); fn_table_entry->type_entry = fn_type; - fn_table_entry->is_test = is_test; if (fn_type->id == TypeTableEntryIdInvalid) { fn_proto->skip = true; return; } - if (fn_proto->is_inline && is_noinline) { - add_node_error(g, node, buf_sprintf("function is both inline and noinline")); - fn_proto->skip = true; - return; - } else if (fn_proto->is_inline) { + if (fn_proto->is_inline) { fn_table_entry->fn_inline = FnInlineAlways; - } else if (is_noinline) { - fn_table_entry->fn_inline = FnInlineNever; } @@ -1881,7 +1816,6 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) zig_panic("TODO resolve_top_level_decl NodeTypeUse"); break; case NodeTypeFnDef: - case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: @@ -2406,13 +2340,11 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { context->parent_loop_node = parent->parent_loop_node; context->c_import_buf = parent->c_import_buf; context->codegen_excluded = parent->codegen_excluded; - context->safety_off = parent->safety_off; } if (node && node->type == NodeTypeFnDef) { AstNode *fn_proto_node = node->data.fn_def.fn_proto; context->fn_entry = fn_proto_node->data.fn_proto.fn_table_entry; - context->safety_off = context->fn_entry->safety_off; } else if (parent) { context->fn_entry = parent->fn_entry; } @@ -5246,6 +5178,203 @@ static TypeTableEntry *analyze_int_type(CodeGen *g, ImportTableEntry *import, } +static TypeTableEntry *analyze_set_fn_test(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node) +{ + AstNode **fn_node = &node->data.fn_call_expr.params.at(0); + AstNode **value_node = &node->data.fn_call_expr.params.at(1); + + FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node); + if (!fn_entry) { + return g->builtin_types.entry_invalid; + } + + bool ok = resolve_const_expr_bool(g, import, context, value_node, &fn_entry->is_test); + if (!ok) { + return g->builtin_types.entry_invalid; + } + + if (fn_entry->fn_test_set_node) { + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function test attribute set twice")); + add_error_note(g, msg, fn_entry->fn_test_set_node, buf_sprintf("first set here")); + return g->builtin_types.entry_invalid; + } + fn_entry->fn_test_set_node = node; + + g->test_fn_count += 1; + return g->builtin_types.entry_void; +} + +static TypeTableEntry *analyze_set_fn_no_inline(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node) +{ + AstNode **fn_node = &node->data.fn_call_expr.params.at(0); + AstNode **value_node = &node->data.fn_call_expr.params.at(1); + + FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node); + if (!fn_entry) { + return g->builtin_types.entry_invalid; + } + + bool is_noinline; + bool ok = resolve_const_expr_bool(g, import, context, value_node, &is_noinline); + if (!ok) { + return g->builtin_types.entry_invalid; + } + + if (fn_entry->fn_no_inline_set_node) { + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function no inline attribute set twice")); + add_error_note(g, msg, fn_entry->fn_no_inline_set_node, buf_sprintf("first set here")); + return g->builtin_types.entry_invalid; + } + fn_entry->fn_no_inline_set_node = node; + + if (fn_entry->fn_inline == FnInlineAlways) { + add_node_error(g, node, buf_sprintf("function is both inline and noinline")); + fn_entry->proto_node->data.fn_proto.skip = true; + return g->builtin_types.entry_invalid; + } else if (is_noinline) { + fn_entry->fn_inline = FnInlineNever; + } + + return g->builtin_types.entry_void; +} + +static TypeTableEntry *analyze_set_fn_static_eval(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node) +{ + AstNode **fn_node = &node->data.fn_call_expr.params.at(0); + AstNode **value_node = &node->data.fn_call_expr.params.at(1); + + FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node); + if (!fn_entry) { + return g->builtin_types.entry_invalid; + } + + bool want_static_eval; + bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_static_eval); + if (!ok) { + return g->builtin_types.entry_invalid; + } + + if (fn_entry->fn_static_eval_set_node) { + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function static eval attribute set twice")); + add_error_note(g, msg, fn_entry->fn_static_eval_set_node, buf_sprintf("first set here")); + return g->builtin_types.entry_invalid; + } + fn_entry->fn_static_eval_set_node = node; + + if (want_static_eval && !context->fn_entry->is_pure) { + add_node_error(g, node, buf_sprintf("attribute appears too late within function")); + return g->builtin_types.entry_invalid; + } + + if (want_static_eval) { + fn_entry->want_pure = WantPureTrue; + fn_entry->want_pure_attr_node = node; + } else { + fn_entry->want_pure = WantPureFalse; + fn_entry->is_pure = false; + } + + return g->builtin_types.entry_void; +} + +static TypeTableEntry *analyze_set_fn_visible(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node) +{ + AstNode **fn_node = &node->data.fn_call_expr.params.at(0); + AstNode **value_node = &node->data.fn_call_expr.params.at(1); + + FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node); + if (!fn_entry) { + return g->builtin_types.entry_invalid; + } + + bool want_export; + bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_export); + if (!ok) { + return g->builtin_types.entry_invalid; + } + + if (fn_entry->fn_export_set_node) { + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function visibility set twice")); + add_error_note(g, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here")); + return g->builtin_types.entry_invalid; + } + fn_entry->fn_export_set_node = node; + + AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto; + if (fn_proto->top_level_decl.visib_mod != VisibModExport) { + ErrorMsg *msg = add_node_error(g, node, + buf_sprintf("function must be marked export to set function visibility")); + add_error_note(g, msg, fn_entry->proto_node, buf_sprintf("function declared here")); + return g->builtin_types.entry_void; + } + if (!want_export) { + fn_proto->top_level_decl.visib_mod = VisibModPub; + } + + return g->builtin_types.entry_void; +} + +static TypeTableEntry *analyze_set_debug_safety(CodeGen *g, ImportTableEntry *import, + BlockContext *parent_context, AstNode *node) +{ + AstNode **target_node = &node->data.fn_call_expr.params.at(0); + AstNode **value_node = &node->data.fn_call_expr.params.at(1); + + TypeTableEntry *target_type = analyze_expression(g, import, parent_context, nullptr, *target_node); + BlockContext *target_context; + ConstExprValue *const_val = &get_resolved_expr(*target_node)->const_val; + if (target_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } + if (!const_val->ok) { + add_node_error(g, *target_node, buf_sprintf("unable to evaluate constant expression")); + return g->builtin_types.entry_invalid; + } + if (target_type->id == TypeTableEntryIdBlock) { + target_context = const_val->data.x_block; + } else if (target_type->id == TypeTableEntryIdFn) { + target_context = const_val->data.x_fn->fn_def_node->data.fn_def.block_context; + } else if (target_type->id == TypeTableEntryIdMetaType) { + TypeTableEntry *type_arg = const_val->data.x_type; + if (type_arg->id == TypeTableEntryIdStruct) { + target_context = type_arg->data.structure.block_context; + } else if (type_arg->id == TypeTableEntryIdEnum) { + target_context = type_arg->data.enumeration.block_context; + } else if (type_arg->id == TypeTableEntryIdUnion) { + target_context = type_arg->data.unionation.block_context; + } else { + add_node_error(g, *target_node, + buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name))); + return g->builtin_types.entry_invalid; + } + } else { + add_node_error(g, *target_node, + buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name))); + return g->builtin_types.entry_invalid; + } + + bool want_debug_safety; + bool ok = resolve_const_expr_bool(g, import, parent_context, value_node, &want_debug_safety); + if (!ok) { + return g->builtin_types.entry_invalid; + } + + if (target_context->safety_set_node) { + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("debug safety for scope set twice")); + add_error_note(g, msg, target_context->safety_set_node, buf_sprintf("first set here")); + return g->builtin_types.entry_invalid; + } + target_context->safety_set_node = node; + + target_context->safety_off = !want_debug_safety; + + return g->builtin_types.entry_void; +} + static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -5607,6 +5736,16 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry return analyze_int_type(g, import, context, node); case BuiltinFnIdUnreachable: return g->builtin_types.entry_unreachable; + case BuiltinFnIdSetFnTest: + return analyze_set_fn_test(g, import, context, node); + case BuiltinFnIdSetFnNoInline: + return analyze_set_fn_no_inline(g, import, context, node); + case BuiltinFnIdSetFnStaticEval: + return analyze_set_fn_static_eval(g, import, context, node); + case BuiltinFnIdSetFnVisible: + return analyze_set_fn_visible(g, import, context, node); + case BuiltinFnIdSetDebugSafety: + return analyze_set_debug_safety(g, import, context, node); } zig_unreachable(); } @@ -6876,7 +7015,6 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn break; case NodeTypeSwitchProng: case NodeTypeSwitchRange: - case NodeTypeDirective: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeRoot: @@ -7109,7 +7247,6 @@ static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *conte // error value declarations do not depend on other top level decls preview_error_value_decl(g, node); break; - case NodeTypeDirective: case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: @@ -7418,7 +7555,6 @@ Expr *get_resolved_expr(AstNode *node) { case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeDirective: case NodeTypeUse: case NodeTypeContainerDecl: case NodeTypeStructField: @@ -7469,7 +7605,6 @@ static TopLevelDecl *get_as_top_level_decl(AstNode *node) { case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: - case NodeTypeDirective: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeSymbol: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 81429586b5..1a8463e85d 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -141,8 +141,6 @@ static const char *node_type_str(NodeType node_type) { return "ArrayAccessExpr"; case NodeTypeSliceExpr: return "SliceExpr"; - case NodeTypeDirective: - return "Directive"; case NodeTypeReturnExpr: return "ReturnExpr"; case NodeTypeDefer: @@ -416,13 +414,6 @@ static void render_node(AstRender *ar, AstNode *node) { } case NodeTypeFnDef: { - ZigList *directives = - node->data.fn_def.fn_proto->data.fn_proto.top_level_decl.directives; - if (directives) { - for (size_t i = 0; i < directives->length; i += 1) { - render_node(ar, directives->at(i)); - } - } render_node(ar, node->data.fn_def.fn_proto); fprintf(ar->f, " "); render_node(ar, node->data.fn_def.body); @@ -445,11 +436,6 @@ static void render_node(AstRender *ar, AstNode *node) { print_indent(ar); fprintf(ar->f, "}"); break; - case NodeTypeDirective: - fprintf(ar->f, "#%s(", buf_ptr(node->data.directive.name)); - render_node(ar, node->data.directive.expr); - fprintf(ar->f, ")\n"); - break; case NodeTypeReturnExpr: { const char *return_str = return_string(node->data.return_expr.kind); diff --git a/src/codegen.cpp b/src/codegen.cpp index 956b5be0a6..296a9998c4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -352,8 +352,20 @@ static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValue } } +static bool want_debug_safety_recursive(CodeGen *g, BlockContext *context) { + if (context->safety_set_node || !context->parent) { + return !context->safety_off; + } + context->safety_off = want_debug_safety_recursive(g, context->parent); + context->safety_set_node = context->parent->safety_set_node; + return !context->safety_off; +} + static bool want_debug_safety(CodeGen *g, AstNode *node) { - return !g->is_release_build && !node->block_context->safety_off; + if (g->is_release_build) { + return false; + } + return want_debug_safety_recursive(g, node->block_context); } static void gen_debug_safety_crash(CodeGen *g) { @@ -709,6 +721,13 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { return gen_truncate(g, node); case BuiltinFnIdUnreachable: return gen_unreachable(g, node); + case BuiltinFnIdSetFnTest: + case BuiltinFnIdSetFnVisible: + case BuiltinFnIdSetFnStaticEval: + case BuiltinFnIdSetFnNoInline: + case BuiltinFnIdSetDebugSafety: + // do nothing + return nullptr; } zig_unreachable(); } @@ -3617,7 +3636,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeDirective: case NodeTypeUse: case NodeTypeContainerDecl: case NodeTypeStructField: @@ -4880,6 +4898,11 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdIntType, "intType", 2); create_builtin_fn_with_arg_count(g, BuiltinFnIdUnreachable, "unreachable", 0); + create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnTest, "setFnTest", 2); + create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnVisible, "setFnVisible", 2); + create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnStaticEval, "setFnStaticEval", 2); + create_builtin_fn_with_arg_count(g, BuiltinFnIdSetFnNoInline, "setFnNoInline", 2); + create_builtin_fn_with_arg_count(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2); } static void init(CodeGen *g, Buf *source_path) { diff --git a/src/eval.cpp b/src/eval.cpp index 3677a14f6a..b74fb38af9 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -963,6 +963,12 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_ case BuiltinFnIdCompileErr: case BuiltinFnIdIntType: zig_unreachable(); + case BuiltinFnIdSetFnTest: + case BuiltinFnIdSetFnVisible: + case BuiltinFnIdSetFnStaticEval: + case BuiltinFnIdSetFnNoInline: + case BuiltinFnIdSetDebugSafety: + return false; } return false; @@ -1398,7 +1404,6 @@ static bool eval_expr(EvalFn *ef, AstNode *node, ConstExprValue *out) { case NodeTypeUse: case NodeTypeAsmExpr: case NodeTypeParamDecl: - case NodeTypeDirective: case NodeTypeTypeDecl: zig_unreachable(); } diff --git a/src/parseh.cpp b/src/parseh.cpp index 307129d4a0..1a581f3a23 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -124,7 +124,6 @@ static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char node->data.variable_declaration.is_const = is_const; node->data.variable_declaration.top_level_decl.visib_mod = c->visib_mod; node->data.variable_declaration.expr = init_node; - node->data.variable_declaration.top_level_decl.directives = nullptr; node->data.variable_declaration.type = type_node; normalize_parent_ptrs(node); return node; diff --git a/src/parser.cpp b/src/parser.cpp index 52d3a855d0..6ab0024433 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -211,8 +211,7 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, size_t *token_index, bool ma static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory); static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory); static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, - ZigList *directives, VisibMod visib_mod); +static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod); static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index); static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory); @@ -233,39 +232,6 @@ static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token return token; } -/* -Directive = "#" "Symbol" "(" Expression ")" -*/ -static AstNode *ast_parse_directive(ParseContext *pc, size_t *token_index) { - Token *number_sign = ast_eat_token(pc, token_index, TokenIdNumberSign); - - AstNode *node = ast_create_node(pc, NodeTypeDirective, number_sign); - - Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol); - - node->data.directive.name = token_buf(name_symbol); - - node->data.directive.expr = ast_parse_grouped_expr(pc, token_index, true); - - normalize_parent_ptrs(node); - return node; -} - -static void ast_parse_directives(ParseContext *pc, size_t *token_index, - ZigList *directives) -{ - for (;;) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdNumberSign) { - AstNode *directive_node = ast_parse_directive(pc, token_index); - directives->append(directive_node); - } else { - return; - } - } - zig_unreachable(); -} - /* TypeExpr = PrefixOpExpression | "var" */ @@ -686,7 +652,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo return node; } else if (token->id == TokenIdKeywordExtern) { *token_index += 1; - AstNode *node = ast_parse_fn_proto(pc, token_index, true, nullptr, VisibModPrivate); + AstNode *node = ast_parse_fn_proto(pc, token_index, true, VisibModPrivate); node->data.fn_proto.is_extern = true; return node; } else if (token->id == TokenIdAtSign) { @@ -735,7 +701,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo return array_type_node; } - AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, nullptr, VisibModPrivate); + AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, VisibModPrivate); if (fn_proto_node) { return fn_proto_node; } @@ -1487,7 +1453,7 @@ static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) { VariableDeclaration : ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) */ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory, - ZigList *directives, VisibMod visib_mod) + VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); @@ -1509,7 +1475,6 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to node->data.variable_declaration.is_const = is_const; node->data.variable_declaration.top_level_decl.visib_mod = visib_mod; - node->data.variable_declaration.top_level_decl.directives = directives; Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); node->data.variable_declaration.symbol = token_buf(name_token); @@ -1985,8 +1950,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand if (statement_node) { semicolon_expected = false; } else { - statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, - nullptr, VisibModPrivate); + statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate); if (!statement_node) { statement_node = ast_parse_defer_expr(pc, token_index); } @@ -2023,25 +1987,35 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand } /* -FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr) +FnProto = option("coldcc" | "nakedcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) */ -static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); + Token *fn_token; - if (first_token->id != TokenIdKeywordFn) { - if (mandatory) { - ast_expect_token(pc, first_token, TokenIdKeywordFn); - } else { - return nullptr; - } + bool is_coldcc = false; + bool is_nakedcc = false; + if (first_token->id == TokenIdKeywordColdCC) { + *token_index += 1; + fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); + is_coldcc = true; + } else if (first_token->id == TokenIdKeywordNakedCC) { + *token_index += 1; + fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); + is_nakedcc = true; + } else if (first_token->id == TokenIdKeywordFn) { + fn_token = first_token; + *token_index += 1; + } else if (mandatory) { + ast_expect_token(pc, first_token, TokenIdKeywordFn); + } else { + return nullptr; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeFnProto, first_token); + AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token); node->data.fn_proto.top_level_decl.visib_mod = visib_mod; - node->data.fn_proto.top_level_decl.directives = directives; + node->data.fn_proto.is_coldcc = is_coldcc; + node->data.fn_proto.is_nakedcc = is_nakedcc; Token *fn_name = &pc->tokens->at(*token_index); if (fn_name->id == TokenIdSymbol) { @@ -2068,9 +2042,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m /* FnDef = option("inline" | "extern") FnProto Block */ -static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); bool is_inline; bool is_extern; @@ -2087,7 +2059,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool man is_extern = false; } - AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, directives, visib_mod); + AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod); if (!fn_proto) { if (is_inline || is_extern) { *token_index -= 1; @@ -2115,9 +2087,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool man /* ExternDecl = "extern" (FnProto | VariableDeclaration) ";" */ -static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, bool mandatory, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *extern_kw = &pc->tokens->at(*token_index); if (extern_kw->id != TokenIdKeywordExtern) { if (mandatory) { @@ -2128,7 +2098,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo } *token_index += 1; - AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, directives, visib_mod); + AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, visib_mod); if (fn_proto_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); @@ -2138,7 +2108,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo return fn_proto_node; } - AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, directives, visib_mod); + AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod); if (var_decl_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); @@ -2155,9 +2125,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo /* UseDecl = "use" Expression ";" */ -static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod visib_mod) { Token *use_kw = &pc->tokens->at(*token_index); if (use_kw->id != TokenIdKeywordUse) return nullptr; @@ -2165,7 +2133,6 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); node->data.use.top_level_decl.visib_mod = visib_mod; - node->data.use.top_level_decl.directives = directives; node->data.use.expr = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdSemicolon); @@ -2175,13 +2142,11 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, } /* -ContainerDecl = ("struct" | "enum" | "union") "Symbol" option(ParamDeclList) "{" many(StructMember) "}" -StructMember = many(Directive) option(VisibleMod) (StructField | FnDef | GlobalVarDecl | ContainerDecl) -StructField : "Symbol" option(":" Expression) ",") +ContainerDecl = ("struct" | "enum" | "union") Symbol option(ParamDeclList) "{" many(StructMember) "}" +StructMember = (StructField | FnDef | GlobalVarDecl | ContainerDecl) +StructField = Symbol option(":" Expression) ",") */ -static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); ContainerKind kind; @@ -2203,7 +2168,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, node->data.struct_decl.kind = kind; node->data.struct_decl.name = token_buf(struct_name); node->data.struct_decl.top_level_decl.visib_mod = visib_mod; - node->data.struct_decl.top_level_decl.directives = directives; Token *paren_or_brace = &pc->tokens->at(*token_index); if (paren_or_brace->id == TokenIdLParen) { @@ -2217,10 +2181,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, } for (;;) { - Token *directive_token = &pc->tokens->at(*token_index); - ZigList *directive_list = allocate>(1); - ast_parse_directives(pc, token_index, directive_list); - Token *visib_tok = &pc->tokens->at(*token_index); VisibMod visib_mod; if (visib_tok->id == TokenIdKeywordPub) { @@ -2233,20 +2193,20 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, visib_mod = VisibModPrivate; } - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, directive_list, visib_mod); + AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod); if (fn_def_node) { node->data.struct_decl.decls.append(fn_def_node); continue; } - AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, directive_list, visib_mod); + AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod); if (var_decl_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); node->data.struct_decl.decls.append(var_decl_node); continue; } - AstNode *container_decl_node = ast_parse_container_decl(pc, token_index, directive_list, visib_mod); + AstNode *container_decl_node = ast_parse_container_decl(pc, token_index, visib_mod); if (container_decl_node) { node->data.struct_decl.decls.append(container_decl_node); continue; @@ -2255,10 +2215,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdRBrace) { - if (directive_list->length > 0) { - ast_error(pc, directive_token, "invalid directive"); - } - *token_index += 1; break; } else if (token->id == TokenIdSymbol) { @@ -2266,7 +2222,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, *token_index += 1; field_node->data.struct_field.top_level_decl.visib_mod = visib_mod; - field_node->data.struct_field.top_level_decl.directives = directive_list; field_node->data.struct_field.name = token_buf(token); Token *expr_or_comma = &pc->tokens->at(*token_index); @@ -2293,9 +2248,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, /* ErrorValueDecl : "error" "Symbol" ";" */ -static AstNode *ast_parse_error_value_decl(ParseContext *pc, size_t *token_index, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_error_value_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); if (first_token->id != TokenIdKeywordError) { @@ -2308,7 +2261,6 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, size_t *token_index AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token); node->data.error_value_decl.top_level_decl.visib_mod = visib_mod; - node->data.error_value_decl.top_level_decl.directives = directives; node->data.error_value_decl.name = token_buf(name_tok); normalize_parent_ptrs(node); @@ -2318,9 +2270,7 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, size_t *token_index /* TypeDecl = "type" "Symbol" "=" TypeExpr ";" */ -static AstNode *ast_parse_type_decl(ParseContext *pc, size_t *token_index, - ZigList *directives, VisibMod visib_mod) -{ +static AstNode *ast_parse_type_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); if (first_token->id != TokenIdKeywordType) { @@ -2338,21 +2288,17 @@ static AstNode *ast_parse_type_decl(ParseContext *pc, size_t *token_index, ast_eat_token(pc, token_index, TokenIdSemicolon); node->data.type_decl.top_level_decl.visib_mod = visib_mod; - node->data.type_decl.top_level_decl.directives = directives; normalize_parent_ptrs(node); return node; } /* -TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl) +TopLevelItem = ErrorValueDecl | Block | TopLevelDecl +TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | ContainerDecl | GlobalVarDecl | TypeDecl | UseDecl) */ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, ZigList *top_level_decls) { for (;;) { - Token *directive_token = &pc->tokens->at(*token_index); - ZigList *directives = allocate>(1); - ast_parse_directives(pc, token_index, directives); - Token *visib_tok = &pc->tokens->at(*token_index); VisibMod visib_mod; if (visib_tok->id == TokenIdKeywordPub) { @@ -2365,61 +2311,56 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig visib_mod = VisibModPrivate; } - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, directives, visib_mod); + AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod); if (fn_def_node) { top_level_decls->append(fn_def_node); continue; } - AstNode *fn_proto_node = ast_parse_extern_decl(pc, token_index, false, directives, visib_mod); + AstNode *fn_proto_node = ast_parse_extern_decl(pc, token_index, false, visib_mod); if (fn_proto_node) { top_level_decls->append(fn_proto_node); continue; } - AstNode *use_node = ast_parse_use(pc, token_index, directives, visib_mod); + AstNode *use_node = ast_parse_use(pc, token_index, visib_mod); if (use_node) { top_level_decls->append(use_node); continue; } - AstNode *struct_node = ast_parse_container_decl(pc, token_index, directives, visib_mod); + AstNode *struct_node = ast_parse_container_decl(pc, token_index, visib_mod); if (struct_node) { top_level_decls->append(struct_node); continue; } - AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, - directives, visib_mod); + AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod); if (var_decl_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); top_level_decls->append(var_decl_node); continue; } - AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, directives, visib_mod); + AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, visib_mod); if (error_value_node) { top_level_decls->append(error_value_node); continue; } - AstNode *type_decl_node = ast_parse_type_decl(pc, token_index, directives, visib_mod); + AstNode *type_decl_node = ast_parse_type_decl(pc, token_index, visib_mod); if (type_decl_node) { top_level_decls->append(type_decl_node); continue; } - if (directives->length > 0) { - ast_error(pc, directive_token, "invalid directive"); - } - return; } zig_unreachable(); } /* -Root : many(TopLevelDecl) token(EOF) +Root = many(TopLevelItem) "EOF" */ static AstNode *ast_parse_root(ParseContext *pc, size_t *token_index) { AstNode *node = ast_create_node(pc, NodeTypeRoot, &pc->tokens->at(*token_index)); @@ -2471,7 +2412,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont break; case NodeTypeFnProto: visit_field(&node->data.fn_proto.return_type, visit, context); - visit_node_list(node->data.fn_proto.top_level_decl.directives, visit, context); visit_node_list(&node->data.fn_proto.params, visit, context); break; case NodeTypeFnDef: @@ -2487,9 +2427,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeBlock: visit_node_list(&node->data.block.statements, visit, context); break; - case NodeTypeDirective: - visit_field(&node->data.directive.expr, visit, context); - break; case NodeTypeReturnExpr: visit_field(&node->data.return_expr.expr, visit, context); break; @@ -2497,12 +2434,10 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.defer.expr, visit, context); break; case NodeTypeVariableDeclaration: - visit_node_list(node->data.variable_declaration.top_level_decl.directives, visit, context); visit_field(&node->data.variable_declaration.type, visit, context); visit_field(&node->data.variable_declaration.expr, visit, context); break; case NodeTypeTypeDecl: - visit_node_list(node->data.type_decl.top_level_decl.directives, visit, context); visit_field(&node->data.type_decl.child_type, visit, context); break; case NodeTypeErrorValueDecl: @@ -2550,7 +2485,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont break; case NodeTypeUse: visit_field(&node->data.use.expr, visit, context); - visit_node_list(node->data.use.top_level_decl.directives, visit, context); break; case NodeTypeBoolLiteral: // none @@ -2626,11 +2560,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeContainerDecl: visit_node_list(&node->data.struct_decl.fields, visit, context); visit_node_list(&node->data.struct_decl.decls, visit, context); - visit_node_list(node->data.struct_decl.top_level_decl.directives, visit, context); break; case NodeTypeStructField: visit_field(&node->data.struct_field.type, visit, context); - visit_node_list(node->data.struct_field.top_level_decl.directives, visit, context); break; case NodeTypeContainerInitExpr: visit_field(&node->data.container_init_expr.type, visit, context); @@ -2688,16 +2620,6 @@ static void clone_subtree_list_omit_inline_params(ZigList *dest, ZigL } } -static void clone_subtree_list_ptr(ZigList **dest_ptr, ZigList *src, - uint32_t *next_node_index) -{ - if (src) { - ZigList *dest = allocate>(1); - *dest_ptr = dest; - clone_subtree_list(dest, src, next_node_index); - } -} - static void clone_subtree_field_special(AstNode **dest, AstNode *src, uint32_t *next_node_index, enum AstCloneSpecial special) { @@ -2713,10 +2635,6 @@ static void clone_subtree_field(AstNode **dest, AstNode *src, uint32_t *next_nod return clone_subtree_field_special(dest, src, next_node_index, AstCloneSpecialNone); } -static void clone_subtree_tld(TopLevelDecl *dest, TopLevelDecl *src, uint32_t *next_node_index) { - clone_subtree_list_ptr(&dest->directives, src->directives, next_node_index); -} - AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, enum AstCloneSpecial special) { AstNode *new_node = allocate_nonzero(1); safe_memcpy(new_node, old_node, 1); @@ -2730,8 +2648,6 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, &old_node->data.root.top_level_decls, next_node_index); break; case NodeTypeFnProto: - clone_subtree_tld(&new_node->data.fn_proto.top_level_decl, &old_node->data.fn_proto.top_level_decl, - next_node_index); clone_subtree_field(&new_node->data.fn_proto.return_type, old_node->data.fn_proto.return_type, next_node_index); @@ -2761,9 +2677,6 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, clone_subtree_list(&new_node->data.block.statements, &old_node->data.block.statements, next_node_index); break; - case NodeTypeDirective: - clone_subtree_field(&new_node->data.directive.expr, old_node->data.directive.expr, next_node_index); - break; case NodeTypeReturnExpr: clone_subtree_field(&new_node->data.return_expr.expr, old_node->data.return_expr.expr, next_node_index); break; @@ -2771,14 +2684,10 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, clone_subtree_field(&new_node->data.defer.expr, old_node->data.defer.expr, next_node_index); break; case NodeTypeVariableDeclaration: - clone_subtree_list_ptr(&new_node->data.variable_declaration.top_level_decl.directives, - old_node->data.variable_declaration.top_level_decl.directives, next_node_index); clone_subtree_field(&new_node->data.variable_declaration.type, old_node->data.variable_declaration.type, next_node_index); clone_subtree_field(&new_node->data.variable_declaration.expr, old_node->data.variable_declaration.expr, next_node_index); break; case NodeTypeTypeDecl: - clone_subtree_list_ptr(&new_node->data.type_decl.top_level_decl.directives, - old_node->data.type_decl.top_level_decl.directives, next_node_index); clone_subtree_field(&new_node->data.type_decl.child_type, old_node->data.type_decl.child_type, next_node_index); break; case NodeTypeErrorValueDecl: @@ -2832,8 +2741,6 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, break; case NodeTypeUse: clone_subtree_field(&new_node->data.use.expr, old_node->data.use.expr, next_node_index); - clone_subtree_list_ptr(&new_node->data.use.top_level_decl.directives, - old_node->data.use.top_level_decl.directives, next_node_index); break; case NodeTypeBoolLiteral: // none @@ -2908,13 +2815,9 @@ AstNode *ast_clone_subtree_special(AstNode *old_node, uint32_t *next_node_index, next_node_index); clone_subtree_list(&new_node->data.struct_decl.decls, &old_node->data.struct_decl.decls, next_node_index); - clone_subtree_list_ptr(&new_node->data.struct_decl.top_level_decl.directives, - old_node->data.struct_decl.top_level_decl.directives, next_node_index); break; case NodeTypeStructField: clone_subtree_field(&new_node->data.struct_field.type, old_node->data.struct_field.type, next_node_index); - clone_subtree_list_ptr(&new_node->data.struct_field.top_level_decl.directives, - old_node->data.struct_field.top_level_decl.directives, next_node_index); break; case NodeTypeContainerInitExpr: clone_subtree_field(&new_node->data.container_init_expr.type, old_node->data.container_init_expr.type, next_node_index); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 62c4ea1f4c..3af876d074 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -109,6 +109,7 @@ struct ZigKeyword { static const struct ZigKeyword zig_keywords[] = { {"asm", TokenIdKeywordAsm}, {"break", TokenIdKeywordBreak}, + {"coldcc", TokenIdKeywordColdCC}, {"const", TokenIdKeywordConst}, {"continue", TokenIdKeywordContinue}, {"defer", TokenIdKeywordDefer}, @@ -123,6 +124,7 @@ static const struct ZigKeyword zig_keywords[] = { {"goto", TokenIdKeywordGoto}, {"if", TokenIdKeywordIf}, {"inline", TokenIdKeywordInline}, + {"nakedcc", TokenIdKeywordNakedCC}, {"noalias", TokenIdKeywordNoAlias}, {"null", TokenIdKeywordNull}, {"pub", TokenIdKeywordPub}, @@ -1476,6 +1478,8 @@ const char * token_name(TokenId id) { case TokenIdKeywordType: return "type"; case TokenIdKeywordInline: return "inline"; case TokenIdKeywordDefer: return "defer"; + case TokenIdKeywordColdCC: return "coldcc"; + case TokenIdKeywordNakedCC: return "nakedcc"; case TokenIdLParen: return "("; case TokenIdRParen: return ")"; case TokenIdComma: return ","; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index f67828b7cc..48f41b6ea7 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -46,6 +46,8 @@ enum TokenId { TokenIdKeywordInline, TokenIdKeywordDefer, TokenIdKeywordThis, + TokenIdKeywordColdCC, + TokenIdKeywordNakedCC, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/std/bootstrap.zig b/std/bootstrap.zig index 2a2d1148dd..adb263ede6 100644 --- a/std/bootstrap.zig +++ b/std/bootstrap.zig @@ -13,9 +13,9 @@ const want_main_symbol = !want_start_symbol; var argc: usize = undefined; var argv: &&u8 = undefined; -#attribute("naked") -#condition(want_start_symbol) -export fn _start() -> unreachable { +export nakedcc fn _start() -> unreachable { + @setFnVisible(this, want_start_symbol); + switch (@compileVar("arch")) { x86_64 => { argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> usize)); @@ -44,8 +44,9 @@ fn callMainAndExit() -> unreachable { linux.exit(0); } -#condition(want_main_symbol) export fn main(c_argc: i32, c_argv: &&u8) -> i32 { + @setFnVisible(this, want_main_symbol); + argc = usize(c_argc); argv = c_argv; callMain() %% return 1; diff --git a/std/builtin.zig b/std/builtin.zig index cc4cbbcafc..5fe596eb33 100644 --- a/std/builtin.zig +++ b/std/builtin.zig @@ -1,8 +1,9 @@ // These functions are provided when not linking against libc because LLVM // sometimes generates code that calls them. -#debug_safety(false) export fn memset(dest: &u8, c: u8, n: usize) -> &u8 { + @setDebugSafety(this, false); + var index: usize = 0; while (index != n) { dest[index] = c; @@ -11,8 +12,9 @@ export fn memset(dest: &u8, c: u8, n: usize) -> &u8 { return dest; } -#debug_safety(false) export fn memcpy(noalias dest: &u8, noalias src: &const u8, n: usize) -> &u8 { + @setDebugSafety(this, false); + var index: usize = 0; while (index != n) { dest[index] = src[index]; diff --git a/std/compiler_rt.zig b/std/compiler_rt.zig index a227d45a43..b27da3b3de 100644 --- a/std/compiler_rt.zig +++ b/std/compiler_rt.zig @@ -8,18 +8,19 @@ const udwords = [2]su_int; const low = if (@compileVar("is_big_endian")) 1 else 0; const high = 1 - low; -#debug_safety(false) export fn __udivdi3(a: du_int, b: du_int) -> du_int { + @setDebugSafety(this, false); return __udivmoddi4(a, b, null); } -#debug_safety(false) fn du_int_to_udwords(x: du_int) -> udwords { + @setDebugSafety(this, false); return *(&udwords)(&x); } -#debug_safety(false) export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { + @setDebugSafety(this, false); + const n_uword_bits = @sizeOf(su_int) * CHAR_BIT; const n_udword_bits = @sizeOf(du_int) * CHAR_BIT; var n = du_int_to_udwords(a); @@ -203,15 +204,17 @@ export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { return *(&du_int)(&q[0]); } -#debug_safety(false) export fn __umoddi3(a: du_int, b: du_int) -> du_int { + @setDebugSafety(this, false); + var r: du_int = undefined; __udivmoddi4(a, b, &r); return r; } -#attribute("test") fn test_umoddi3() { + @setFnTest(this, true); + test_one_umoddi3(0, 1, 0); test_one_umoddi3(2, 1, 0); test_one_umoddi3(0x8000000000000000, 1, 0x0); @@ -224,8 +227,9 @@ fn test_one_umoddi3(a: du_int, b: du_int, expected_r: du_int) { assert(r == expected_r); } -#attribute("test") fn test_udivmoddi4() { + @setFnTest(this, true); + const cases = [][4]du_int { []du_int{0x0000000000000000, 0x0000000000000001, 0x0000000000000000, 0x0000000000000000}, []du_int{0x0000000080000000, 0x0000000100000001, 0x0000000000000000, 0x0000000080000000}, diff --git a/std/cstr.zig b/std/cstr.zig index e5035aec4b..afe8d5b404 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -126,8 +126,9 @@ pub struct CBuf { } } -#attribute("test") fn testSimpleCBuf() { + @setFnTest(this, true); + var buf = %%CBuf.initEmpty(&debug.global_allocator); assert(buf.len() == 0); %%buf.appendCStr(c"hello"); @@ -146,12 +147,14 @@ fn testSimpleCBuf() { assert(buf.startsWithCBuf(&buf2)); } -#attribute("test") fn testCompileTimeStrCmp() { + @setFnTest(this, true); + assert(@constEval(cmp(c"aoeu", c"aoez") == -1)); } -#attribute("test") fn testCompileTimeStrLen() { + @setFnTest(this, true); + assert(@constEval(len(c"123456789") == 9)); } diff --git a/std/hash_map.zig b/std/hash_map.zig index c20d78c202..1428954220 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -230,8 +230,9 @@ pub struct SmallHashMap(K: type, V: type, hash: fn(key: K)->u32, eql: fn(a: K, b } } -#attribute("test") fn basicHashMapTest() { + @setFnTest(this, true); + var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined; map.init(&debug.global_allocator); defer map.deinit(); diff --git a/std/io.zig b/std/io.zig index 938368c585..be8702f44a 100644 --- a/std/io.zig +++ b/std/io.zig @@ -423,8 +423,9 @@ fn bufPrintUnsigned(inline T: type, out_buf: []u8, x: T) -> usize { return len; } -#attribute("test") fn parseU64DigitTooBig() { + @setFnTest(this, true); + parseUnsigned(u64, "123a", 10) %% |err| { if (err == error.InvalidChar) return; @unreachable(); diff --git a/std/list.zig b/std/list.zig index 36c682cc1c..0c5bf92da6 100644 --- a/std/list.zig +++ b/std/list.zig @@ -49,8 +49,9 @@ pub struct List(T: type) { } } -#attribute("test") fn basicListTest() { + @setFnTest(this, true); + var list = List(i32).init(&debug.global_allocator); defer list.deinit(); diff --git a/std/mem.zig b/std/mem.zig index e4eb94f8da..ba65679c73 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -77,8 +77,8 @@ pub fn sliceAsInt(buf: []u8, is_be: bool, inline T: type) -> T { return result; } -#attribute("test") fn testSliceAsInt() { + @setFnTest(this, true); { const buf = []u8{0x00, 0x00, 0x12, 0x34}; const answer = sliceAsInt(buf[0...], true, u64); diff --git a/std/net.zig b/std/net.zig index 3c943091b2..2b52e8bf97 100644 --- a/std/net.zig +++ b/std/net.zig @@ -180,8 +180,9 @@ error Overflow; error JunkAtEnd; error Incomplete; -#static_eval_enable(false) fn parseIp6(buf: []const u8) -> %Address { + @setFnStaticEval(this, false); + var result: Address = undefined; result.family = linux.AF_INET6; result.scope_id = 0; @@ -318,8 +319,9 @@ fn parseIp4(buf: []const u8) -> %u32 { } -#attribute("test") fn testParseIp4() { + @setFnTest(this, true); + assert(%%parseIp4("127.0.0.1") == endian.swapIfLe(u32, 0x7f000001)); switch (parseIp4("256.0.0.1")) { Overflow => {}, else => @unreachable(), } switch (parseIp4("x.0.0.1")) { InvalidChar => {}, else => @unreachable(), } @@ -328,8 +330,9 @@ fn testParseIp4() { switch (parseIp4("100..0.1")) { InvalidChar => {}, else => @unreachable(), } } -#attribute("test") fn testParseIp6() { + @setFnTest(this, true); + { const addr = %%parseIp6("FF01:0:0:0:0:0:0:FB"); assert(addr.addr[0] == 0xff); @@ -338,8 +341,9 @@ fn testParseIp6() { } } -#attribute("test") fn testLookupSimpleIp() { + @setFnTest(this, true); + { var addrs_buf: [5]Address = undefined; const addrs = %%lookup("192.168.1.1", addrs_buf); diff --git a/std/os.zig b/std/os.zig index 806cc43f33..8def16601d 100644 --- a/std/os.zig +++ b/std/os.zig @@ -27,8 +27,7 @@ pub fn getRandomBytes(buf: []u8) -> %void { } } -#attribute("cold") -pub fn abort() -> unreachable { +pub coldcc fn abort() -> unreachable { switch (@compileVar("os")) { linux, darwin => { system.raise(system.SIGABRT); diff --git a/std/rand.zig b/std/rand.zig index 15cf9626b7..16af7c490d 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -153,8 +153,9 @@ struct MersenneTwister( } } -#attribute("test") fn testFloat32() { + @setFnTest(this, true); + var r: Rand = undefined; r.init(42); @@ -165,8 +166,9 @@ fn testFloat32() { }} } -#attribute("test") fn testMT19937_64() { + @setFnTest(this, true); + var rng: MT19937_64 = undefined; rng.init(rand_test.mt64_seed); for (rand_test.mt64_data) |value| { @@ -174,8 +176,9 @@ fn testMT19937_64() { } } -#attribute("test") fn testMT19937_32() { + @setFnTest(this, true); + var rng: MT19937_32 = undefined; rng.init(rand_test.mt32_seed); for (rand_test.mt32_data) |value| { diff --git a/std/str.zig b/std/str.zig index e72a042e3f..ced98a46ea 100644 --- a/std/str.zig +++ b/std/str.zig @@ -12,8 +12,9 @@ pub fn sliceEql(inline T: type, a: []const T, b: []const T) -> bool { return true; } -#attribute("test") -fn stringEquality() { +fn testStringEquality() { + @setFnTest(this, true); + assert(eql("abcd", "abcd")); assert(!eql("abcdef", "abZdef")); assert(!eql("abcdefg", "abcdef")); diff --git a/test/cases/const_slice_child.zig b/test/cases/const_slice_child.zig index 97b37028d1..bd22ca6da8 100644 --- a/test/cases/const_slice_child.zig +++ b/test/cases/const_slice_child.zig @@ -2,8 +2,9 @@ const assert = @import("std").debug.assert; var argv: &&const u8 = undefined; -#attribute("test") fn constSliceChild() { + @setFnTest(this, true); + const strs = ([]&const u8) { c"one", c"two", @@ -13,16 +14,18 @@ fn constSliceChild() { bar(strs.len); } -#static_eval_enable(false) fn foo(args: [][]const u8) { + @setFnStaticEval(this, false); + assert(args.len == 3); assert(streql(args[0], "one")); assert(streql(args[1], "two")); assert(streql(args[2], "three")); } -#static_eval_enable(false) fn bar(argc: usize) { + @setFnStaticEval(this, false); + var args: [argc][]u8 = undefined; for (args) |_, i| { const ptr = argv[i]; @@ -31,15 +34,17 @@ fn bar(argc: usize) { foo(args); } -#static_eval_enable(false) fn strlen(ptr: &const u8) -> usize { + @setFnStaticEval(this, false); + var count: usize = 0; while (ptr[count] != 0; count += 1) {} return count; } -#static_eval_enable(false) fn streql(a: []const u8, b: []const u8) -> bool { + @setFnStaticEval(this, false); + if (a.len != b.len) return false; for (a) |item, index| { if (b[index] != item) return false; diff --git a/test/cases/enum_to_int.zig b/test/cases/enum_to_int.zig index 54994754dd..0be2c32a45 100644 --- a/test/cases/enum_to_int.zig +++ b/test/cases/enum_to_int.zig @@ -8,17 +8,24 @@ enum Number { Four, } -#attribute("test") fn enumToInt() { - shouldEqual(Number.Zero, 0); - shouldEqual(Number.One, 1); - shouldEqual(Number.Two, 2); - shouldEqual(Number.Three, 3); - shouldEqual(Number.Four, 4); + @setFnTest(this, true); + + shouldEqual(false, Number.Zero, 0); + shouldEqual(false, Number.One, 1); + shouldEqual(false, Number.Two, 2); + shouldEqual(false, Number.Three, 3); + shouldEqual(false, Number.Four, 4); + + shouldEqual(true, Number.Zero, 0); + shouldEqual(true, Number.One, 1); + shouldEqual(true, Number.Two, 2); + shouldEqual(true, Number.Three, 3); + shouldEqual(true, Number.Four, 4); } -// TODO add test with this disabled -#static_eval_enable(false) -fn shouldEqual(n: Number, expected: usize) { +fn shouldEqual(inline static_eval: bool, n: Number, expected: usize) { + @setFnStaticEval(this, static_eval); + assert(usize(n) == expected); } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index cff5883b19..4f26c9f258 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -15,8 +15,9 @@ enum ET { } } -#attribute("test") fn enumWithMembers() { + @setFnTest(this, true); + const a = ET.SINT { -42 }; const b = ET.UINT { 42 }; var buf: [20]u8 = undefined; diff --git a/test/cases/max_value_type.zig b/test/cases/max_value_type.zig index 62b9508318..e93ce5eb84 100644 --- a/test/cases/max_value_type.zig +++ b/test/cases/max_value_type.zig @@ -1,7 +1,8 @@ const assert = @import("std").debug.assert; -#attribute("test") fn maxValueType() { + @setFnTest(this, true); + // If the type of @maxValue(i32) was i32 then this implicit cast to // u32 would not work. But since the value is a number literal, // it works fine. diff --git a/test/cases/maybe_return.zig b/test/cases/maybe_return.zig index e779c1c50b..0caa5bdb0b 100644 --- a/test/cases/maybe_return.zig +++ b/test/cases/maybe_return.zig @@ -1,15 +1,17 @@ const assert = @import("std").debug.assert; -#attribute("test") fn maybeReturn() { + @setFnTest(this, true); + assert(??foo(1235)); assert(if (const _ ?= foo(null)) false else true); assert(!??foo(1234)); } -// TODO add another function with static_eval_enable(true) -#static_eval_enable(false) +// TODO test static eval maybe return fn foo(x: ?i32) -> ?bool { + @setFnStaticEval(this, false); + const value = ?return x; return value > 1234; } diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/cases/namespace_depends_on_compile_var/index.zig index d6c7313e88..3fdc207cb9 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/cases/namespace_depends_on_compile_var/index.zig @@ -1,7 +1,8 @@ const assert = @import("std").debug.assert; -#attribute("test") fn namespaceDependsOnCompileVar() { + @setFnTest(this, true); + if (some_namespace.a_bool) { assert(some_namespace.a_bool); } else { diff --git a/test/cases/pub_enum/index.zig b/test/cases/pub_enum/index.zig index 7767c94002..72ace5a117 100644 --- a/test/cases/pub_enum/index.zig +++ b/test/cases/pub_enum/index.zig @@ -1,16 +1,18 @@ const assert = @import("std").debug.assert; const other = @import("other.zig"); -#attribute("test") fn pubEnum() { + @setFnTest(this, true); + pubEnumTest(other.APubEnum.Two); } fn pubEnumTest(foo: other.APubEnum) { assert(foo == other.APubEnum.Two); } -#attribute("test") fn castWithImportedSymbol() { + @setFnTest(this, true); + assert(other.size_t(42) == 42); } diff --git a/test/cases/return_type_type.zig b/test/cases/return_type_type.zig index 4337c072b2..24b7a06500 100644 --- a/test/cases/return_type_type.zig +++ b/test/cases/return_type_type.zig @@ -10,8 +10,9 @@ pub struct SmallList(inline T: type, inline STATIC_SIZE: usize) { prealloc_items: [STATIC_SIZE]T, } -#attribute("test") fn functionWithReturnTypeType() { + @setFnTest(this, true); + var list: List(i32) = undefined; var list2: List(i32) = undefined; list.length = 10; diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig index 0973075180..9711718e8b 100644 --- a/test/cases/sizeof_and_typeof.zig +++ b/test/cases/sizeof_and_typeof.zig @@ -1,7 +1,8 @@ const assert = @import("std").debug.assert; -#attribute("test") fn sizeofAndTypeOf() { + @setFnTest(this, true); + const y: @typeOf(x) = 120; assert(@sizeOf(@typeOf(y)) == 2); } diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/cases/struct_contains_slice_of_itself.zig index 24b5c92116..2a8fc52ded 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/cases/struct_contains_slice_of_itself.zig @@ -5,8 +5,9 @@ struct Node { children: []Node, } -#attribute("test") fn structContainsSliceOfItself() { + @setFnTest(this, true); + var nodes = []Node { Node { .payload = 1, diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index cb8c864357..fb7b2e2832 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -14,16 +14,18 @@ enum FormValue { Other: bool, } -#static_eval_enable(false) fn doThing(form_id: u64) -> %FormValue { + @setFnStaticEval(this, false); + return switch (form_id) { 17 => FormValue.Address { %return readOnce() }, else => error.InvalidDebugInfo, } } -#attribute("test") fn switchProngReturnsErrorEnum() { + @setFnTest(this, true); + %%doThing(17); assert(read_count == 1); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index ad37761f45..cc447660da 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -7,8 +7,9 @@ enum FormValue { error Whatever; -#static_eval_enable(false) fn foo(id: u64) -> %FormValue { + @setFnStaticEval(this, false); + switch (id) { 2 => FormValue.Two { true }, 1 => FormValue.One, @@ -16,8 +17,9 @@ fn foo(id: u64) -> %FormValue { } } -#attribute("test") fn switchProngImplicitCast() { + @setFnTest(this, true); + const result = switch (%%foo(2)) { One => false, Two => |x| x, diff --git a/test/cases/this.zig b/test/cases/this.zig index deac9c26e8..f7197290f2 100644 --- a/test/cases/this.zig +++ b/test/cases/this.zig @@ -25,13 +25,15 @@ fn factorial(x: i32) -> i32 { } } -#attribute("test") fn thisReferToModuleCallPrivateFn() { + @setFnTest(this, true); + assert(module.add(1, 2) == 3); } -#attribute("test") fn thisReferToContainer() { + @setFnTest(this, true); + var pt = Point(i32) { .x = 12, .y = 34, @@ -41,7 +43,8 @@ fn thisReferToContainer() { assert(pt.y == 35); } -#attribute("test") fn thisReferToFn() { + @setFnTest(this, true); + assert(factorial(5) == 120); } diff --git a/test/cases/var_params.zig b/test/cases/var_params.zig index 66cf4da8b7..8db3b3e331 100644 --- a/test/cases/var_params.zig +++ b/test/cases/var_params.zig @@ -1,7 +1,8 @@ const assert = @import("std").debug.assert; -#attribute("test") fn varParams() { + @setFnTest(this, true); + assert(max_i32(12, 34) == 34); assert(max_f64(1.2, 3.4) == 3.4); @@ -21,12 +22,14 @@ fn max_f64(a: f64, b: f64) -> f64 { max(a, b) } -#static_eval_enable(false) fn max_i32_noeval(a: i32, b: i32) -> i32 { + @setFnStaticEval(this, false); + max(a, b) } -#static_eval_enable(false) fn max_f64_noeval(a: f64, b: f64) -> f64 { + @setFnStaticEval(this, false); + max(a, b) } diff --git a/test/cases/zeroes.zig b/test/cases/zeroes.zig index 94774e434d..c30b01d4af 100644 --- a/test/cases/zeroes.zig +++ b/test/cases/zeroes.zig @@ -7,8 +7,9 @@ struct Foo { d: ?i32, } -#attribute("test") fn initializing_a_struct_with_zeroes() { + @setFnTest(this, true); + const foo: Foo = zeroes; assert(foo.a == 0.0); assert(foo.b == 0); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 4b03efc7d6..564381cd1a 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -279,8 +279,9 @@ pub fn bar_function() { )SOURCE"); add_source_file(tc, "other.zig", R"SOURCE( -#static_eval_enable(false) pub fn foo_function() -> bool { + @setFnStaticEval(this, false); + // this one conflicts with the one from foo return true; } @@ -686,14 +687,6 @@ fn a() {} fn a() {} )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'a'"); - add_compile_fail_case("bad directive", R"SOURCE( -#bogus1("") -extern fn b(); -#bogus2("") -fn a() {} - )SOURCE", 2, ".tmp_source.zig:2:1: error: invalid directive: 'bogus1'", - ".tmp_source.zig:4:1: error: invalid directive: 'bogus2'"); - add_compile_fail_case("unreachable with return", R"SOURCE( fn a() -> unreachable {return;} )SOURCE", 1, ".tmp_source.zig:2:24: error: expected type 'unreachable', got 'void'"); @@ -1280,8 +1273,11 @@ struct Foo { x: i32, } const a = get_it(); -#static_eval_enable(false) -fn get_it() -> Foo { Foo {.x = 13} } +fn get_it() -> Foo { + @setFnStaticEval(this, false); + Foo {.x = 13} +} + )SOURCE", 1, ".tmp_source.zig:5:17: error: unable to evaluate constant expression"); add_compile_fail_case("undeclared identifier error should mark fn as impure", R"SOURCE( @@ -1316,8 +1312,11 @@ fn foo() { else => 3, }; } -#static_eval_enable(false) -fn bar() -> i32 { 2 } +fn bar() -> i32 { + @setFnStaticEval(this, false); + 2 +} + )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type"); add_compile_fail_case("atomic orderings of cmpxchg", R"SOURCE( @@ -1458,7 +1457,6 @@ pub struct SmallList(inline T: type, inline STATIC_SIZE: usize) { prealloc_items: [STATIC_SIZE]T, } -#attribute("test") fn function_with_return_type_type() { var list: List(i32) = undefined; list.length = 10; @@ -1623,12 +1621,15 @@ pub fn main(args: [][]u8) -> %void { const a = []i32{1, 2, 3, 4}; baz(bar(a)); } -#static_eval_enable(false) fn bar(a: []i32) -> i32 { + @setFnStaticEval(this, false); + a[4] } -#static_eval_enable(false) -fn baz(a: i32) {} +fn baz(a: i32) { + @setFnStaticEval(this, false); +} + )SOURCE"); add_debug_safety_case("integer addition overflow", R"SOURCE( @@ -1637,8 +1638,9 @@ pub fn main(args: [][]u8) -> %void { const x = add(65530, 10); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn add(a: u16, b: u16) -> u16 { + @setFnStaticEval(this, false); + a + b } )SOURCE"); @@ -1649,8 +1651,9 @@ pub fn main(args: [][]u8) -> %void { const x = sub(10, 20); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn sub(a: u16, b: u16) -> u16 { + @setFnStaticEval(this, false); + a - b } )SOURCE"); @@ -1661,8 +1664,9 @@ pub fn main(args: [][]u8) -> %void { const x = mul(300, 6000); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn mul(a: u16, b: u16) -> u16 { + @setFnStaticEval(this, false); + a * b } )SOURCE"); @@ -1673,8 +1677,9 @@ pub fn main(args: [][]u8) -> %void { const x = neg(-32768); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn neg(a: i16) -> i16 { + @setFnStaticEval(this, false); + -a } )SOURCE"); @@ -1685,8 +1690,9 @@ pub fn main(args: [][]u8) -> %void { const x = shl(-16385, 1); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn shl(a: i16, b: i16) -> i16 { + @setFnStaticEval(this, false); + a << b } )SOURCE"); @@ -1697,8 +1703,9 @@ pub fn main(args: [][]u8) -> %void { const x = shl(0b0010111111111111, 3); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn shl(a: u16, b: u16) -> u16 { + @setFnStaticEval(this, false); + a << b } )SOURCE"); @@ -1708,8 +1715,9 @@ error Whatever; pub fn main(args: [][]u8) -> %void { const x = div0(999, 0); } -#static_eval_enable(false) fn div0(a: i32, b: i32) -> i32 { + @setFnStaticEval(this, false); + a / b } )SOURCE"); @@ -1720,8 +1728,9 @@ pub fn main(args: [][]u8) -> %void { const x = divExact(10, 3); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn divExact(a: i32, b: i32) -> i32 { + @setFnStaticEval(this, false); + @divExact(a, b) } )SOURCE"); @@ -1732,8 +1741,9 @@ pub fn main(args: [][]u8) -> %void { const x = widenSlice([]u8{1, 2, 3, 4, 5}); if (x.len == 0) return error.Whatever; } -#static_eval_enable(false) fn widenSlice(slice: []u8) -> []i32 { + @setFnStaticEval(this, false); + ([]i32)(slice) } )SOURCE"); @@ -1744,8 +1754,9 @@ pub fn main(args: [][]u8) -> %void { const x = shorten_cast(200); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn shorten_cast(x: i32) -> i8 { + @setFnStaticEval(this, false); + i8(x) } )SOURCE"); @@ -1756,8 +1767,9 @@ pub fn main(args: [][]u8) -> %void { const x = unsigned_cast(-10); if (x == 0) return error.Whatever; } -#static_eval_enable(false) fn unsigned_cast(x: i32) -> u32 { + @setFnStaticEval(this, false); + u32(x) } )SOURCE"); diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 777de7f29e..d85f50c564 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -19,12 +19,15 @@ const test_this = @import("cases/this.zig"); // normal comment /// this is a documentation comment /// doc comment line 2 -#attribute("test") -fn emptyFunctionWithComments() {} +fn emptyFunctionWithComments() { + @setFnTest(this, true); +} + -#attribute("test") fn ifStatements() { + @setFnTest(this, true); + shouldBeEqual(1, 1); firstEqlThird(2, 1, 2); } @@ -48,8 +51,9 @@ fn firstEqlThird(a: i32, b: i32, c: i32) { } -#attribute("test") fn params() { + @setFnTest(this, true); + assert(testParamsAdd(22, 11) == 33); } fn testParamsAdd(a: i32, b: i32) -> i32 { @@ -57,8 +61,9 @@ fn testParamsAdd(a: i32, b: i32) -> i32 { } -#attribute("test") fn localVariables() { + @setFnTest(this, true); + testLocVars(2); } fn testLocVars(b: i32) { @@ -66,14 +71,16 @@ fn testLocVars(b: i32) { if (a + b != 3) @unreachable(); } -#attribute("test") fn boolLiterals() { + @setFnTest(this, true); + assert(true); assert(!false); } -#attribute("test") fn voidParameters() { + @setFnTest(this, true); + voidFun(1, void{}, 2, {}); } fn voidFun(a : i32, b : void, c : i32, d : void) { @@ -83,8 +90,9 @@ fn voidFun(a : i32, b : void, c : i32, d : void) { return vv; } -#attribute("test") fn mutableLocalVariables() { + @setFnTest(this, true); + var zero : i32 = 0; assert(zero == 0); @@ -95,8 +103,9 @@ fn mutableLocalVariables() { assert(i == 3); } -#attribute("test") fn arrays() { + @setFnTest(this, true); + var array : [5]u32 = undefined; var i : u32 = 0; @@ -120,8 +129,9 @@ fn getArrayLen(a: []u32) -> usize { a.len } -#attribute("test") fn shortCircuit() { + @setFnTest(this, true); + var hit_1 = false; var hit_2 = false; var hit_3 = false; @@ -148,13 +158,15 @@ fn shortCircuit() { assert(hit_4); } -#static_eval_enable(false) fn assertRuntime(b: bool) { + @setFnStaticEval(this, false); + if (!b) @unreachable() } -#attribute("test") fn modifyOperators() { + @setFnTest(this, true); + var i : i32 = 0; i += 5; assert(i == 5); i -= 2; assert(i == 3); @@ -171,8 +183,9 @@ fn modifyOperators() { } -#attribute("test") fn separateBlockScopes() { + @setFnTest(this, true); + { const no_conflict : i32 = 5; assert(no_conflict == 5); @@ -186,8 +199,9 @@ fn separateBlockScopes() { } -#attribute("test") fn voidStructFields() { + @setFnTest(this, true); + const foo = VoidStructFieldsFoo { .a = void{}, .b = 1, @@ -204,8 +218,9 @@ struct VoidStructFieldsFoo { -#attribute("test") pub fn structs() { + @setFnTest(this, true); + var foo : StructFoo = undefined; @memset(&foo, 0, @sizeOf(StructFoo)); foo.a += 1; @@ -234,8 +249,9 @@ struct Val { x: i32, } -#attribute("test") fn structPointToSelf() { + @setFnTest(this, true); + var root : Node = undefined; root.val.x = 1; @@ -248,8 +264,9 @@ fn structPointToSelf() { assert(node.next.next.next.val.x == 1); } -#attribute("test") fn structByvalAssign() { + @setFnTest(this, true); + var foo1 : StructFoo = undefined; var foo2 : StructFoo = undefined; @@ -269,16 +286,18 @@ fn structInitializer() { const g1 : i32 = 1233 + 1; var g2 : i32 = 0; -#attribute("test") fn globalVariables() { + @setFnTest(this, true); + assert(g2 == 0); g2 = g1; assert(g2 == 1234); } -#attribute("test") fn whileLoop() { + @setFnTest(this, true); + var i : i32 = 0; while (i < 4) { i += 1; @@ -295,8 +314,9 @@ fn whileLoop2() -> i32 { } } -#attribute("test") fn voidArrays() { + @setFnTest(this, true); + var array: [4]void = undefined; array[0] = void{}; array[1] = array[2]; @@ -305,8 +325,9 @@ fn voidArrays() { } -#attribute("test") fn threeExprInARow() { + @setFnTest(this, true); + assertFalse(false || false || false); assertFalse(true && true && false); assertFalse(1 | 2 | 4 != 7); @@ -325,8 +346,9 @@ fn assertFalse(b: bool) { } -#attribute("test") fn maybeType() { + @setFnTest(this, true); + const x : ?bool = true; if (const y ?= x) { @@ -353,8 +375,9 @@ fn maybeType() { } -#attribute("test") fn enumType() { + @setFnTest(this, true); + const foo1 = EnumTypeFoo.One {13}; const foo2 = EnumTypeFoo.Two {EnumType { .x = 1234, .y = 5678, }}; const bar = EnumTypeBar.B; @@ -387,8 +410,9 @@ enum EnumTypeBar { } -#attribute("test") fn arrayLiteral() { + @setFnTest(this, true); + const hex_mult = []u16{4096, 256, 16, 1}; assert(hex_mult.len == 4); @@ -396,8 +420,9 @@ fn arrayLiteral() { } -#attribute("test") fn constNumberLiteral() { + @setFnTest(this, true); + const one = 1; const eleven = ten + one; @@ -406,8 +431,9 @@ fn constNumberLiteral() { const ten = 10; -#attribute("test") fn errorValues() { + @setFnTest(this, true); + const a = i32(error.err1); const b = i32(error.err2); assert(a != b); @@ -417,8 +443,9 @@ error err2; -#attribute("test") fn fnCallOfStructField() { + @setFnTest(this, true); + assert(callStructField(Foo {.ptr = aFunc,}) == 13); } @@ -434,8 +461,9 @@ fn callStructField(foo: Foo) -> i32 { -#attribute("test") fn redefinitionOfErrorValuesAllowed() { + @setFnTest(this, true); + shouldBeNotEqual(error.AnError, error.SecondError); } error AnError; @@ -448,8 +476,9 @@ fn shouldBeNotEqual(a: error, b: error) { -#attribute("test") fn constantEnumWithPayload() { + @setFnTest(this, true); + var empty = AnEnumWithPayload.Empty; var full = AnEnumWithPayload.Full {13}; shouldBeEmpty(empty); @@ -476,8 +505,9 @@ enum AnEnumWithPayload { } -#attribute("test") fn continueInForLoop() { + @setFnTest(this, true); + const array = []i32 {1, 2, 3, 4, 5}; var sum : i32 = 0; for (array) |x| { @@ -491,8 +521,9 @@ fn continueInForLoop() { } -#attribute("test") fn castBoolToInt() { + @setFnTest(this, true); + const t = true; const f = false; assert(i32(t) == i32(1)); @@ -506,8 +537,9 @@ fn nonConstCastBoolToInt(t: bool, f: bool) { } -#attribute("test") fn switchOnEnum() { + @setFnTest(this, true); + const fruit = Fruit.Orange; nonConstSwitchOnEnum(fruit); } @@ -516,8 +548,9 @@ enum Fruit { Orange, Banana, } -#static_eval_enable(false) fn nonConstSwitchOnEnum(fruit: Fruit) { + @setFnStaticEval(this, false); + switch (fruit) { Apple => @unreachable(), Orange => {}, @@ -525,12 +558,14 @@ fn nonConstSwitchOnEnum(fruit: Fruit) { } } -#attribute("test") fn switchStatement() { + @setFnTest(this, true); + nonConstSwitch(SwitchStatmentFoo.C); } -#static_eval_enable(false) fn nonConstSwitch(foo: SwitchStatmentFoo) { + @setFnStaticEval(this, false); + const val: i32 = switch (foo) { A => 1, B => 2, @@ -547,8 +582,9 @@ enum SwitchStatmentFoo { } -#attribute("test") fn switchProngWithVar() { + @setFnTest(this, true); + switchProngWithVarFn(SwitchProngWithVarEnum.One {13}); switchProngWithVarFn(SwitchProngWithVarEnum.Two {13.0}); switchProngWithVarFn(SwitchProngWithVarEnum.Meh); @@ -558,8 +594,9 @@ enum SwitchProngWithVarEnum { Two: f32, Meh, } -#static_eval_enable(false) fn switchProngWithVarFn(a: SwitchProngWithVarEnum) { + @setFnStaticEval(this, false); + switch(a) { One => |x| { if (x != 13) @unreachable(); @@ -574,13 +611,15 @@ fn switchProngWithVarFn(a: SwitchProngWithVarEnum) { } -#attribute("test") fn errReturnInAssignment() { + @setFnTest(this, true); + %%doErrReturnInAssignment(); } -#static_eval_enable(false) fn doErrReturnInAssignment() -> %void { + @setFnStaticEval(this, false); + var x : i32 = undefined; x = %return makeANonErr(); } @@ -591,15 +630,17 @@ fn makeANonErr() -> %i32 { -#attribute("test") fn rhsMaybeUnwrapReturn() { + @setFnTest(this, true); + const x = ?true; const y = x ?? return; } -#attribute("test") fn implicitCastFnUnreachableReturn() { + @setFnTest(this, true); + wantsFnWithVoid(fnWithUnreachable); } @@ -610,15 +651,17 @@ fn fnWithUnreachable() -> unreachable { } -#attribute("test") fn explicitCastMaybePointers() { + @setFnTest(this, true); + const a: ?&i32 = undefined; const b: ?&f32 = (?&f32)(a); } -#attribute("test") fn constExprEvalOnSingleExprBlocks() { + @setFnTest(this, true); + assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); } @@ -635,14 +678,16 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) -> i32 { } -#attribute("test") fn builtinConstEval() { + @setFnTest(this, true); + const x : i32 = @constEval(1 + 2 + 3); assert(x == @constEval(6)); } -#attribute("test") fn slicing() { + @setFnTest(this, true); + var array : [20]i32 = undefined; array[5] = 1234; @@ -659,8 +704,9 @@ fn slicing() { } -#attribute("test") fn memcpyAndMemsetIntrinsics() { + @setFnTest(this, true); + var foo : [20]u8 = undefined; var bar : [20]u8 = undefined; @@ -671,31 +717,36 @@ fn memcpyAndMemsetIntrinsics() { } -#attribute("test") -fn arrayDotLenConstExpr() { } +fn arrayDotLenConstExpr() { + @setFnTest(this, true); +} + struct ArrayDotLenConstExpr { y: [@constEval(some_array.len)]u8, } const some_array = []u8 {0, 1, 2, 3}; -#attribute("test") fn countLeadingZeroes() { + @setFnTest(this, true); + assert(@clz(u8, 0b00001010) == 4); assert(@clz(u8, 0b10001010) == 0); assert(@clz(u8, 0b00000000) == 8); } -#attribute("test") fn countTrailingZeroes() { + @setFnTest(this, true); + assert(@ctz(u8, 0b10100000) == 5); assert(@ctz(u8, 0b10001010) == 1); assert(@ctz(u8, 0b00000000) == 8); } -#attribute("test") fn multilineString() { + @setFnTest(this, true); + const s1 = \\one \\two) @@ -705,8 +756,9 @@ fn multilineString() { assert(str.eql(s1, s2)); } -#attribute("test") fn multilineCString() { + @setFnTest(this, true); + const s1 = c\\one c\\two) @@ -718,8 +770,9 @@ fn multilineCString() { -#attribute("test") fn simpleGenericFn() { + @setFnTest(this, true); + assert(max(i32, 3, -1) == 3); assert(max(f32, 0.123, 0.456) == 0.456); assert(add(2, 3) == 5); @@ -734,8 +787,9 @@ fn add(inline a: i32, b: i32) -> i32 { } -#attribute("test") fn constantEqualFunctionPointers() { + @setFnTest(this, true); + const alias = emptyFn; assert(@constEval(emptyFn == alias)); } @@ -743,44 +797,50 @@ fn constantEqualFunctionPointers() { fn emptyFn() {} -#attribute("test") fn genericMallocFree() { + @setFnTest(this, true); + const a = %%memAlloc(u8, 10); memFree(u8, a); } const some_mem : [100]u8 = undefined; -#static_eval_enable(false) fn memAlloc(inline T: type, n: usize) -> %[]T { + @setFnStaticEval(this, false); + return (&T)(&some_mem[0])[0...n]; } fn memFree(inline T: type, mem: []T) { } -#attribute("test") fn callFnWithEmptyString() { + @setFnTest(this, true); + acceptsString(""); } fn acceptsString(foo: []u8) { } -#attribute("test") fn hexEscape() { + @setFnTest(this, true); + assert(str.eql("\x68\x65\x6c\x6c\x6f", "hello")); } error AnError; error ALongerErrorName; -#attribute("test") fn errorNameString() { + @setFnTest(this, true); + assert(str.eql(@errorName(error.AnError), "AnError")); assert(str.eql(@errorName(error.ALongerErrorName), "ALongerErrorName")); } -#attribute("test") fn gotoAndLabels() { + @setFnTest(this, true); + gotoLoop(); assert(goto_counter == 10); } @@ -799,12 +859,14 @@ var goto_counter: i32 = 0; -#attribute("test") fn gotoLeaveDeferScope() { + @setFnTest(this, true); + testGotoLeaveDeferScope(true); } -#static_eval_enable(false) fn testGotoLeaveDeferScope(b: bool) { + @setFnStaticEval(this, false); + var it_worked = false; goto entry; @@ -819,8 +881,9 @@ entry: } -#attribute("test") fn castUndefined() { + @setFnTest(this, true); + const array: [100]u8 = undefined; const slice = ([]u8)(array); testCastUndefined(slice); @@ -828,8 +891,9 @@ fn castUndefined() { fn testCastUndefined(x: []u8) {} -#attribute("test") fn castSmallUnsignedToLargerSigned() { + @setFnTest(this, true); + assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } @@ -837,8 +901,9 @@ fn castSmallUnsignedToLargerSigned1(x: u8) -> i16 { x } fn castSmallUnsignedToLargerSigned2(x: u16) -> i64 { x } -#attribute("test") fn implicitCastAfterUnreachable() { + @setFnTest(this, true); + assert(outer() == 1234); } fn inner() -> i32 { 1234 } @@ -847,8 +912,9 @@ fn outer() -> i64 { } -#attribute("test") fn elseIfExpression() { + @setFnTest(this, true); + assert(elseIfExpressionF(1) == 1); } fn elseIfExpressionF(c: u8) -> u8 { @@ -861,8 +927,9 @@ fn elseIfExpressionF(c: u8) -> u8 { } } -#attribute("test") fn errBinaryOperator() { + @setFnTest(this, true); + const a = errBinaryOperatorG(true) %% 3; const b = errBinaryOperatorG(false) %% 3; assert(a == 3); @@ -877,16 +944,18 @@ fn errBinaryOperatorG(x: bool) -> %isize { } } -#attribute("test") fn unwrapSimpleValueFromError() { + @setFnTest(this, true); + const i = %%unwrapSimpleValueFromErrorDo(); assert(i == 13); } fn unwrapSimpleValueFromErrorDo() -> %isize { 13 } -#attribute("test") fn storeMemberFunctionInVariable() { + @setFnTest(this, true); + const instance = MemberFnTestFoo { .x = 1234, }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); @@ -897,15 +966,17 @@ struct MemberFnTestFoo { fn member(foo: MemberFnTestFoo) -> i32 { foo.x } } -#attribute("test") fn callMemberFunctionDirectly() { + @setFnTest(this, true); + const instance = MemberFnTestFoo { .x = 1234, }; const result = MemberFnTestFoo.member(instance); assert(result == 1234); } -#attribute("test") fn memberFunctions() { + @setFnTest(this, true); + const r = MemberFnRand {.seed = 1234}; assert(r.getSeed() == 1234); } @@ -916,16 +987,18 @@ struct MemberFnRand { } } -#attribute("test") fn staticFunctionEvaluation() { + @setFnTest(this, true); + assert(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); fn staticAdd(a: i32, b: i32) -> i32 { a + b } -#attribute("test") fn staticallyInitalizedList() { + @setFnTest(this, true); + assert(static_point_list[0].x == 1); assert(static_point_list[0].y == 2); assert(static_point_list[1].x == 3); @@ -944,8 +1017,9 @@ fn makePoint(x: i32, y: i32) -> Point { } -#attribute("test") fn staticEvalRecursive() { + @setFnTest(this, true); + assert(some_data.len == 21); } var some_data: [usize(fibbonaci(7))]u8 = undefined; @@ -954,8 +1028,9 @@ fn fibbonaci(x: i32) -> i32 { return fibbonaci(x - 1) + fibbonaci(x - 2); } -#attribute("test") fn staticEvalWhile() { + @setFnTest(this, true); + assert(static_eval_while_number == 1); } const static_eval_while_number = staticWhileLoop1(); @@ -968,8 +1043,9 @@ fn staticWhileLoop2() -> i32 { } } -#attribute("test") fn staticEvalListInit() { + @setFnTest(this, true); + assert(static_vec3.data[2] == 1.0); } const static_vec3 = vec3(0.0, 0.0, 1.0); @@ -983,8 +1059,9 @@ pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 { } -#attribute("test") fn genericFnWithImplicitCast() { + @setFnTest(this, true); + assert(getFirstByte(u8, []u8 {13}) == 13); assert(getFirstByte(u16, []u16 {0, 13}) == 0); } @@ -993,8 +1070,9 @@ fn getFirstByte(inline T: type, mem: []T) -> u8 { getByte((&u8)(&mem[0])) } -#attribute("test") fn continueAndBreak() { + @setFnTest(this, true); + runContinueAndBreakTest(); assert(continue_and_break_counter == 8); } @@ -1013,8 +1091,9 @@ fn runContinueAndBreakTest() { } -#attribute("test") fn pointerDereferencing() { + @setFnTest(this, true); + var x = i32(3); const y = &x; @@ -1024,16 +1103,18 @@ fn pointerDereferencing() { assert(*y == 4); } -#attribute("test") fn constantExpressions() { + @setFnTest(this, true); + var array : [array_size]u8 = undefined; assert(@sizeOf(@typeOf(array)) == 20); } const array_size : u8 = 20; -#attribute("test") fn minValueAndMaxValue() { + @setFnTest(this, true); + assert(@maxValue(u8) == 255); assert(@maxValue(u16) == 65535); assert(@maxValue(u32) == 4294967295); @@ -1055,8 +1136,9 @@ fn minValueAndMaxValue() { assert(@minValue(i64) == -9223372036854775808); } -#attribute("test") fn overflowIntrinsics() { + @setFnTest(this, true); + var result: u8 = undefined; assert(@addWithOverflow(u8, 250, 100, &result)); assert(!@addWithOverflow(u8, 100, 150, &result)); @@ -1064,8 +1146,9 @@ fn overflowIntrinsics() { } -#attribute("test") fn nestedArrays() { + @setFnTest(this, true); + const array_of_strings = [][]u8 {"hello", "this", "is", "my", "thing"}; for (array_of_strings) |s, i| { if (i == 0) assert(str.eql(s, "hello")); @@ -1076,21 +1159,24 @@ fn nestedArrays() { } } -#attribute("test") fn intToPtrCast() { + @setFnTest(this, true); + const x = isize(13); const y = (&u8)(x); const z = usize(y); assert(z == 13); } -#attribute("test") fn stringConcatenation() { + @setFnTest(this, true); + assert(str.eql("OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } -#attribute("test") fn constantStructWithNegation() { + @setFnTest(this, true); + assert(vertices[0].x == -0.6); } struct Vertex { @@ -1107,8 +1193,9 @@ const vertices = []Vertex { }; -#attribute("test") fn returnWithImplicitCastFromWhileLoop() { + @setFnTest(this, true); + %%returnWithImplicitCastFromWhileLoopTest(); } fn returnWithImplicitCastFromWhileLoopTest() -> %void { @@ -1117,8 +1204,9 @@ fn returnWithImplicitCastFromWhileLoopTest() -> %void { } } -#attribute("test") fn returnStructByvalFromFunction() { + @setFnTest(this, true); + const bar = makeBar(1234, 5678); assert(bar.y == 5678); } @@ -1133,8 +1221,9 @@ fn makeBar(x: i32, y: i32) -> Bar { } } -#attribute("test") fn functionPointers() { + @setFnTest(this, true); + const fns = []@typeOf(fn1) { fn1, fn2, fn3, fn4, }; for (fns) |f, i| { assert(f() == u32(i) + 5); @@ -1147,8 +1236,9 @@ fn fn4() -> u32 {8} -#attribute("test") fn staticallyInitalizedStruct() { + @setFnTest(this, true); + st_init_str_foo.x += 1; assert(st_init_str_foo.x == 14); } @@ -1158,8 +1248,9 @@ struct StInitStrFoo { } var st_init_str_foo = StInitStrFoo { .x = 13, .y = true, }; -#attribute("test") fn staticallyInitializedArrayLiteral() { + @setFnTest(this, true); + const y : [4]u8 = st_init_arr_lit_x; assert(y[3] == 4); } @@ -1167,8 +1258,9 @@ const st_init_arr_lit_x = []u8{1,2,3,4}; -#attribute("test") fn pointerToVoidReturnType() { + @setFnTest(this, true); + %%testPointerToVoidReturnType(); } fn testPointerToVoidReturnType() -> %void { @@ -1181,8 +1273,9 @@ fn testPointerToVoidReturnType2() -> &void { } -#attribute("test") fn callResultOfIfElseExpression() { + @setFnTest(this, true); + assert(str.eql(f2(true), "a")); assert(str.eql(f2(false), "b")); } @@ -1193,8 +1286,9 @@ fn fA() -> []u8 { "a" } fn fB() -> []u8 { "b" } -#attribute("test") fn constExpressionEvalHandlingOfVariables() { + @setFnTest(this, true); + var x = true; while (x) { x = false; @@ -1203,8 +1297,9 @@ fn constExpressionEvalHandlingOfVariables() { -#attribute("test") fn constantEnumInitializationWithDifferingSizes() { + @setFnTest(this, true); + test3_1(test3_foo); test3_2(test3_bar); } @@ -1219,8 +1314,9 @@ struct Test3Point { } const test3_foo = Test3Foo.Three{Test3Point {.x = 3, .y = 4}}; const test3_bar = Test3Foo.Two{13}; -#static_eval_enable(false) fn test3_1(f: Test3Foo) { + @setFnStaticEval(this, false); + switch (f) { Three => |pt| { assert(pt.x == 3); @@ -1229,8 +1325,9 @@ fn test3_1(f: Test3Foo) { else => @unreachable(), } } -#static_eval_enable(false) fn test3_2(f: Test3Foo) { + @setFnStaticEval(this, false); + switch (f) { Two => |x| { assert(x == 13); @@ -1241,8 +1338,9 @@ fn test3_2(f: Test3Foo) { -#attribute("test") fn whileWithContinueExpr() { + @setFnTest(this, true); + var sum: i32 = 0; {var i: i32 = 0; while (i < 10; i += 1) { if (i == 5) continue; @@ -1252,38 +1350,47 @@ fn whileWithContinueExpr() { } -#attribute("test") fn forLoopWithPointerElemVar() { + @setFnTest(this, true); + const source = "abcdefg"; var target: [source.len]u8 = undefined; @memcpy(&target[0], &source[0], source.len); mangleString(target); assert(str.eql(target, "bcdefgh")); } -#static_eval_enable(false) fn mangleString(s: []u8) { + @setFnStaticEval(this, false); + for (s) |*c| { *c += 1; } } -#attribute("test") fn emptyStructMethodCall() { + @setFnTest(this, true); + const es = EmptyStruct{}; assert(es.method() == 1234); } struct EmptyStruct { - #static_eval_enable(false) - fn method(es: EmptyStruct) -> i32 { 1234 } + fn method(es: EmptyStruct) -> i32 { + @setFnStaticEval(this, false); + 1234 + } + } -#attribute("test") -fn @"weird function name"() { } +fn @"weird function name"() { + @setFnTest(this, true); +} + -#attribute("test") fn returnEmptyStructFromFn() { + @setFnTest(this, true); + testReturnEmptyStructFromFn(); testReturnEmptyStructFromFnNoeval(); } @@ -1291,13 +1398,15 @@ struct EmptyStruct2 {} fn testReturnEmptyStructFromFn() -> EmptyStruct2 { EmptyStruct2 {} } -#static_eval_enable(false) fn testReturnEmptyStructFromFnNoeval() -> EmptyStruct2 { + @setFnStaticEval(this, false); + EmptyStruct2 {} } -#attribute("test") fn passSliceOfEmptyStructToFn() { + @setFnTest(this, true); + assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []EmptyStruct2) -> usize { @@ -1305,8 +1414,9 @@ fn testPassSliceOfEmptyStructToFn(slice: []EmptyStruct2) -> usize { } -#attribute("test") fn pointerComparison() { + @setFnTest(this, true); + const a = ([]u8)("a"); const b = &a; assert(ptrEql(b, b)); @@ -1315,15 +1425,17 @@ fn ptrEql(a: &[]u8, b: &[]u8) -> bool { a == b } -#attribute("test") fn characterLiterals() { + @setFnTest(this, true); + assert('\'' == single_quote); } const single_quote = '\''; -#attribute("test") fn switchWithMultipleExpressions() { + @setFnTest(this, true); + const x: i32 = switch (returnsFive()) { 1, 2, 3 => 1, 4, 5, 6 => 2, @@ -1331,12 +1443,16 @@ fn switchWithMultipleExpressions() { }; assert(x == 2); } -#static_eval_enable(false) -fn returnsFive() -> i32 { 5 } +fn returnsFive() -> i32 { + @setFnStaticEval(this, false); + 5 +} + -#attribute("test") fn switchOnErrorUnion() { + @setFnTest(this, true); + const x = switch (returnsTen()) { Ok => |val| val + 1, ItBroke, NoMem => 1, @@ -1347,20 +1463,28 @@ fn switchOnErrorUnion() { error ItBroke; error NoMem; error CrappedOut; -#static_eval_enable(false) -fn returnsTen() -> %i32 { 10 } +fn returnsTen() -> %i32 { + @setFnStaticEval(this, false); + 10 +} + -#attribute("test") fn boolCmp() { + @setFnTest(this, true); + assert(testBoolCmp(true, false) == false); } -#static_eval_enable(false) -fn testBoolCmp(a: bool, b: bool) -> bool { a == b } +fn testBoolCmp(a: bool, b: bool) -> bool { + @setFnStaticEval(this, false); + a == b +} + -#attribute("test") fn takeAddressOfParameter() { + @setFnTest(this, true); + testTakeAddressOfParameter(12.34); testTakeAddressOfParameterNoeval(12.34); } @@ -1368,20 +1492,23 @@ fn testTakeAddressOfParameter(f: f32) { const f_ptr = &f; assert(*f_ptr == 12.34); } -#static_eval_enable(false) fn testTakeAddressOfParameterNoeval(f: f32) { + @setFnStaticEval(this, false); + const f_ptr = &f; assert(*f_ptr == 12.34); } -#attribute("test") fn arrayMultOperator() { + @setFnTest(this, true); + assert(str.eql("ab" ** 5, "ababababab")); } -#attribute("test") fn stringEscapes() { + @setFnTest(this, true); + assert(str.eql("\"", "\x22")); assert(str.eql("\'", "\x27")); assert(str.eql("\n", "\x0a")); @@ -1391,12 +1518,14 @@ fn stringEscapes() { assert(str.eql("\u1234\u0069", "\xe1\x88\xb4\x69")); } -#attribute("test") fn ifVarMaybePointer() { + @setFnTest(this, true); + assert(shouldBeAPlus1(Particle {.a = 14, .b = 1, .c = 1, .d = 1}) == 15); } -#static_eval_enable(false) fn shouldBeAPlus1(p: Particle) -> u64 { + @setFnStaticEval(this, false); + var maybe_particle: ?Particle = p; if (const *particle ?= maybe_particle) { particle.a += 1; @@ -1413,8 +1542,9 @@ struct Particle { d: u64, } -#attribute("test") fn assignToIfVarPtr() { + @setFnTest(this, true); + var maybe_bool: ?bool = true; if (const *b ?= maybe_bool) { @@ -1424,22 +1554,25 @@ fn assignToIfVarPtr() { assert(??maybe_bool == false); } -#attribute("test") fn cmpxchg() { + @setFnTest(this, true); + var x: i32 = 1234; while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} assert(x == 5678); } -#attribute("test") fn fence() { + @setFnTest(this, true); + var x: i32 = 1234; @fence(AtomicOrder.SeqCst); x = 5678; } -#attribute("test") fn unsignedWrapping() { + @setFnTest(this, true); + testUnsignedWrappingEval(@maxValue(u32)); testUnsignedWrappingNoeval(@maxValue(u32)); } @@ -1449,16 +1582,18 @@ fn testUnsignedWrappingEval(x: u32) { const orig = zero -% 1; assert(orig == @maxValue(u32)); } -#static_eval_enable(false) fn testUnsignedWrappingNoeval(x: u32) { + @setFnStaticEval(this, false); + const zero = x +% 1; assert(zero == 0); const orig = zero -% 1; assert(orig == @maxValue(u32)); } -#attribute("test") fn signedWrapping() { + @setFnTest(this, true); + testSignedWrappingEval(@maxValue(i32)); testSignedWrappingNoeval(@maxValue(i32)); } @@ -1468,16 +1603,18 @@ fn testSignedWrappingEval(x: i32) { const max_val = min_val -% 1; assert(max_val == @maxValue(i32)); } -#static_eval_enable(false) fn testSignedWrappingNoeval(x: i32) { + @setFnStaticEval(this, false); + const min_val = x +% 1; assert(min_val == @minValue(i32)); const max_val = min_val -% 1; assert(max_val == @maxValue(i32)); } -#attribute("test") fn negationWrapping() { + @setFnTest(this, true); + testNegationWrappingEval(@minValue(i16)); testNegationWrappingNoeval(@minValue(i16)); } @@ -1486,15 +1623,17 @@ fn testNegationWrappingEval(x: i16) { const neg = -%x; assert(neg == -32768); } -#static_eval_enable(false) fn testNegationWrappingNoeval(x: i16) { + @setFnStaticEval(this, false); + assert(x == -32768); const neg = -%x; assert(neg == -32768); } -#attribute("test") fn shlWrapping() { + @setFnTest(this, true); + testShlWrappingEval(@maxValue(u16)); testShlWrappingNoeval(@maxValue(u16)); } @@ -1502,22 +1641,25 @@ fn testShlWrappingEval(x: u16) { const shifted = x <<% 1; assert(shifted == 65534); } -#static_eval_enable(false) fn testShlWrappingNoeval(x: u16) { + @setFnStaticEval(this, false); + const shifted = x <<% 1; assert(shifted == 65534); } -#attribute("test") fn shlWithOverflow() { + @setFnTest(this, true); + var result: u16 = undefined; assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); assert(result == 0b1011111111111100); } -#attribute("test") fn cStringConcatenation() { + @setFnTest(this, true); + const a = c"OK" ++ c" IT " ++ c"WORKED"; const b = c"OK IT WORKED"; @@ -1530,8 +1672,9 @@ fn cStringConcatenation() { assert(b[len] == 0); } -#attribute("test") fn genericStruct() { + @setFnTest(this, true); + var a1 = GenNode(i32) {.value = 13, .next = null,}; var b1 = GenNode(bool) {.value = true, .next = null,}; assert(a1.value == 13); @@ -1544,8 +1687,9 @@ struct GenNode(T: type) { fn getVal(n: &const GenNode(T)) -> T { n.value } } -#attribute("test") fn castSliceToU8Slice() { + @setFnTest(this, true); + assert(@sizeOf(i32) == 4); var big_thing_array = []i32{1, 2, 3, 4}; const big_thing_slice: []i32 = big_thing_array; @@ -1565,26 +1709,31 @@ fn castSliceToU8Slice() { assert(bytes[11] == @maxValue(u8)); } -#attribute("test") fn floatDivision() { + @setFnTest(this, true); + assert(fdiv32(12.0, 3.0) == 4.0); } -#static_eval_enable(false) fn fdiv32(a: f32, b: f32) -> f32 { + @setFnStaticEval(this, false); + a / b } -#attribute("test") fn exactDivision() { + @setFnTest(this, true); + assert(divExact(55, 11) == 5); } -#static_eval_enable(false) fn divExact(a: u32, b: u32) -> u32 { + @setFnStaticEval(this, false); + @divExact(a, b) } -#attribute("test") fn nullLiteralOutsideFunction() { + @setFnTest(this, true); + const is_null = if (const _ ?= here_is_a_null_literal.context) false else true; assert(is_null); } @@ -1595,25 +1744,29 @@ const here_is_a_null_literal = SillyStruct { .context = null, }; -#attribute("test") fn truncate() { + @setFnTest(this, true); + assert(testTruncate(0x10fd) == 0xfd); } -#static_eval_enable(false) fn testTruncate(x: u32) -> u8 { + @setFnStaticEval(this, false); + @truncate(u8, x) } -#attribute("test") fn constDeclsInStruct() { + @setFnTest(this, true); + assert(GenericDataThing(3).count_plus_one == 4); } struct GenericDataThing(count: isize) { const count_plus_one = count + 1; } -#attribute("test") fn useGenericParamInGenericParam() { + @setFnTest(this, true); + assert(aGenericFn(i32, 3, 4) == 7); } fn aGenericFn(inline T: type, inline a: T, b: T) -> T { @@ -1621,14 +1774,16 @@ fn aGenericFn(inline T: type, inline a: T, b: T) -> T { } -#attribute("test") fn unsigned64BitDivision() { + @setFnTest(this, true); + const result = div(1152921504606846976, 34359738365); assert(result.quotient == 33554432); assert(result.remainder == 100663296); } -#static_eval_enable(false) fn div(a: u64, b: u64) -> DivResult { + @setFnStaticEval(this, false); + DivResult { .quotient = a / b, .remainder = a % b, @@ -1639,8 +1794,9 @@ struct DivResult { remainder: u64, } -#attribute("test") fn intTypeBuiltin() { + @setFnTest(this, true); + assert(@intType(true, 8) == i8); assert(@intType(true, 16) == i16); assert(@intType(true, 32) == i32); @@ -1670,16 +1826,18 @@ fn intTypeBuiltin() { } -#attribute("test") fn intToEnum() { + @setFnTest(this, true); + testIntToEnumEval(3); testIntToEnumNoeval(3); } fn testIntToEnumEval(x: i32) { assert(IntToEnumNumber(x) == IntToEnumNumber.Three); } -#static_eval_enable(false) fn testIntToEnumNoeval(x: i32) { + @setFnStaticEval(this, false); + assert(IntToEnumNumber(x) == IntToEnumNumber.Three); } enum IntToEnumNumber {