diff options
| -rw-r--r-- | CMakeLists.txt | 3 | ||||
| -rw-r--r-- | doc/langref.md | 8 | ||||
| -rw-r--r-- | doc/semantic_analysis.md | 75 | ||||
| -rw-r--r-- | doc/targets.md | 2 | ||||
| -rw-r--r-- | doc/vim/syntax/zig.vim | 2 | ||||
| -rw-r--r-- | example/guess_number/main.zig | 31 | ||||
| -rw-r--r-- | example/hello_world/hello.zig | 6 | ||||
| -rw-r--r-- | example/hello_world/hello_libc.zig | 9 | ||||
| -rw-r--r-- | src/all_types.hpp | 138 | ||||
| -rw-r--r-- | src/analyze.cpp | 1526 | ||||
| -rw-r--r-- | src/analyze.hpp | 6 | ||||
| -rw-r--r-- | src/ast_render.cpp | 54 | ||||
| -rw-r--r-- | src/codegen.cpp | 347 | ||||
| -rw-r--r-- | src/codegen.hpp | 2 | ||||
| -rw-r--r-- | src/errmsg.cpp | 45 | ||||
| -rw-r--r-- | src/main.cpp | 27 | ||||
| -rw-r--r-- | src/parseh.cpp | 16 | ||||
| -rw-r--r-- | src/parser.cpp | 141 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 11 | ||||
| -rw-r--r-- | src/tokenizer.hpp | 3 | ||||
| -rw-r--r-- | std/bootstrap.zig | 29 | ||||
| -rw-r--r-- | std/index.zig | 4 | ||||
| -rw-r--r-- | std/io.zig (renamed from std/std.zig) | 54 | ||||
| -rw-r--r-- | std/os.zig | 16 | ||||
| -rw-r--r-- | std/rand.zig | 28 | ||||
| -rw-r--r-- | std/test_runner.zig | 22 | ||||
| -rw-r--r-- | std/test_runner_libc.zig | 4 | ||||
| -rw-r--r-- | std/test_runner_nolibc.zig | 4 | ||||
| -rw-r--r-- | test/run_tests.cpp | 649 | ||||
| -rw-r--r-- | test/self_hosted.zig | 3 | ||||
| -rw-r--r-- | test/test_std.zig | 1 |
31 files changed, 1446 insertions, 1820 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 35a5abb417..4222b7f454 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,12 +137,13 @@ set(ZIG_STD_SRC "${CMAKE_SOURCE_DIR}/std/test_runner.zig" "${CMAKE_SOURCE_DIR}/std/test_runner_libc.zig" "${CMAKE_SOURCE_DIR}/std/test_runner_nolibc.zig" - "${CMAKE_SOURCE_DIR}/std/std.zig" + "${CMAKE_SOURCE_DIR}/std/io.zig" "${CMAKE_SOURCE_DIR}/std/os.zig" "${CMAKE_SOURCE_DIR}/std/syscall.zig" "${CMAKE_SOURCE_DIR}/std/errno.zig" "${CMAKE_SOURCE_DIR}/std/rand.zig" "${CMAKE_SOURCE_DIR}/std/math.zig" + "${CMAKE_SOURCE_DIR}/std/index.zig" ) diff --git a/doc/langref.md b/doc/langref.md index a963490498..b875b8a03a 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -5,9 +5,7 @@ ``` Root = many(TopLevelDecl) "EOF" -TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl) - -CImportDecl = "c_import" Block +TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | ContainerDecl | GlobalVarDecl | ErrorValueDecl | TypeDecl | UseDecl) TypeDecl = "type" "Symbol" "=" TypeExpr ";" @@ -23,9 +21,7 @@ StructMember = many(Directive) option(VisibleMod) (StructField | FnDef) StructField = "Symbol" option(":" Expression) ",") -Import = "import" "String" ";" - -RootExportDecl = "export" "Symbol" "String" ";" +UseDecl = "use" Expression ";" ExternDecl = "extern" (FnProto | VariableDeclaration) ";" diff --git a/doc/semantic_analysis.md b/doc/semantic_analysis.md new file mode 100644 index 0000000000..054563e022 --- /dev/null +++ b/doc/semantic_analysis.md @@ -0,0 +1,75 @@ +# How Semantic Analysis Works + +We start with a set of files. Typically the user only has one entry point file, +which imports the other files they want to use. However, the compiler may +choose to add more files to the compilation, for example bootstrap.zig which +contains the code that calls main. + +Our goal now is to treat everything that is marked with the `export` keyword +as a root node, and then then parse and semantically analyze as little as +possible in order to fulfill these exports. + +So, some parts of the code very well may have uncaught semantic errors, but as +long as the code is not referenced in any way, the compiler will not complain +because the code may as well not exist. This is similar to the fact that code +excluded from compilation with an `#ifdef` in C is not analyzed. Avoiding +analyzing unused code will save compilation time - one of Zig's goals. + +So, for each file, we iterate over the top level declarations. The set of top +level declarations are: + + * Function Definition + * Global Variable Declaration + * Container Declaration (struct or enum) + * Type Declaration + * Error Value Declaration + * Use Declaration + +Each of these can have `export` attached to them except for error value +declarations and use declarations. + +When we see a top level declaration during this iteration, we determine its +unique name identifier within the file. For example, for a function definition, +the unique name identifier is simply its name. Using this name we add the top +level declaration to a map. + +If the top level declaration is exported, we add it to a set of exported top +level identifiers. + +If the top level declaration is a use declaration, we add it to a set of use +declarations. + +If the top level declaration is an error value declaration, we assign it a value +and increment the count of error values. + +After this preliminary iteration over the top level declarations, we iterate +over the use declarations and resolve them. To resolve a use declaration, we +analyze the associated expression, verify that its type is the namespace type, +and then add all the items from the namespace into the top level declaration +map for the current file. + +To analyze an expression, we recurse the abstract syntax tree of the +expression. Whenever we must look up a symbol, if the symbol exists already, +we can use it. Otherwise, we look it up in the top level declaration map. +If it exists, we can use it. Otherwise, we interrupt resolving this use +declaration to resolve the next one. If a dependency loop is detected, emit +an error. If all use declarations are resolved yet the symbol we need still +does not exist, emit an error. + +To analyze an `@import` expression, find the referenced file, parse it, and +add it to the set of files to perform semantic analysis on. + +Proceed through the rest of the use declarations the same way. + +If we make it through the use declarations without an error, then we have a +complete map of all globals that exist in the current file. + +Next we iterate over the set of exported top level declarations. + +If it's a function definition, add it to the set of exported function +definitions and resolve the function prototype only. Otherwise, resolve the +top level declaration completely. This may involve recursively resolving other +top level declarations that expressions depend on. + +Finally, iterate over the set of exported function definitions and analyze the +bodies. diff --git a/doc/targets.md b/doc/targets.md index fe1e388bb0..8b4afe7be0 100644 --- a/doc/targets.md +++ b/doc/targets.md @@ -8,7 +8,7 @@ How to pass a byvalue struct parameter in the C calling convention is target-specific. Add logic for how to do function prototypes and function calls for the target when an exported or external function has a byvalue struct. -Write the target-specific code in std.zig. +Write the target-specific code in the standard library. Update the C integer types to be the correct size for the target. diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 5ecfb89a61..0877eb56af 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -14,7 +14,7 @@ syn keyword zigConditional if else switch syn keyword zigRepeat while for syn keyword zigConstant null undefined -syn keyword zigKeyword fn import c_import +syn keyword zigKeyword fn use syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 void unreachable type error syn keyword zigType c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 6601c1d829..3f52f57367 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -1,39 +1,38 @@ -export executable "guess_number"; - -import "std.zig"; -import "rand.zig"; -import "os.zig"; +const std = @import("std"); +const io = std.io; +const Rand = std.Rand; +const os = std.os; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("Welcome to the Guess Number Game in Zig.\n"); + %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n"); var seed : u32 = undefined; const seed_bytes = (&u8)(&seed)[0...4]; - %%os_get_random_bytes(seed_bytes); + %%os.get_random_bytes(seed_bytes); - var rand = rand_new(seed); + var rand = Rand.init(seed); const answer = rand.range_u64(0, 100) + 1; while (true) { - %%stdout.printf("\nGuess a number between 1 and 100: "); + %%io.stdout.printf("\nGuess a number between 1 and 100: "); var line_buf : [20]u8 = undefined; - const line_len = stdin.read(line_buf) %% |err| { - %%stdout.printf("Unable to read from stdin.\n"); + const line_len = io.stdin.read(line_buf) %% |err| { + %%io.stdout.printf("Unable to read from stdin.\n"); return err; }; - const guess = parse_u64(line_buf[0...line_len - 1], 10) %% { - %%stdout.printf("Invalid number.\n"); + const guess = io.parse_u64(line_buf[0...line_len - 1], 10) %% { + %%io.stdout.printf("Invalid number.\n"); continue; }; if (guess > answer) { - %%stdout.printf("Guess lower.\n"); + %%io.stdout.printf("Guess lower.\n"); } else if (guess < answer) { - %%stdout.printf("Guess higher.\n"); + %%io.stdout.printf("Guess higher.\n"); } else { - %%stdout.printf("You win!\n"); + %%io.stdout.printf("You win!\n"); return; } } diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 90e330b67d..10611da9b5 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -1,7 +1,5 @@ -export executable "hello"; - -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("Hello, world!\n"); + %%io.stdout.printf("Hello, world!\n"); } diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 206886d3e8..1f7ba9046a 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -1,11 +1,6 @@ -#link("c") -export executable "hello"; - -c_import { - @c_include("stdio.h"); -} +const c = @c_import(@c_include("stdio.h")); export fn main(argc: c_int, argv: &&u8) -> c_int { - printf(c"Hello, world!\n"); + c.printf(c"Hello, world!\n"); return 0; } diff --git a/src/all_types.hpp b/src/all_types.hpp index 3b7c02d3ec..6d6affd440 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -76,6 +76,7 @@ struct ConstExprValue { ConstStructValue x_struct; ConstArrayValue x_array; ConstPtrValue x_ptr; + ImportTableEntry *x_import; } data; }; @@ -91,6 +92,7 @@ enum ReturnKnowledge { struct Expr { TypeTableEntry *type_entry; ReturnKnowledge return_knowledge; + VariableTableEntry *variable; LLVMValueRef const_llvm_val; ConstExprValue const_val; @@ -103,13 +105,30 @@ struct StructValExprCodeGen { AstNode *source_node; }; +enum VisibMod { + VisibModPrivate, + VisibModPub, + VisibModExport, +}; + +enum TldResolution { + TldResolutionUnresolved, + TldResolutionInvalid, + TldResolutionOk, +}; + struct TopLevelDecl { - // reminder: hash tables must be initialized before use - HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> deps; + // populated by parser Buf *name; + ZigList<AstNode *> *directives; + VisibMod visib_mod; + + // populated by semantic analyzer ImportTableEntry *import; // set this flag temporarily to detect infinite loops - bool in_current_deps; + bool dep_loop_flag; + TldResolution resolution; + AstNode *parent_decl; }; struct TypeEnumField { @@ -120,7 +139,6 @@ struct TypeEnumField { enum NodeType { NodeTypeRoot, - NodeTypeRootExportDecl, NodeTypeFnProto, NodeTypeFnDef, NodeTypeFnDecl, @@ -143,8 +161,7 @@ enum NodeType { NodeTypeArrayAccessExpr, NodeTypeSliceExpr, NodeTypeFieldAccessExpr, - NodeTypeImport, - NodeTypeCImport, + NodeTypeUse, NodeTypeBoolLiteral, NodeTypeNullLiteral, NodeTypeUndefinedLiteral, @@ -173,15 +190,8 @@ struct AstNodeRoot { ZigList<AstNode *> top_level_decls; }; -enum VisibMod { - VisibModPrivate, - VisibModPub, - VisibModExport, -}; - struct AstNodeFnProto { - ZigList<AstNode *> *directives; // can be null if no directives - VisibMod visib_mod; + TopLevelDecl top_level_decl; Buf name; ZigList<AstNode *> params; AstNode *return_type; @@ -191,13 +201,10 @@ struct AstNodeFnProto { // populated by semantic analyzer: - // the struct decl node this fn proto is inside. can be null. - AstNode *struct_node; // the function definition this fn proto is inside. can be null. AstNode *fn_def_node; FnTableEntry *fn_table_entry; bool skip; - TopLevelDecl top_level_decl; Expr resolved_expr; }; @@ -263,41 +270,36 @@ struct AstNodeDefer { }; struct AstNodeVariableDeclaration { + TopLevelDecl top_level_decl; Buf symbol; bool is_const; bool is_extern; - VisibMod visib_mod; // one or both of type and expr will be non null AstNode *type; AstNode *expr; - ZigList<AstNode *> *directives; // populated by semantic analyzer - TopLevelDecl top_level_decl; Expr resolved_expr; VariableTableEntry *variable; }; struct AstNodeTypeDecl { - VisibMod visib_mod; - ZigList<AstNode *> *directives; + TopLevelDecl top_level_decl; Buf symbol; AstNode *child_type; // populated by semantic analyzer - TopLevelDecl top_level_decl; // if this is set, don't process the node; we've already done so // and here is the type (with id TypeTableEntryIdTypeDecl) TypeTableEntry *override_type; + TypeTableEntry *child_type_entry; }; struct AstNodeErrorValueDecl { + TopLevelDecl top_level_decl; Buf name; - VisibMod visib_mod; - ZigList<AstNode *> *directives; // populated by semantic analyzer - TopLevelDecl top_level_decl; ErrorTableEntry *err; }; @@ -430,12 +432,6 @@ struct AstNodeDirective { AstNode *expr; }; -struct AstNodeRootExportDecl { - Buf type; - Buf name; - ZigList<AstNode *> *directives; -}; - enum PrefixOp { PrefixOpInvalid, PrefixOpBoolNot, @@ -458,19 +454,8 @@ struct AstNodePrefixOpExpr { Expr resolved_expr; }; -struct AstNodeImport { - Buf path; - ZigList<AstNode *> *directives; - VisibMod visib_mod; - - // populated by semantic analyzer - ImportTableEntry *import; -}; - -struct AstNodeCImport { - ZigList<AstNode *> *directives; - VisibMod visib_mod; - AstNode *block; +struct AstNodeUse { + AstNode *expr; // populated by semantic analyzer TopLevelDecl top_level_decl; @@ -600,23 +585,21 @@ enum ContainerKind { }; struct AstNodeStructDecl { + TopLevelDecl top_level_decl; Buf name; ContainerKind kind; ZigList<AstNode *> fields; ZigList<AstNode *> fns; - ZigList<AstNode *> *directives; - VisibMod visib_mod; // populated by semantic analyzer + BlockContext *block_context; TypeTableEntry *type_entry; - TopLevelDecl top_level_decl; }; struct AstNodeStructField { + TopLevelDecl top_level_decl; Buf name; AstNode *type; - ZigList<AstNode *> *directives; - VisibMod visib_mod; }; struct AstNodeStringLiteral { @@ -695,8 +678,6 @@ struct AstNodeSymbolExpr { // populated by semantic analyzer Expr resolved_expr; - VariableTableEntry *variable; - FnTableEntry *fn_entry; // set this to instead of analyzing the node, pretend it's a type entry and it's this one. TypeTableEntry *override_type_entry; TypeEnumField *enum_field; @@ -750,7 +731,6 @@ struct AstNode { BlockContext *block_context; union { AstNodeRoot root; - AstNodeRootExportDecl root_export_decl; AstNodeFnDef fn_def; AstNodeFnDecl fn_decl; AstNodeFnProto fn_proto; @@ -768,8 +748,7 @@ struct AstNode { AstNodeFnCallExpr fn_call_expr; AstNodeArrayAccessExpr array_access_expr; AstNodeSliceExpr slice_expr; - AstNodeImport import; - AstNodeCImport c_import; + AstNodeUse use; AstNodeIfBoolExpr if_bool_expr; AstNodeIfVarExpr if_var_expr; AstNodeWhileExpr while_expr; @@ -868,8 +847,7 @@ struct TypeTableEntryStruct { uint64_t size_bytes; bool is_invalid; // true if any fields are invalid bool is_unknown_size_array; - // reminder: hash tables must be initialized before use - HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table; + BlockContext *block_context; // set this flag temporarily to detect infinite loops bool embedded_in_current; @@ -895,8 +873,7 @@ struct TypeTableEntryEnum { TypeTableEntry *tag_type; TypeTableEntry *union_type; - // reminder: hash tables must be initialized before use - HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table; + BlockContext *block_context; // set this flag temporarily to detect infinite loops bool embedded_in_current; @@ -947,6 +924,7 @@ enum TypeTableEntryId { TypeTableEntryIdEnum, TypeTableEntryIdFn, TypeTableEntryIdTypeDecl, + TypeTableEntryIdNamespace, }; struct TypeTableEntry { @@ -979,26 +957,26 @@ struct TypeTableEntry { TypeTableEntry *error_parent; }; -struct ImporterInfo { - ImportTableEntry *import; - AstNode *source_node; +struct PackageTableEntry { + Buf root_src_dir; + Buf root_src_path; // relative to root_src_dir + + // reminder: hash tables must be initialized before use + HashMap<Buf *, PackageTableEntry *, buf_hash, buf_eql_buf> package_table; }; struct ImportTableEntry { AstNode *root; - Buf *path; // relative to root_source_dir + Buf *path; // relative to root_package->root_src_dir + PackageTableEntry *package; LLVMZigDIFile *di_file; Buf *source_code; ZigList<int> *line_offsets; BlockContext *block_context; - ZigList<ImporterInfo> importers; AstNode *c_import_node; bool any_imports_failed; - // reminder: hash tables must be initialized before use - HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table; - HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table; - HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table; + ZigList<AstNode *> use_decls; }; struct FnTableEntry { @@ -1008,14 +986,12 @@ struct FnTableEntry { ImportTableEntry *import_entry; // Required to be a pre-order traversal of the AST. (parents must come before children) ZigList<BlockContext *> all_block_contexts; - TypeTableEntry *member_of_struct; Buf symbol_name; TypeTableEntry *type_entry; // function type bool is_inline; bool internal_linkage; bool is_extern; bool is_test; - uint32_t ref_count; // if this is 0 we don't have to codegen it ZigList<AstNode *> cast_alloca_list; ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list; @@ -1042,6 +1018,8 @@ enum BuiltinFnId { BuiltinFnIdConstEval, BuiltinFnIdCtz, BuiltinFnIdClz, + BuiltinFnIdImport, + BuiltinFnIdCImport, }; struct BuiltinFnEntry { @@ -1061,17 +1039,22 @@ struct CodeGen { LLVMZigDIBuilder *dbuilder; LLVMZigDICompileUnit *compile_unit; - ZigList<Buf *> lib_search_paths; - ZigList<Buf *> link_libs; + ZigList<Buf *> link_libs; // non-libc link libs // reminder: hash tables must be initialized before use HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table; HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> primitive_type_table; - HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> unresolved_top_level_decls; HashMap<FnTypeId *, TypeTableEntry *, fn_type_id_hash, fn_type_id_eql> fn_type_table; HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table; + ZigList<ImportTableEntry *> import_queue; + int import_queue_index; + ZigList<AstNode *> export_queue; + int export_queue_index; + ZigList<AstNode *> use_queue; + int use_queue_index; + uint32_t next_unresolved_index; struct { @@ -1095,6 +1078,7 @@ struct CodeGen { TypeTableEntry *entry_unreachable; TypeTableEntry *entry_type; TypeTableEntry *entry_invalid; + TypeTableEntry *entry_namespace; TypeTableEntry *entry_num_lit_int; TypeTableEntry *entry_num_lit_float; TypeTableEntry *entry_undef; @@ -1126,7 +1110,8 @@ struct CodeGen { LLVMTargetMachineRef target_machine; LLVMZigDIFile *dummy_di_file; bool is_native_target; - Buf *root_source_dir; + PackageTableEntry *root_package; + PackageTableEntry *std_package; Buf *root_out_name; bool windows_subsystem_windows; bool windows_subsystem_console; @@ -1176,6 +1161,8 @@ struct CodeGen { ZigList<const char *> lib_dirs; uint32_t test_fn_count; + + bool check_unused; }; struct VariableTableEntry { @@ -1202,7 +1189,8 @@ struct BlockContext { AstNode *node; // any variables that are introduced by this scope - HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table; + HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> decl_table; + HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> var_table; // if the block is inside a function, this is the function it is in: FnTableEntry *fn_entry; diff --git a/src/analyze.cpp b/src/analyze.cpp index e73902f118..04980b05e8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -14,8 +14,10 @@ #include "config.h" #include "ast_render.hpp" -static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, +static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); +static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import, + BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only); static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type); @@ -27,13 +29,18 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry * static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node); -static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, BlockContext *context, - FnTableEntry *fn); +static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn); static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type); static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node, TypeTableEntry *expected_type, uint64_t x); -static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node); -static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node); +static AstNode *find_decl(BlockContext *context, Buf *name); +static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only); +static TopLevelDecl *get_as_top_level_decl(AstNode *node); +static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *source_node, + AstNodeVariableDeclaration *variable_declaration, + bool expr_is_maybe, AstNode *decl_node); +static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node); static AstNode *first_executing_node(AstNode *node) { switch (node->type) { @@ -52,7 +59,6 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeSwitchRange: return first_executing_node(node->data.switch_range.start); case NodeTypeRoot: - case NodeTypeRootExportDecl: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: @@ -69,8 +75,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeCharLiteral: case NodeTypeSymbol: case NodeTypePrefixOpExpr: - case NodeTypeImport: - case NodeTypeCImport: + case NodeTypeUse: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: @@ -109,43 +114,47 @@ ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { return err; } +ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) { + // if this assert fails, then parseh generated code that + // failed semantic analysis, which isn't supposed to happen + assert(!node->owner->c_import_node); + + ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, + node->owner->source_code, node->owner->line_offsets, msg); + + err_msg_add_note(parent_msg, err); + return err; +} + TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { TypeTableEntry *entry = allocate<TypeTableEntry>(1); entry->arrays_by_size.init(2); entry->id = id; + return entry; +} - switch (id) { - case TypeTableEntryIdInvalid: - case TypeTableEntryIdMetaType: - case TypeTableEntryIdVoid: - case TypeTableEntryIdBool: - case TypeTableEntryIdUnreachable: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdPointer: - case TypeTableEntryIdArray: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdMaybe: - case TypeTableEntryIdFn: - case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: - case TypeTableEntryIdUndefLit: - case TypeTableEntryIdTypeDecl: - // nothing to init - break; - case TypeTableEntryIdStruct: - entry->data.structure.fn_table.init(8); - break; - case TypeTableEntryIdEnum: - entry->data.enumeration.fn_table.init(8); - break; - +static BlockContext **get_container_block_context_ptr(TypeTableEntry *type_entry) { + if (type_entry->id == TypeTableEntryIdStruct) { + return &type_entry->data.structure.block_context; + } else if (type_entry->id == TypeTableEntryIdEnum) { + return &type_entry->data.enumeration.block_context; } + zig_unreachable(); +} +static BlockContext *get_container_block_context(TypeTableEntry *type_entry) { + return *get_container_block_context_ptr(type_entry); +} + +static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node, + BlockContext *parent_context) +{ + TypeTableEntry *entry = new_type_table_entry(id); + *get_container_block_context_ptr(entry) = new_block_context(source_node, parent_context); return entry; } + static int bits_needed_for_unsigned(uint64_t x) { if (x <= UINT8_MAX) { return 8; @@ -182,6 +191,7 @@ static bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdPureError: case TypeTableEntryIdFn: case TypeTableEntryIdTypeDecl: + case TypeTableEntryIdNamespace: return true; } zig_unreachable(); @@ -671,7 +681,7 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, ContainerKind kind, AstNode *decl_node, const char *name) { TypeTableEntryId type_id = container_to_type(kind); - TypeTableEntry *entry = new_type_table_entry(type_id); + TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, import->block_context); switch (kind) { case ContainerKindStruct: @@ -730,13 +740,19 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { return const_val->data.x_type; } +static TypeTableEntry *analyze_type_expr_pointer_only(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node, bool pointer_only) +{ + AstNode **node_ptr = node->parent_field; + analyze_expression_pointer_only(g, import, context, nullptr, *node_ptr, pointer_only); + return resolve_type(g, *node_ptr); +} + // Calls analyze_expression on node, and then resolve_type. static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { - AstNode **node_ptr = node->parent_field; - analyze_expression(g, import, context, nullptr, *node_ptr); - return resolve_type(g, *node_ptr); + return analyze_type_expr_pointer_only(g, import, context, node, false); } static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -750,7 +766,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor } FnTypeId fn_type_id = {0}; - fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); + fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport); fn_type_id.is_naked = is_naked; fn_type_id.is_cold = is_cold; fn_type_id.param_count = node->data.fn_proto.params.length; @@ -782,6 +798,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor case TypeTableEntryIdUndefLit: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: + case TypeTableEntryIdNamespace: fn_proto->skip = true; add_node_error(g, child->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed'", buf_ptr(&type_entry->name))); @@ -880,9 +897,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t bool is_naked = false; bool is_test = false; - if (fn_proto->directives) { - for (int i = 0; i < fn_proto->directives->length; i += 1) { - AstNode *directive_node = fn_proto->directives->at(i); + if (fn_proto->top_level_decl.directives) { + for (int 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")) { @@ -907,12 +924,12 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); } } else if (buf_eql_str(name, "condition")) { - if (fn_proto->visib_mod == VisibModExport) { + 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->visib_mod = VisibModPub; + fn_proto->top_level_decl.visib_mod = VisibModPub; } } else { add_node_error(g, directive_node, @@ -925,12 +942,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t } } - bool is_internal = (fn_proto->visib_mod != VisibModExport); + 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; - if (!is_internal) { - fn_table_entry->ref_count += 1; - } @@ -972,18 +986,19 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute); } - // Add debug info. - unsigned line_number = node->line + 1; - unsigned scope_line = line_number; - bool is_definition = fn_table_entry->fn_def_node != nullptr; - unsigned flags = 0; - bool is_optimized = g->is_release_build; - LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder, - import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "", - import->di_file, line_number, - fn_type->di_type, fn_table_entry->internal_linkage, - is_definition, scope_line, flags, is_optimized, fn_table_entry->fn_value); if (fn_table_entry->fn_def_node) { + // Add debug info. + unsigned line_number = node->line + 1; + unsigned scope_line = line_number; + bool is_definition = fn_table_entry->fn_def_node != nullptr; + unsigned flags = 0; + bool is_optimized = g->is_release_build; + LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder, + import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "", + import->di_file, line_number, + fn_type->di_type, fn_table_entry->internal_linkage, + is_definition, scope_line, flags, is_optimized, fn_table_entry->fn_value); + BlockContext *context = new_block_context(fn_table_entry->fn_def_node, import->block_context); fn_table_entry->fn_def_node->data.fn_def.block_context = context; context->di_scope = LLVMZigSubprogramToScope(subprogram); @@ -1295,59 +1310,43 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE struct_type->zero_bits = (debug_size_in_bits == 0); } -static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, - AstNode *proto_node) -{ +static void get_fully_qualified_decl_name(Buf *buf, AstNode *decl_node, uint8_t sep) { + TopLevelDecl *tld = get_as_top_level_decl(decl_node); + AstNode *parent_decl = tld->parent_decl; + + if (parent_decl) { + get_fully_qualified_decl_name(buf, parent_decl, sep); + buf_append_char(buf, sep); + buf_append_buf(buf, tld->name); + } else { + buf_init_from_buf(buf, tld->name); + } +} + +static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *proto_node) { if (proto_node->data.fn_proto.skip) { return; } + + AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl; + AstNode *fn_def_node = proto_node->data.fn_proto.fn_def_node; - AstNode *struct_node = proto_node->data.fn_proto.struct_node; bool is_extern = proto_node->data.fn_proto.is_extern; - TypeTableEntry *struct_type; - if (struct_node) { - assert(struct_node->type == NodeTypeStructDecl); - struct_type = struct_node->data.struct_decl.type_entry; - } else { - struct_type = nullptr; - } Buf *proto_name = &proto_node->data.fn_proto.name; - auto fn_table = struct_type ? &struct_type->data.structure.fn_table : &import->fn_table; - - auto entry = fn_table->maybe_get(proto_name); - bool skip = false; - bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate); - if (entry) { - add_node_error(g, proto_node, - buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); - proto_node->data.fn_proto.skip = true; - skip = true; - } if (!is_extern && proto_node->data.fn_proto.is_var_args) { add_node_error(g, proto_node, buf_sprintf("variadic arguments only allowed in extern functions")); } - if (skip) { - return; - } FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); fn_table_entry->import_entry = import; fn_table_entry->proto_node = proto_node; fn_table_entry->fn_def_node = fn_def_node; fn_table_entry->is_extern = is_extern; - fn_table_entry->member_of_struct = struct_type; - if (struct_type) { - buf_resize(&fn_table_entry->symbol_name, 0); - buf_appendf(&fn_table_entry->symbol_name, "%s_%s", - buf_ptr(&struct_type->name), - buf_ptr(proto_name)); - } else { - buf_init_from_buf(&fn_table_entry->symbol_name, proto_name); - } + get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_'); g->fn_protos.append(fn_table_entry); @@ -1355,39 +1354,13 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, g->fn_defs.append(fn_table_entry); } - fn_table->put(proto_name, fn_table_entry); - - bool is_main_fn = !struct_type && (import == g->root_import) && buf_eql_str(proto_name, "main"); + bool is_main_fn = !parent_decl && (import == g->root_import) && buf_eql_str(proto_name, "main"); if (is_main_fn) { g->main_fn = fn_table_entry; - - if (g->bootstrap_import && !g->is_test_build) { - g->bootstrap_import->fn_table.put(buf_create_from_str("zig_user_main"), fn_table_entry); - } - } - bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main"); - if (is_test_main_fn) { - assert(g->bootstrap_import); - assert(g->is_test_build); - g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); } proto_node->data.fn_proto.fn_table_entry = fn_table_entry; resolve_function_proto(g, proto_node, fn_table_entry, import); - - if (is_pub && !struct_type) { - for (int i = 0; i < import->importers.length; i += 1) { - ImporterInfo importer = import->importers.at(i); - auto table_entry = importer.import->fn_table.maybe_get(proto_name); - if (table_entry) { - add_node_error(g, importer.source_node, - buf_sprintf("import of function '%s' overrides existing definition", - buf_ptr(proto_name))); - } else { - importer.import->fn_table.put(proto_name, fn_table_entry); - } - } - } } static void preview_error_value_decl(CodeGen *g, AstNode *node) { @@ -1403,110 +1376,40 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) { // duplicate error definitions allowed and they get the same value err->value = existing_entry->value->value; } else { + assert(g->error_value_count < (1 << g->err_tag_type->data.integral.bit_count)); err->value = g->error_value_count; g->error_value_count += 1; g->error_table.put(&err->name, err); } node->data.error_value_decl.err = err; + node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk; } -static void resolve_error_value_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { - assert(node->type == NodeTypeErrorValueDecl); - - ErrorTableEntry *err = node->data.error_value_decl.err; - - import->error_table.put(&err->name, err); - - bool is_pub = (node->data.error_value_decl.visib_mod != VisibModPrivate); - if (is_pub) { - for (int i = 0; i < import->importers.length; i += 1) { - ImporterInfo importer = import->importers.at(i); - importer.import->error_table.put(&err->name, err); - } - } -} - -static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *parent_import, AstNode *node) { - assert(node->type == NodeTypeCImport); - - AstNode *block_node = node->data.c_import.block; - - BlockContext *child_context = new_block_context(node, parent_import->block_context); - child_context->c_import_buf = buf_alloc(); - - TypeTableEntry *resolved_type = analyze_block_expr(g, parent_import, child_context, - g->builtin_types.entry_void, block_node); - - if (resolved_type->id == TypeTableEntryIdInvalid) { +static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { + TopLevelDecl *tld = get_as_top_level_decl(node); + if (tld->resolution != TldResolutionUnresolved) { return; } - - find_libc_include_path(g); - - ImportTableEntry *child_import = allocate<ImportTableEntry>(1); - child_import->fn_table.init(32); - child_import->type_table.init(8); - child_import->error_table.init(8); - child_import->c_import_node = node; - - child_import->importers.append({parent_import, node}); - - if (node->data.c_import.visib_mod != VisibModPrivate) { - for (int i = 0; i < parent_import->importers.length; i += 1) { - ImporterInfo importer = parent_import->importers.at(i); - child_import->importers.append(importer); - } - } - - ZigList<ErrorMsg *> errors = {0}; - - int err; - if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) { - zig_panic("unable to parse h file: %s\n", err_str(err)); - } - - if (errors.length > 0) { - ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed")); - for (int i = 0; i < errors.length; i += 1) { - ErrorMsg *err_msg = errors.at(i); - err_msg_add_note(parent_err_msg, err_msg); - } - - for (int i = 0; i < child_import->importers.length; i += 1) { - child_import->importers.at(i).import->any_imports_failed = true; - } + if (pointer_only && node->type == NodeTypeStructDecl) { return; } - if (g->verbose) { - fprintf(stderr, "\nc_import:\n"); - fprintf(stderr, "-----------\n"); - ast_render(stderr, child_import->root, 4); - } + ImportTableEntry *import = tld->import; + assert(import); - child_import->di_file = parent_import->di_file; - child_import->block_context = new_block_context(child_import->root, nullptr); - - detect_top_level_decl_deps(g, child_import, child_import->root); - analyze_top_level_decls_root(g, child_import, child_import->root); -} - -static void satisfy_dep(CodeGen *g, AstNode *node) { - Buf *name = get_resolved_top_level_decl(node)->name; - if (name) { - g->unresolved_top_level_decls.maybe_remove(name); + if (tld->dep_loop_flag) { + add_node_error(g, node, buf_sprintf("'%s' depends on itself", buf_ptr(tld->name))); + tld->resolution = TldResolutionInvalid; + return; + } else { + tld->dep_loop_flag = true; } -} -static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeFnProto: preview_fn_proto(g, import, node); break; - case NodeTypeRootExportDecl: - // handled earlier - return; case NodeTypeStructDecl: { TypeTableEntry *type_entry = node->data.struct_decl.type_entry; @@ -1526,8 +1429,10 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode } case NodeTypeVariableDeclaration: { - VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context, - nullptr, node); + AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; + VariableTableEntry *var = analyze_variable_declaration_raw(g, import, import->block_context, + node, variable_declaration, false, node); + g->global_vars.append(var); break; } @@ -1547,34 +1452,13 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode entry = get_typedecl_type(g, buf_ptr(decl_name), child_type); } } - - import->type_table.put(decl_name, entry); - - bool is_pub = (node->data.type_decl.visib_mod != VisibModPrivate); - if (is_pub) { - for (int i = 0; i < import->importers.length; i += 1) { - ImporterInfo importer = import->importers.at(i); - auto table_entry = importer.import->type_table.maybe_get(&entry->name); - if (table_entry) { - add_node_error(g, importer.source_node, - buf_sprintf("import of type '%s' overrides existing definition", - buf_ptr(&entry->name))); - } else { - importer.import->type_table.put(&entry->name, entry); - } - } - } - + node->data.type_decl.child_type_entry = entry; break; } case NodeTypeErrorValueDecl: - resolve_error_value_decl(g, import, node); break; - case NodeTypeImport: - // nothing to do here - return; - case NodeTypeCImport: - resolve_c_import_decl(g, import, node); + case NodeTypeUse: + zig_panic("TODO resolve_top_level_decl NodeTypeUse"); break; case NodeTypeFnDef: case NodeTypeDirective: @@ -1619,8 +1503,8 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode zig_unreachable(); } - - satisfy_dep(g, node); + tld->resolution = TldResolutionOk; + tld->dep_loop_flag = false; } static FnTableEntry *get_context_fn_entry(BlockContext *context) { @@ -1656,6 +1540,7 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) { case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNamespace: return false; case TypeTableEntryIdBool: @@ -2063,7 +1948,8 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { BlockContext *context = allocate<BlockContext>(1); context->node = node; context->parent = parent; - context->variable_table.init(4); + context->decl_table.init(1); + context->var_table.init(1); if (parent) { context->parent_loop_node = parent->parent_loop_node; @@ -2085,23 +1971,28 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { return context; } -static VariableTableEntry *find_variable(BlockContext *context, Buf *name, bool local_only) { - while (context && (!local_only || context->fn_entry)) { - auto entry = context->variable_table.maybe_get(name); - if (entry) +static AstNode *find_decl(BlockContext *context, Buf *name) { + while (context) { + auto entry = context->decl_table.maybe_get(name); + if (entry) { return entry->value; - + } context = context->parent; } return nullptr; } -static TypeTableEntry *find_container(ImportTableEntry *import, Buf *name) { - auto entry = import->type_table.maybe_get(name); - if (entry) - return entry->value; - else - return nullptr; +static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name) { + BlockContext *context = orig_context; + while (context) { + auto entry = context->var_table.maybe_get(name); + if (entry) { + return entry->value; + } + context = context->parent; + } + + return nullptr; } static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) { @@ -2155,6 +2046,7 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdStruct); + assert(type_entry->data.structure.complete); for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *field = &type_entry->data.structure.fields[i]; if (buf_eql_buf(field->name, name)) { @@ -2330,8 +2222,8 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i { assert(node->type == NodeTypeFieldAccessExpr); - AstNode *struct_expr_node = node->data.field_access_expr.struct_expr; - TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, struct_expr_node); + AstNode **struct_expr_node = &node->data.field_access_expr.struct_expr; + TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node); Buf *field_name = &node->data.field_access_expr.field_name; bool wrapped_in_fn_call = node->data.field_access_expr.is_fn_call; @@ -2342,17 +2234,30 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ? struct_type : struct_type->data.pointer.child_type; + if (!bare_struct_type->data.structure.complete) { + resolve_struct_type(g, bare_struct_type->data.structure.decl_node->owner, bare_struct_type); + } + node->data.field_access_expr.bare_struct_type = bare_struct_type; node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_struct_type, field_name); if (node->data.field_access_expr.type_struct_field) { return node->data.field_access_expr.type_struct_field->type_entry; } else if (wrapped_in_fn_call) { - auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(field_name); - if (table_entry) { + BlockContext *container_block_context = get_container_block_context(bare_struct_type); + auto entry = container_block_context->decl_table.maybe_get(field_name); + AstNode *fn_decl_node = entry ? entry->value : nullptr; + if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) { + resolve_top_level_decl(g, fn_decl_node, false); + TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node); + if (tld->resolution == TldResolutionInvalid) { + return g->builtin_types.entry_invalid; + } + node->data.field_access_expr.is_member_fn = true; - return resolve_expr_const_val_as_fn(g, node, context, table_entry->value); + FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry; + return resolve_expr_const_val_as_fn(g, node, fn_entry); } else { - add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'", + add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); return g->builtin_types.entry_invalid; } @@ -2372,7 +2277,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i return g->builtin_types.entry_invalid; } } else if (struct_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *child_type = resolve_type(g, struct_expr_node); + TypeTableEntry *child_type = resolve_type(g, *struct_expr_node); if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; @@ -2381,12 +2286,15 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i } else if (child_type->id == TypeTableEntryIdEnum) { return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name); } else if (child_type->id == TypeTableEntryIdStruct) { - auto entry = child_type->data.structure.fn_table.maybe_get(field_name); - if (entry) { - return resolve_expr_const_val_as_fn(g, node, context, entry->value); + BlockContext *container_block_context = get_container_block_context(child_type); + auto entry = container_block_context->decl_table.maybe_get(field_name); + AstNode *decl_node = entry ? entry->value : nullptr; + if (decl_node) { + bool pointer_only = false; + return analyze_decl_ref(g, node, decl_node, pointer_only); } else { add_node_error(g, node, - buf_sprintf("struct '%s' has no function called '%s'", + buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return g->builtin_types.entry_invalid; } @@ -2397,6 +2305,26 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name))); return g->builtin_types.entry_invalid; } + } else if (struct_type->id == TypeTableEntryIdNamespace) { + ConstExprValue *const_val = &get_resolved_expr(*struct_expr_node)->const_val; + assert(const_val->ok); + ImportTableEntry *namespace_import = const_val->data.x_import; + AstNode *decl_node = find_decl(namespace_import->block_context, field_name); + if (decl_node) { + TopLevelDecl *tld = get_as_top_level_decl(decl_node); + if (tld->visib_mod == VisibModPrivate) { + ErrorMsg *msg = add_node_error(g, node, + buf_sprintf("'%s' is private", buf_ptr(field_name))); + add_error_note(g, msg, decl_node, buf_sprintf("declared here")); + } + bool pointer_only = false; + return analyze_decl_ref(g, node, decl_node, pointer_only); + } else { + add_node_error(g, node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), + buf_ptr(namespace_import->path))); + return g->builtin_types.entry_invalid; + } } else { if (struct_type->id != TypeTableEntryIdInvalid) { add_node_error(g, node, @@ -2500,12 +2428,7 @@ static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode return other_expr->type_entry; } -static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, BlockContext *context, - FnTableEntry *fn) -{ - if (!context->codegen_excluded) { - fn->ref_count += 1; - } +static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn) { Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_fn = fn; @@ -2635,7 +2558,7 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode * static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, Buf *err_name) { - auto err_table_entry = import->error_table.maybe_get(err_name); + auto err_table_entry = g->error_table.maybe_get(err_name); if (err_table_entry) { return resolve_expr_const_val_as_err(g, node, err_table_entry->value); @@ -2647,9 +2570,48 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry * return g->builtin_types.entry_invalid; } +static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var) { + get_resolved_expr(source_node)->variable = var; + if (var->is_const) { + AstNode *decl_node = var->decl_node; + if (decl_node->type == NodeTypeVariableDeclaration) { + AstNode *expr_node = decl_node->data.variable_declaration.expr; + ConstExprValue *other_const_val = &get_resolved_expr(expr_node)->const_val; + if (other_const_val->ok) { + return resolve_expr_const_val_as_other_expr(g, source_node, expr_node); + } + } + } + return var->type; +} + +static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, + bool pointer_only) +{ + resolve_top_level_decl(g, decl_node, pointer_only); + TopLevelDecl *tld = get_as_top_level_decl(decl_node); + if (tld->resolution == TldResolutionInvalid) { + return g->builtin_types.entry_invalid; + } + + if (decl_node->type == NodeTypeVariableDeclaration) { + VariableTableEntry *var = decl_node->data.variable_declaration.variable; + return analyze_var_ref(g, source_node, var); + } else if (decl_node->type == NodeTypeFnProto) { + FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; + assert(fn_entry->type_entry); + return resolve_expr_const_val_as_fn(g, source_node, fn_entry); + } else if (decl_node->type == NodeTypeStructDecl) { + return resolve_expr_const_val_as_type(g, source_node, decl_node->data.struct_decl.type_entry); + } else if (decl_node->type == NodeTypeTypeDecl) { + return resolve_expr_const_val_as_type(g, source_node, decl_node->data.type_decl.child_type_entry); + } else { + zig_unreachable(); + } +} static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node) + TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { if (node->data.symbol_expr.override_type_entry) { return resolve_expr_const_val_as_type(g, node, node->data.symbol_expr.override_type_entry); @@ -2662,32 +2624,14 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value); } - VariableTableEntry *var = find_variable(context, variable_name, false); + VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { - node->data.symbol_expr.variable = var; - if (var->is_const) { - AstNode *decl_node = var->decl_node; - if (decl_node->type == NodeTypeVariableDeclaration) { - AstNode *expr_node = decl_node->data.variable_declaration.expr; - ConstExprValue *other_const_val = &get_resolved_expr(expr_node)->const_val; - if (other_const_val->ok) { - return resolve_expr_const_val_as_other_expr(g, node, expr_node); - } - } - } - return var->type; + return analyze_var_ref(g, node, var); } - TypeTableEntry *container_type = find_container(import, variable_name); - if (container_type) { - return resolve_expr_const_val_as_type(g, node, container_type); - } - - auto fn_table_entry = import->fn_table.maybe_get(variable_name); - if (fn_table_entry) { - assert(fn_table_entry->value->type_entry); - node->data.symbol_expr.fn_entry = fn_table_entry->value; - return resolve_expr_const_val_as_fn(g, node, context, fn_table_entry->value); + AstNode *decl_node = find_decl(context, variable_name); + if (decl_node) { + return analyze_decl_ref(g, node, decl_node, pointer_only); } if (import->any_imports_failed) { @@ -2760,18 +2704,21 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc TypeTableEntry *expected_rhs_type = nullptr; lhs_node->block_context = block_context; if (lhs_node->type == NodeTypeSymbol) { - Buf *name = &lhs_node->data.symbol_expr.symbol; - if (purpose == LValPurposeAddressOf) { - expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node); - } else { - VariableTableEntry *var = find_variable(block_context, name, false); + bool pointer_only = purpose == LValPurposeAddressOf; + expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node, pointer_only); + if (expected_rhs_type->id == TypeTableEntryIdInvalid) { + return g->builtin_types.entry_invalid; + } + if (purpose != LValPurposeAddressOf) { + Buf *name = &lhs_node->data.symbol_expr.symbol; + VariableTableEntry *var = find_variable(g, block_context, name); if (var) { if (var->is_const) { add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant")); expected_rhs_type = g->builtin_types.entry_invalid; } else { expected_rhs_type = var->type; - lhs_node->data.symbol_expr.variable = var; + get_resolved_expr(lhs_node)->variable = var; } } else { add_node_error(g, lhs_node, @@ -3176,29 +3123,36 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Impor if (name) { buf_init_from_buf(&variable_entry->name, name); - VariableTableEntry *existing_var; - existing_var = find_variable(context, name, context->fn_entry != nullptr); - - if (existing_var) { - add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); - variable_entry->type = g->builtin_types.entry_invalid; - } else { - auto primitive_table_entry = g->primitive_type_table.maybe_get(name); - TypeTableEntry *type; - if (primitive_table_entry) { - type = primitive_table_entry->value; - } else { - type = find_container(import, name); - } - if (type) { - add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); + if (type_entry->id != TypeTableEntryIdInvalid) { + VariableTableEntry *existing_var = find_variable(g, context, name); + if (existing_var) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); + add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); variable_entry->type = g->builtin_types.entry_invalid; + } else { + auto primitive_table_entry = g->primitive_type_table.maybe_get(name); + if (primitive_table_entry) { + TypeTableEntry *type = primitive_table_entry->value; + add_node_error(g, source_node, + buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); + variable_entry->type = g->builtin_types.entry_invalid; + } else { + AstNode *decl_node = find_decl(context, name); + if (decl_node && decl_node->type != NodeTypeVariableDeclaration) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("redefinition of '%s'", buf_ptr(name))); + add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here")); + variable_entry->type = g->builtin_types.entry_invalid; + } + } } } - context->variable_table.put(&variable_entry->name, variable_entry); + context->var_table.put(&variable_entry->name, variable_entry); } else { + // TODO replace _anon with @anon and make sure all tests still pass buf_init_from_str(&variable_entry->name, "_anon"); } if (context->fn_entry) { @@ -3248,10 +3202,10 @@ static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *i static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, - bool expr_is_maybe) + bool expr_is_maybe, AstNode *decl_node) { bool is_const = variable_declaration->is_const; - bool is_export = (variable_declaration->visib_mod == VisibModExport); + bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport); bool is_extern = variable_declaration->is_extern; TypeTableEntry *explicit_type = nullptr; @@ -3312,22 +3266,6 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa variable_declaration->variable = var; - - bool is_pub = (variable_declaration->visib_mod != VisibModPrivate); - if (is_pub) { - for (int i = 0; i < import->importers.length; i += 1) { - ImporterInfo importer = import->importers.at(i); - auto table_entry = importer.import->block_context->variable_table.maybe_get(&var->name); - if (table_entry) { - add_node_error(g, importer.source_node, - buf_sprintf("import of variable '%s' overrides existing definition", - buf_ptr(&var->name))); - } else { - importer.import->block_context->variable_table.put(&var->name, var); - } - } - } - return var; } @@ -3335,7 +3273,7 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; - return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false); + return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false, nullptr); } static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import, @@ -3516,7 +3454,8 @@ static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, Bl AstNode *elem_var_node = node->data.for_expr.elem_node; elem_var_node->block_context = child_context; Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol; - node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name, child_type, true); + node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name, + child_type, true); AstNode *index_var_node = node->data.for_expr.index_node; if (index_var_node) { @@ -3673,7 +3612,8 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *child_context = new_block_context(node, parent_context); - analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true); + analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true, + nullptr); VariableTableEntry *var = node->data.if_var_expr.var_decl.variable; if (var->type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; @@ -4069,6 +4009,145 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B return g->builtin_types.entry_invalid; } +static TypeTableEntry *resolve_expr_const_val_as_import(CodeGen *g, AstNode *node, ImportTableEntry *import) { + Expr *expr = get_resolved_expr(node); + expr->const_val.ok = true; + expr->const_val.data.x_import = import; + return g->builtin_types.entry_namespace; +} + +static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node) +{ + assert(node->type == NodeTypeFnCallExpr); + + if (context != import->block_context) { + add_node_error(g, node, buf_sprintf("@import valid only at top level scope")); + return g->builtin_types.entry_invalid; + } + + AstNode *first_param_node = node->data.fn_call_expr.params.at(0); + Buf *import_target_str = resolve_const_expr_str(g, import, context, first_param_node->parent_field); + if (!import_target_str) { + return g->builtin_types.entry_invalid; + } + + Buf *import_target_path; + Buf *search_dir; + assert(import->package); + PackageTableEntry *target_package; + auto package_entry = import->package->package_table.maybe_get(import_target_str); + if (package_entry) { + target_package = package_entry->value; + import_target_path = &target_package->root_src_path; + search_dir = &target_package->root_src_dir; + } else { + // try it as a filename + target_package = import->package; + import_target_path = import_target_str; + search_dir = &import->package->root_src_dir; + } + + Buf full_path = BUF_INIT; + os_path_join(search_dir, import_target_path, &full_path); + + Buf *import_code = buf_alloc(); + Buf *abs_full_path = buf_alloc(); + int err; + if ((err = os_path_real(&full_path, abs_full_path))) { + if (err == ErrorFileNotFound) { + add_node_error(g, node, + buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); + return g->builtin_types.entry_invalid; + } else { + g->error_during_imports = true; + add_node_error(g, node, + buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); + return g->builtin_types.entry_invalid; + } + } + + auto import_entry = g->import_table.maybe_get(abs_full_path); + if (import_entry) { + return resolve_expr_const_val_as_import(g, node, import_entry->value); + } + + if ((err = os_fetch_file_path(abs_full_path, import_code))) { + if (err == ErrorFileNotFound) { + add_node_error(g, node, + buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); + return g->builtin_types.entry_invalid; + } else { + add_node_error(g, node, + buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); + return g->builtin_types.entry_invalid; + } + } + ImportTableEntry *target_import = add_source_file(g, target_package, + abs_full_path, search_dir, import_target_path, import_code); + + scan_decls(g, target_import, target_import->block_context, target_import->root); + + return resolve_expr_const_val_as_import(g, node, target_import); +} + +static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import, + BlockContext *parent_context, AstNode *node) +{ + assert(node->type == NodeTypeFnCallExpr); + + if (parent_context != parent_import->block_context) { + add_node_error(g, node, buf_sprintf("@c_import valid only at top level scope")); + return g->builtin_types.entry_invalid; + } + + AstNode *block_node = node->data.fn_call_expr.params.at(0); + + BlockContext *child_context = new_block_context(node, parent_context); + child_context->c_import_buf = buf_alloc(); + + TypeTableEntry *resolved_type = analyze_expression(g, parent_import, child_context, + g->builtin_types.entry_void, block_node); + + if (resolved_type->id == TypeTableEntryIdInvalid) { + return resolved_type; + } + + find_libc_include_path(g); + + ImportTableEntry *child_import = allocate<ImportTableEntry>(1); + child_import->c_import_node = node; + + ZigList<ErrorMsg *> errors = {0}; + + int err; + if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) { + zig_panic("unable to parse h file: %s\n", err_str(err)); + } + + if (errors.length > 0) { + ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed")); + for (int i = 0; i < errors.length; i += 1) { + ErrorMsg *err_msg = errors.at(i); + err_msg_add_note(parent_err_msg, err_msg); + } + + return g->builtin_types.entry_invalid; + } + + if (g->verbose) { + fprintf(stderr, "\nc_import:\n"); + fprintf(stderr, "-----------\n"); + ast_render(stderr, child_import->root, 4); + } + + child_import->di_file = parent_import->di_file; + child_import->block_context = new_block_context(child_import->root, nullptr); + + scan_decls(g, child_import, child_import->block_context, child_import->root); + return resolve_expr_const_val_as_import(g, node, child_import); +} + static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -4252,6 +4331,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNamespace: add_node_error(g, expr_node, buf_sprintf("type '%s' not eligible for @typeof", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; @@ -4391,6 +4471,10 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry return g->builtin_types.entry_invalid; } } + case BuiltinFnIdImport: + return analyze_import(g, import, context, node); + case BuiltinFnIdCImport: + return analyze_c_import(g, import, context, node); } zig_unreachable(); @@ -4456,12 +4540,7 @@ static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, node->data.fn_call_expr.fn_entry = fn_table_entry; - if (!context->codegen_excluded) { - fn_table_entry->ref_count += 1; - } - return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_type); - } static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -4515,10 +4594,16 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import } } else if (child_type->id == TypeTableEntryIdStruct) { Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name; - auto entry = child_type->data.structure.fn_table.maybe_get(field_name); - if (entry) { - return analyze_fn_call_raw(g, import, context, expected_type, node, - entry->value, nullptr); + BlockContext *container_block_context = get_container_block_context(child_type); + auto entry = container_block_context->decl_table.maybe_get(field_name); + AstNode *decl_node = entry ? entry->value : nullptr; + if (decl_node && decl_node->type == NodeTypeFnProto) { + bool pointer_only = false; + resolve_top_level_decl(g, decl_node, pointer_only); + + FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry; + assert(fn_entry); + return analyze_fn_call_raw(g, import, context, expected_type, node, fn_entry, nullptr); } else { add_node_error(g, node, buf_sprintf("struct '%s' has no function called '%s'", @@ -4565,19 +4650,19 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo TypeTableEntry *expected_type, AstNode *node) { PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + AstNode **expr_node = &node->data.prefix_op_expr.primary_expr; switch (prefix_op) { case PrefixOpInvalid: zig_unreachable(); case PrefixOpBoolNot: { TypeTableEntry *type_entry = analyze_expression(g, import, context, g->builtin_types.entry_bool, - expr_node); + *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_bool; } - ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val; + ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; if (!target_const_val->ok) { return g->builtin_types.entry_bool; } @@ -4588,7 +4673,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo case PrefixOpBinNot: { TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, - expr_node); + *expr_node); if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if (expr_type->id == TypeTableEntryIdInt || @@ -4596,7 +4681,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo { return expr_type; } else { - add_node_error(g, expr_node, buf_sprintf("invalid binary not type: '%s'", + add_node_error(g, *expr_node, buf_sprintf("invalid binary not type: '%s'", buf_ptr(&expr_type->name))); return g->builtin_types.entry_invalid; } @@ -4604,8 +4689,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } case PrefixOpNegation: { - TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, - expr_node); + TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, *expr_node); if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if ((expr_type->id == TypeTableEntryIdInt && @@ -4614,7 +4698,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo expr_type->id == TypeTableEntryIdNumLitInt || expr_type->id == TypeTableEntryIdNumLitFloat) { - ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val; + ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; if (!target_const_val->ok) { return expr_type; } @@ -4635,12 +4719,13 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo bool is_const = (prefix_op == PrefixOpConstAddressOf); TypeTableEntry *child_type = analyze_lvalue(g, import, context, - expr_node, LValPurposeAddressOf, is_const); + *expr_node, LValPurposeAddressOf, is_const); if (child_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (child_type->id == TypeTableEntryIdMetaType) { - TypeTableEntry *meta_type = analyze_type_expr(g, import, context, expr_node); + TypeTableEntry *meta_type = analyze_type_expr_pointer_only(g, import, context, + *expr_node, true); if (meta_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (meta_type->id == TypeTableEntryIdUnreachable) { @@ -4653,7 +4738,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } else if (child_type->id == TypeTableEntryIdNumLitInt || child_type->id == TypeTableEntryIdNumLitFloat) { - add_node_error(g, expr_node, + add_node_error(g, *expr_node, buf_sprintf("unable to get address of type '%s'", buf_ptr(&child_type->name))); return g->builtin_types.entry_invalid; } else { @@ -4662,13 +4747,13 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } case PrefixOpDereference: { - TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdPointer) { return type_entry->data.pointer.child_type; } else { - add_node_error(g, expr_node, + add_node_error(g, *expr_node, buf_sprintf("indirection requires pointer operand ('%s' invalid)", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; @@ -4676,12 +4761,12 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } case PrefixOpMaybe: { - TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMetaType) { - TypeTableEntry *meta_type = resolve_type(g, expr_node); + TypeTableEntry *meta_type = resolve_type(g, *expr_node); if (meta_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (meta_type->id == TypeTableEntryIdUnreachable) { @@ -4691,10 +4776,10 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type)); } } else if (type_entry->id == TypeTableEntryIdUnreachable) { - add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in maybe type")); + add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in maybe type")); return g->builtin_types.entry_invalid; } else { - ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val; + ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; TypeTableEntry *maybe_type = get_maybe_type(g, type_entry); if (!target_const_val->ok) { return maybe_type; @@ -4704,12 +4789,12 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } case PrefixOpError: { - TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMetaType) { - TypeTableEntry *meta_type = resolve_type(g, expr_node); + TypeTableEntry *meta_type = resolve_type(g, *expr_node); if (meta_type->id == TypeTableEntryIdInvalid) { return meta_type; } else if (meta_type->id == TypeTableEntryIdUnreachable) { @@ -4719,7 +4804,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type)); } } else if (type_entry->id == TypeTableEntryIdUnreachable) { - add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in error type")); + add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in error type")); return g->builtin_types.entry_invalid; } else { // TODO eval const expr @@ -4729,28 +4814,28 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo } case PrefixOpUnwrapError: { - TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdErrorUnion) { return type_entry->data.error.child_type; } else { - add_node_error(g, expr_node, + add_node_error(g, *expr_node, buf_sprintf("expected error type, got '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } } case PrefixOpUnwrapMaybe: { - TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); + TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node); if (type_entry->id == TypeTableEntryIdInvalid) { return type_entry; } else if (type_entry->id == TypeTableEntryIdMaybe) { return type_entry->data.maybe.child_type; } else { - add_node_error(g, expr_node, + add_node_error(g, *expr_node, buf_sprintf("expected maybe type, got '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } @@ -5094,7 +5179,7 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl } } else { Buf *variable_name = &asm_output->variable_name; - VariableTableEntry *var = find_variable(context, variable_name, false); + VariableTableEntry *var = find_variable(g, context, variable_name); if (var) { asm_output->variable = var; return var->type; @@ -5120,10 +5205,8 @@ static TypeTableEntry *analyze_goto(CodeGen *g, ImportTableEntry *import, BlockC return g->builtin_types.entry_unreachable; } -// When you call analyze_expression, the node you pass might no longer be the child node -// you thought it was due to implicit casting rewriting the AST. -static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node) +static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import, + BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only) { assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid); TypeTableEntry *return_type = nullptr; @@ -5197,7 +5280,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, return_type = analyze_undefined_literal_expr(g, import, context, expected_type, node); break; case NodeTypeSymbol: - return_type = analyze_symbol_expr(g, import, context, expected_type, node); + return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only); break; case NodeTypePrefixOpExpr: return_type = analyze_prefix_op_expr(g, import, context, expected_type, node); @@ -5235,10 +5318,8 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeRoot: - case NodeTypeRootExportDecl: case NodeTypeFnDef: - case NodeTypeImport: - case NodeTypeCImport: + case NodeTypeUse: case NodeTypeLabel: case NodeTypeStructDecl: case NodeTypeStructField: @@ -5263,7 +5344,17 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, return resolved_type; } -static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) { +// When you call analyze_expression, the node you pass might no longer be the child node +// you thought it was due to implicit casting rewriting the AST. +static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + return analyze_expression_pointer_only(g, import, context, expected_type, node, false); +} + +static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { + ImportTableEntry *import = fn_table_entry->import_entry; + AstNode *node = fn_table_entry->fn_def_node; assert(node->type == NodeTypeFnDef); AstNode *fn_proto_node = node->data.fn_def.fn_proto; @@ -5277,7 +5368,6 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo BlockContext *context = node->data.fn_def.block_context; - FnTableEntry *fn_table_entry = fn_proto_node->data.fn_proto.fn_table_entry; TypeTableEntry *fn_type = fn_table_entry->type_entry; AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; for (int i = 0; i < fn_proto->params.length; i += 1) { @@ -5302,7 +5392,8 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo add_node_error(g, param_decl_node, buf_sprintf("missing parameter name")); } - VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, ¶m_decl->name, type, true); + VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, ¶m_decl->name, + type, true); var->src_arg_index = i; param_decl_node->data.param_decl.variable = var; @@ -5315,375 +5406,71 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo node->data.fn_def.implicit_return_type = block_return_type; } -static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { - switch (node->type) { - case NodeTypeFnDef: - analyze_top_level_fn_def(g, import, node); - break; - case NodeTypeStructDecl: - { - for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) { - AstNode *fn_def_node = node->data.struct_decl.fns.at(i); - analyze_top_level_fn_def(g, import, fn_def_node); - } - break; - } - case NodeTypeRootExportDecl: - case NodeTypeImport: - case NodeTypeCImport: - case NodeTypeVariableDeclaration: - case NodeTypeErrorValueDecl: - case NodeTypeFnProto: - case NodeTypeTypeDecl: - // already took care of these - break; - case NodeTypeDirective: - case NodeTypeParamDecl: - case NodeTypeFnDecl: - case NodeTypeReturnExpr: - case NodeTypeDefer: - case NodeTypeRoot: - case NodeTypeBlock: - case NodeTypeBinOpExpr: - case NodeTypeUnwrapErrorExpr: - case NodeTypeFnCallExpr: - case NodeTypeArrayAccessExpr: - case NodeTypeSliceExpr: - case NodeTypeNumberLiteral: - case NodeTypeStringLiteral: - case NodeTypeCharLiteral: - case NodeTypeBoolLiteral: - case NodeTypeNullLiteral: - case NodeTypeUndefinedLiteral: - case NodeTypeSymbol: - case NodeTypePrefixOpExpr: - case NodeTypeIfBoolExpr: - case NodeTypeIfVarExpr: - case NodeTypeWhileExpr: - case NodeTypeForExpr: - case NodeTypeSwitchExpr: - case NodeTypeSwitchProng: - case NodeTypeSwitchRange: - case NodeTypeLabel: - case NodeTypeGoto: - case NodeTypeBreak: - case NodeTypeContinue: - case NodeTypeAsmExpr: - case NodeTypeFieldAccessExpr: - case NodeTypeStructField: - case NodeTypeStructValueField: - case NodeTypeContainerInitExpr: - case NodeTypeArrayType: - case NodeTypeErrorType: - case NodeTypeTypeLiteral: - zig_unreachable(); +static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context, + AstNode *node, Buf *name) +{ + assert(import); + + TopLevelDecl *tld = get_as_top_level_decl(node); + tld->import = import; + tld->name = name; + + if (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport) { + g->export_queue.append(node); } -} -static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node, - TopLevelDecl *decl_node) -{ - switch (node->type) { - case NodeTypeNumberLiteral: - case NodeTypeStringLiteral: - case NodeTypeCharLiteral: - case NodeTypeBoolLiteral: - case NodeTypeNullLiteral: - case NodeTypeUndefinedLiteral: - case NodeTypeGoto: - case NodeTypeBreak: - case NodeTypeContinue: - case NodeTypeErrorValueDecl: - case NodeTypeErrorType: - case NodeTypeTypeLiteral: - // no dependencies on other top level declarations - break; - case NodeTypeSymbol: - { - if (node->data.symbol_expr.override_type_entry) { - break; - } - Buf *name = &node->data.symbol_expr.symbol; - auto table_entry = g->primitive_type_table.maybe_get(name); - if (!table_entry) { - table_entry = import->type_table.maybe_get(name); - } - if (!table_entry || !type_is_complete(table_entry->value)) { - decl_node->deps.put(name, node); - } - break; - } - case NodeTypeBinOpExpr: - collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node); - collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node); - break; - case NodeTypeUnwrapErrorExpr: - collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op1, decl_node); - collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op2, decl_node); - break; - case NodeTypeReturnExpr: - collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); - break; - case NodeTypeDefer: - collect_expr_decl_deps(g, import, node->data.defer.expr, decl_node); - break; - case NodeTypePrefixOpExpr: - collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node); - break; - case NodeTypeFnCallExpr: - if (!node->data.fn_call_expr.is_builtin) { - collect_expr_decl_deps(g, import, node->data.fn_call_expr.fn_ref_expr, decl_node); - } - for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i); - collect_expr_decl_deps(g, import, arg_node, decl_node); - } - break; - case NodeTypeArrayAccessExpr: - collect_expr_decl_deps(g, import, node->data.array_access_expr.array_ref_expr, decl_node); - collect_expr_decl_deps(g, import, node->data.array_access_expr.subscript, decl_node); - break; - case NodeTypeSliceExpr: - collect_expr_decl_deps(g, import, node->data.slice_expr.array_ref_expr, decl_node); - collect_expr_decl_deps(g, import, node->data.slice_expr.start, decl_node); - if (node->data.slice_expr.end) { - collect_expr_decl_deps(g, import, node->data.slice_expr.end, decl_node); - } - break; - case NodeTypeFieldAccessExpr: - collect_expr_decl_deps(g, import, node->data.field_access_expr.struct_expr, decl_node); - break; - case NodeTypeIfBoolExpr: - collect_expr_decl_deps(g, import, node->data.if_bool_expr.condition, decl_node); - collect_expr_decl_deps(g, import, node->data.if_bool_expr.then_block, decl_node); - if (node->data.if_bool_expr.else_node) { - collect_expr_decl_deps(g, import, node->data.if_bool_expr.else_node, decl_node); - } - break; - case NodeTypeIfVarExpr: - if (node->data.if_var_expr.var_decl.type) { - collect_expr_decl_deps(g, import, node->data.if_var_expr.var_decl.type, decl_node); - } - if (node->data.if_var_expr.var_decl.expr) { - collect_expr_decl_deps(g, import, node->data.if_var_expr.var_decl.expr, decl_node); - } - collect_expr_decl_deps(g, import, node->data.if_var_expr.then_block, decl_node); - if (node->data.if_bool_expr.else_node) { - collect_expr_decl_deps(g, import, node->data.if_var_expr.else_node, decl_node); - } - break; - case NodeTypeWhileExpr: - collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node); - collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node); - break; - case NodeTypeForExpr: - collect_expr_decl_deps(g, import, node->data.for_expr.array_expr, decl_node); - collect_expr_decl_deps(g, import, node->data.for_expr.body, decl_node); - break; - case NodeTypeBlock: - for (int i = 0; i < node->data.block.statements.length; i += 1) { - AstNode *stmt = node->data.block.statements.at(i); - collect_expr_decl_deps(g, import, stmt, decl_node); - } - break; - case NodeTypeAsmExpr: - for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) { - AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); - if (asm_output->return_type) { - collect_expr_decl_deps(g, import, asm_output->return_type, decl_node); - } else { - decl_node->deps.put(&asm_output->variable_name, node); - } - } - for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) { - AsmInput *asm_input = node->data.asm_expr.input_list.at(i); - collect_expr_decl_deps(g, import, asm_input->expr, decl_node); - } - break; - case NodeTypeContainerInitExpr: - collect_expr_decl_deps(g, import, node->data.container_init_expr.type, decl_node); - for (int i = 0; i < node->data.container_init_expr.entries.length; i += 1) { - AstNode *child_node = node->data.container_init_expr.entries.at(i); - collect_expr_decl_deps(g, import, child_node, decl_node); - } - break; - case NodeTypeStructValueField: - collect_expr_decl_deps(g, import, node->data.struct_val_field.expr, decl_node); - break; - case NodeTypeArrayType: - if (node->data.array_type.size) { - collect_expr_decl_deps(g, import, node->data.array_type.size, decl_node); - } - collect_expr_decl_deps(g, import, node->data.array_type.child_type, decl_node); - break; - case NodeTypeSwitchExpr: - collect_expr_decl_deps(g, import, node->data.switch_expr.expr, decl_node); - for (int i = 0; i < node->data.switch_expr.prongs.length; i += 1) { - AstNode *prong = node->data.switch_expr.prongs.at(i); - collect_expr_decl_deps(g, import, prong, decl_node); - } - break; - case NodeTypeSwitchProng: - for (int i = 0; i < node->data.switch_prong.items.length; i += 1) { - AstNode *child = node->data.switch_prong.items.at(i); - collect_expr_decl_deps(g, import, child, decl_node); - } - collect_expr_decl_deps(g, import, node->data.switch_prong.expr, decl_node); - break; - case NodeTypeSwitchRange: - collect_expr_decl_deps(g, import, node->data.switch_range.start, decl_node); - collect_expr_decl_deps(g, import, node->data.switch_range.end, decl_node); - break; - case NodeTypeFnProto: - // remember that fn proto node is used for function definitions as well - // as types - for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { - AstNode *param = node->data.fn_proto.params.at(i); - collect_expr_decl_deps(g, import, param, decl_node); - } - if (node->data.fn_proto.directives) { - for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) { - AstNode *directive = node->data.fn_proto.directives->at(i); - collect_expr_decl_deps(g, import, directive, decl_node); - } - } - collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); - break; - case NodeTypeParamDecl: - collect_expr_decl_deps(g, import, node->data.param_decl.type, decl_node); - break; - case NodeTypeTypeDecl: - collect_expr_decl_deps(g, import, node->data.type_decl.child_type, decl_node); - break; - case NodeTypeDirective: - collect_expr_decl_deps(g, import, node->data.directive.expr, decl_node); - break; - case NodeTypeVariableDeclaration: - case NodeTypeRootExportDecl: - case NodeTypeFnDef: - case NodeTypeRoot: - case NodeTypeFnDecl: - case NodeTypeImport: - case NodeTypeCImport: - case NodeTypeLabel: - case NodeTypeStructDecl: - case NodeTypeStructField: - zig_unreachable(); + node->block_context = block_context; + + auto entry = block_context->decl_table.maybe_get(name); + if (entry) { + AstNode *other_decl_node = entry->value; + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); + add_error_note(g, msg, other_decl_node, buf_sprintf("previous definition is here")); + } else { + block_context->decl_table.put(name, node); } } -static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node) { +static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { switch (node->type) { case NodeTypeRoot: for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) { AstNode *child = import->root->data.root.top_level_decls.at(i); - detect_top_level_decl_deps(g, import, child); + scan_decls(g, import, context, child); } break; case NodeTypeStructDecl: { Buf *name = &node->data.struct_decl.name; - auto table_entry = g->primitive_type_table.maybe_get(name); - if (!table_entry) { - table_entry = import->type_table.maybe_get(name); - } - if (table_entry) { - node->data.struct_decl.type_entry = table_entry->value; - add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); - } else { - TypeTableEntry *entry; - if (node->data.struct_decl.type_entry) { - entry = node->data.struct_decl.type_entry; - } else { - entry = get_partial_container_type(g, import, - node->data.struct_decl.kind, node, buf_ptr(name)); - } - - import->type_table.put(&entry->name, entry); - node->data.struct_decl.type_entry = entry; - - bool is_pub = (node->data.struct_decl.visib_mod != VisibModPrivate); - if (is_pub) { - for (int i = 0; i < import->importers.length; i += 1) { - ImporterInfo importer = import->importers.at(i); - auto table_entry = importer.import->type_table.maybe_get(&entry->name); - if (table_entry) { - add_node_error(g, importer.source_node, - buf_sprintf("import of type '%s' overrides existing definition", - buf_ptr(&entry->name))); - } else { - importer.import->type_table.put(&entry->name, entry); - } - } - } - } - - // determine which other top level declarations this struct depends on. - TopLevelDecl *decl_node = &node->data.struct_decl.top_level_decl; - decl_node->deps.init(1); - for (int i = 0; i < node->data.struct_decl.fields.length; i += 1) { - AstNode *field_node = node->data.struct_decl.fields.at(i); - AstNode *type_node = field_node->data.struct_field.type; - collect_expr_decl_deps(g, import, type_node, decl_node); - } - decl_node->name = name; - decl_node->import = import; - if (decl_node->deps.size() > 0) { - g->unresolved_top_level_decls.put(name, node); - } else { - resolve_top_level_decl(g, import, node); - } + TypeTableEntry *container_type = get_partial_container_type(g, import, + node->data.struct_decl.kind, node, buf_ptr(name)); + node->data.struct_decl.type_entry = container_type; + add_top_level_decl(g, import, context, node, name); // handle the member function definitions independently for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) { - AstNode *fn_def_node = node->data.struct_decl.fns.at(i); - AstNode *fn_proto_node = fn_def_node->data.fn_def.fn_proto; - fn_proto_node->data.fn_proto.struct_node = node; - detect_top_level_decl_deps(g, import, fn_def_node); + AstNode *child_node = node->data.struct_decl.fns.at(i); + get_as_top_level_decl(child_node)->parent_decl = node; + BlockContext *child_context = get_container_block_context(container_type); + scan_decls(g, import, child_context, child_node); } break; } case NodeTypeFnDef: node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node; - detect_top_level_decl_deps(g, import, node->data.fn_def.fn_proto); + scan_decls(g, import, context, node->data.fn_def.fn_proto); break; case NodeTypeVariableDeclaration: { - // determine which other top level declarations this variable declaration depends on. - TopLevelDecl *decl_node = &node->data.variable_declaration.top_level_decl; - decl_node->deps.init(1); - if (node->data.variable_declaration.type) { - collect_expr_decl_deps(g, import, node->data.variable_declaration.type, decl_node); - } - if (node->data.variable_declaration.expr) { - collect_expr_decl_deps(g, import, node->data.variable_declaration.expr, decl_node); - } Buf *name = &node->data.variable_declaration.symbol; - decl_node->name = name; - decl_node->import = import; - if (decl_node->deps.size() > 0) { - g->unresolved_top_level_decls.put(name, node); - } else { - resolve_top_level_decl(g, import, node); - } + add_top_level_decl(g, import, context, node, name); break; } case NodeTypeTypeDecl: { - // determine which other top level declarations this variable declaration depends on. - TopLevelDecl *decl_node = &node->data.type_decl.top_level_decl; - decl_node->deps.init(1); - collect_expr_decl_deps(g, import, node, decl_node); - Buf *name = &node->data.type_decl.symbol; - decl_node->name = name; - decl_node->import = import; - if (decl_node->deps.size() > 0) { - g->unresolved_top_level_decls.put(name, node); - } else { - resolve_top_level_decl(g, import, node); - } + add_top_level_decl(g, import, context, node, name); break; } case NodeTypeFnProto: @@ -5695,60 +5482,22 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast add_node_error(g, node, buf_sprintf("missing function name")); break; } - Buf *qualified_name; - AstNode *struct_node = node->data.fn_proto.struct_node; - if (struct_node) { - Buf *struct_name = &struct_node->data.struct_decl.name; - qualified_name = buf_sprintf("%s.%s", buf_ptr(struct_name), buf_ptr(fn_name)); - } else { - qualified_name = fn_name; - } - - // determine which other top level declarations this function prototype depends on. - TopLevelDecl *decl_node = &node->data.fn_proto.top_level_decl; - decl_node->deps.init(1); - - collect_expr_decl_deps(g, import, node, decl_node); - - decl_node->name = qualified_name; - decl_node->import = import; - if (decl_node->deps.size() > 0) { - if (g->unresolved_top_level_decls.maybe_get(qualified_name)) { - node->data.fn_proto.skip = true; - add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(fn_name))); - } else { - g->unresolved_top_level_decls.put(qualified_name, node); - } - } else { - resolve_top_level_decl(g, import, node); - } + add_top_level_decl(g, import, context, node, fn_name); break; } - case NodeTypeRootExportDecl: - resolve_top_level_decl(g, import, node); - break; - case NodeTypeImport: - // already taken care of - break; - case NodeTypeCImport: + case NodeTypeUse: { - TopLevelDecl *decl_node = &node->data.c_import.top_level_decl; - decl_node->deps.init(1); - collect_expr_decl_deps(g, import, node->data.c_import.block, decl_node); - - decl_node->name = buf_sprintf("c_import_%" PRIu32, node->create_index); - decl_node->import = import; - if (decl_node->deps.size() > 0) { - g->unresolved_top_level_decls.put(decl_node->name, node); - } else { - resolve_top_level_decl(g, import, node); - } + TopLevelDecl *tld = get_as_top_level_decl(node); + tld->import = import; + node->block_context = context; + g->use_queue.append(node); + tld->import->use_decls.append(node); break; } case NodeTypeErrorValueDecl: // error value declarations do not depend on other top level decls - resolve_top_level_decl(g, import, node); + preview_error_value_decl(g, node); break; case NodeTypeDirective: case NodeTypeParamDecl: @@ -5792,152 +5541,176 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast } } -static void recursive_resolve_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { - auto it = get_resolved_top_level_decl(node)->deps.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; +static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) { + TopLevelDecl *tld = get_as_top_level_decl(dst_use_node); + AstNode *use_target_node = src_use_node->data.use.expr; + Expr *expr = get_resolved_expr(use_target_node); - auto unresolved_entry = g->unresolved_top_level_decls.maybe_get(entry->key); - if (!unresolved_entry) { - continue; - } + if (expr->type_entry->id == TypeTableEntryIdInvalid) { + return; + } - AstNode *child_node = unresolved_entry->value; + ConstExprValue *const_val = &expr->const_val; + assert(const_val->ok); - if (get_resolved_top_level_decl(child_node)->in_current_deps) { - // dependency loop. we'll let the fact that it's not in the respective - // table cause an error in resolve_top_level_decl. + ImportTableEntry *target_import = const_val->data.x_import; + assert(target_import); + + if (target_import->any_imports_failed) { + tld->import->any_imports_failed = true; + } + + for (int i = 0; i < target_import->root->data.root.top_level_decls.length; i += 1) { + AstNode *decl_node = target_import->root->data.root.top_level_decls.at(i); + if (decl_node->type == NodeTypeFnDef) { + decl_node = decl_node->data.fn_def.fn_proto; + } + TopLevelDecl *target_tld = get_as_top_level_decl(decl_node); + if (!target_tld->name) { continue; } + if (target_tld->visib_mod != VisibModPrivate) { + auto existing_entry = tld->import->block_context->decl_table.maybe_get(target_tld->name); + if (existing_entry) { + AstNode *existing_decl = existing_entry->value; + if (existing_decl != decl_node) { + ErrorMsg *msg = add_node_error(g, dst_use_node, + buf_sprintf("import of '%s' overrides existing definition", + buf_ptr(target_tld->name))); + add_error_note(g, msg, existing_decl, buf_sprintf("previous definition here")); + add_error_note(g, msg, decl_node, buf_sprintf("imported definition here")); + } + } else { + tld->import->block_context->decl_table.put(target_tld->name, decl_node); + } + } + } - // set temporary flag - TopLevelDecl *top_level_decl = get_resolved_top_level_decl(child_node); - top_level_decl->in_current_deps = true; + for (int i = 0; i < target_import->use_decls.length; i += 1) { + AstNode *use_decl_node = target_import->use_decls.at(i); + TopLevelDecl *target_tld = get_as_top_level_decl(use_decl_node); + if (target_tld->visib_mod != VisibModPrivate) { + add_symbols_from_import(g, use_decl_node, dst_use_node); + } + } - recursive_resolve_decl(g, top_level_decl->import, child_node); +} - // unset temporary flag - top_level_decl->in_current_deps = false; - } +static void resolve_use_decl(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeUse); + add_symbols_from_import(g, node, node); +} - resolve_top_level_decl(g, import, node); +static void preview_use_decl(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeUse); + TopLevelDecl *tld = get_as_top_level_decl(node); + TypeTableEntry *use_expr_type = analyze_expression(g, tld->import, tld->import->block_context, + g->builtin_types.entry_namespace, node->data.use.expr); + if (use_expr_type->id == TypeTableEntryIdInvalid) { + tld->import->any_imports_failed = true; + } } -static void resolve_top_level_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { - assert(node->type == NodeTypeRoot); +ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, + Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code) +{ + Buf *full_path = buf_alloc(); + os_path_join(src_dirname, src_basename, full_path); - while (g->unresolved_top_level_decls.size() > 0) { - // for the sake of determinism, find the element with the lowest - // insert index and resolve that one. - AstNode *decl_node = nullptr; - auto it = g->unresolved_top_level_decls.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; + if (g->verbose) { + fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path)); + fprintf(stderr, "----------------\n"); + fprintf(stderr, "%s\n", buf_ptr(source_code)); - AstNode *this_node = entry->value; - if (!decl_node || this_node->create_index < decl_node->create_index) { - decl_node = this_node; - } + fprintf(stderr, "\nTokens:\n"); + fprintf(stderr, "---------\n"); + } - } - // set temporary flag - TopLevelDecl *top_level_decl = get_resolved_top_level_decl(decl_node); - top_level_decl->in_current_deps = true; + Tokenization tokenization = {0}; + tokenize(source_code, &tokenization); - recursive_resolve_decl(g, top_level_decl->import, decl_node); + if (tokenization.err) { + ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column, + source_code, tokenization.line_offsets, tokenization.err); - // unset temporary flag - top_level_decl->in_current_deps = false; + print_err_msg(err, g->err_color); + exit(1); } -} -static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { - assert(node->type == NodeTypeRoot); + if (g->verbose) { + print_tokens(source_code, tokenization.tokens); - for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { - AstNode *child = node->data.root.top_level_decls.at(i); - analyze_top_level_decl(g, import, child); + fprintf(stderr, "\nAST:\n"); + fprintf(stderr, "------\n"); } -} -void semantic_analyze(CodeGen *g) { - { - auto it = g->import_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; + ImportTableEntry *import_entry = allocate<ImportTableEntry>(1); + import_entry->package = package; + import_entry->source_code = source_code; + import_entry->line_offsets = tokenization.line_offsets; + import_entry->path = full_path; - ImportTableEntry *import = entry->value; + import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color, + &g->next_node_index); + assert(import_entry->root); + if (g->verbose) { + ast_print(stderr, import_entry->root, 0); + } - for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) { - AstNode *child = import->root->data.root.top_level_decls.at(i); - if (child->type == NodeTypeImport) { - if (child->data.import.directives) { - for (int i = 0; i < child->data.import.directives->length; i += 1) { - AstNode *directive_node = child->data.import.directives->at(i); - Buf *name = &directive_node->data.directive.name; - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); - } - } + import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); + g->import_table.put(abs_full_path, import_entry); + g->import_queue.append(import_entry); - ImportTableEntry *target_import = child->data.import.import; - assert(target_import); + import_entry->block_context = new_block_context(import_entry->root, nullptr); + import_entry->block_context->di_scope = LLVMZigFileToScope(import_entry->di_file); - target_import->importers.append({import, child}); - } else if (child->type == NodeTypeErrorValueDecl) { - preview_error_value_decl(g, child); - } + + assert(import_entry->root->type == NodeTypeRoot); + for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) { + AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i); + + if (top_level_decl->type == NodeTypeFnDef) { + AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; + assert(proto_node->type == NodeTypeFnProto); + Buf *proto_name = &proto_node->data.fn_proto.name; + + bool is_private = (proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModPrivate); + + if (buf_eql_str(proto_name, "main") && !is_private) { + g->have_exported_main = true; } } } - { - g->err_tag_type = get_smallest_unsigned_int_type(g, g->error_value_count); - - g->builtin_types.entry_pure_error->type_ref = g->err_tag_type->type_ref; - g->builtin_types.entry_pure_error->di_type = g->err_tag_type->di_type; - } + return import_entry; +} - { - auto it = g->import_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - ImportTableEntry *import = entry->value; +void semantic_analyze(CodeGen *g) { + for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) { + ImportTableEntry *import = g->import_queue.at(g->import_queue_index); + scan_decls(g, import, import->block_context, import->root); + } - detect_top_level_decl_deps(g, import, import->root); - } + for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) { + AstNode *use_decl_node = g->use_queue.at(g->use_queue_index); + preview_use_decl(g, use_decl_node); } - { - auto it = g->import_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; + for (int i = 0; i < g->use_queue.length; i += 1) { + AstNode *use_decl_node = g->use_queue.at(i); + resolve_use_decl(g, use_decl_node); + } - ImportTableEntry *import = entry->value; - resolve_top_level_declarations_root(g, import, import->root); - } + for (; g->export_queue_index < g->export_queue.length; g->export_queue_index += 1) { + AstNode *decl_node = g->export_queue.at(g->export_queue_index); + bool pointer_only = false; + resolve_top_level_decl(g, decl_node, pointer_only); } - { - auto it = g->import_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - ImportTableEntry *import = entry->value; - analyze_top_level_decls_root(g, import, import->root); - } + for (int i = 0; i < g->fn_defs.length; i += 1) { + FnTableEntry *fn_entry = g->fn_defs.at(i); + analyze_fn_body(g, fn_entry); } } @@ -6012,13 +5785,11 @@ Expr *get_resolved_expr(AstNode *node) { case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeRoot: - case NodeTypeRootExportDecl: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeDirective: - case NodeTypeImport: - case NodeTypeCImport: + case NodeTypeUse: case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: @@ -6029,18 +5800,20 @@ Expr *get_resolved_expr(AstNode *node) { zig_unreachable(); } -TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { +static TopLevelDecl *get_as_top_level_decl(AstNode *node) { switch (node->type) { case NodeTypeVariableDeclaration: return &node->data.variable_declaration.top_level_decl; case NodeTypeFnProto: return &node->data.fn_proto.top_level_decl; + case NodeTypeFnDef: + return &node->data.fn_def.fn_proto->data.fn_proto.top_level_decl; case NodeTypeStructDecl: return &node->data.struct_decl.top_level_decl; case NodeTypeErrorValueDecl: return &node->data.error_value_decl.top_level_decl; - case NodeTypeCImport: - return &node->data.c_import.top_level_decl; + case NodeTypeUse: + return &node->data.use.top_level_decl; case NodeTypeTypeDecl: return &node->data.type_decl.top_level_decl; case NodeTypeNumberLiteral: @@ -6063,8 +5836,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeAsmExpr: case NodeTypeContainerInitExpr: case NodeTypeRoot: - case NodeTypeRootExportDecl: - case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: @@ -6072,7 +5843,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeSymbol: - case NodeTypeImport: case NodeTypeBoolLiteral: case NodeTypeNullLiteral: case NodeTypeUndefinedLiteral: @@ -6140,6 +5910,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNamespace: zig_unreachable(); case TypeTableEntryIdUnreachable: case TypeTableEntryIdVoid: @@ -6257,6 +6028,7 @@ static TypeTableEntry *type_of_first_thing_in_memory(TypeTableEntry *type_entry) case TypeTableEntryIdUnreachable: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: + case TypeTableEntryIdNamespace: zig_unreachable(); case TypeTableEntryIdArray: return type_of_first_thing_in_memory(type_entry->data.array.child_type); diff --git a/src/analyze.hpp b/src/analyze.hpp index 441211d2fa..48a2f62ded 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -12,11 +12,11 @@ void semantic_analyze(CodeGen *g); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); +ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); BlockContext *new_block_context(AstNode *node, BlockContext *parent); Expr *get_resolved_expr(AstNode *node); -TopLevelDecl *get_resolved_top_level_decl(AstNode *node); bool is_node_void_expr(AstNode *node); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits); TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits); @@ -37,4 +37,8 @@ TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry); bool type_has_bits(TypeTableEntry *type_entry); uint64_t get_memcpy_align(CodeGen *g, TypeTableEntry *type_entry); + +ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, + Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code); + #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 118eee8f2c..4a1d829546 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -101,8 +101,6 @@ static const char *node_type_str(NodeType node_type) { switch (node_type) { case NodeTypeRoot: return "Root"; - case NodeTypeRootExportDecl: - return "RootExportDecl"; case NodeTypeFnDef: return "FnDef"; case NodeTypeFnDecl: @@ -145,10 +143,8 @@ static const char *node_type_str(NodeType node_type) { return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; - case NodeTypeImport: - return "Import"; - case NodeTypeCImport: - return "CImport"; + case NodeTypeUse: + return "Use"; case NodeTypeBoolLiteral: return "BoolLiteral"; case NodeTypeNullLiteral: @@ -214,11 +210,6 @@ void ast_print(FILE *f, AstNode *node, int indent) { ast_print(f, child, indent + 2); } break; - case NodeTypeRootExportDecl: - fprintf(f, "%s %s '%s'\n", node_type_str(node->type), - buf_ptr(&node->data.root_export_decl.type), - buf_ptr(&node->data.root_export_decl.name)); - break; case NodeTypeFnDef: { fprintf(f, "%s\n", node_type_str(node->type)); @@ -372,12 +363,9 @@ void ast_print(FILE *f, AstNode *node, int indent) { case NodeTypeSymbol: fprintf(f, "Symbol %s\n", buf_ptr(&node->data.symbol_expr.symbol)); break; - case NodeTypeImport: - fprintf(f, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.import.path)); - break; - case NodeTypeCImport: + case NodeTypeUse: fprintf(f, "%s\n", node_type_str(node->type)); - ast_print(f, node->data.c_import.block, indent + 2); + ast_print(f, node->data.use.expr, indent + 2); break; case NodeTypeBoolLiteral: fprintf(f, "%s '%s'\n", node_type_str(node->type), @@ -556,7 +544,7 @@ static void render_node(AstRender *ar, AstNode *node) { print_indent(ar); render_node(ar, child); - if (child->type == NodeTypeImport || + if (child->type == NodeTypeUse || child->type == NodeTypeVariableDeclaration || child->type == NodeTypeTypeDecl || child->type == NodeTypeErrorValueDecl || @@ -567,12 +555,10 @@ static void render_node(AstRender *ar, AstNode *node) { fprintf(ar->f, "\n"); } break; - case NodeTypeRootExportDecl: - zig_panic("TODO"); case NodeTypeFnProto: { const char *fn_name = buf_ptr(&node->data.fn_proto.name); - const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod); + const char *pub_str = visib_mod_string(node->data.fn_proto.top_level_decl.visib_mod); const char *extern_str = extern_string(node->data.fn_proto.is_extern); const char *inline_str = inline_string(node->data.fn_proto.is_inline); fprintf(ar->f, "%s%s%sfn %s(", pub_str, inline_str, extern_str, fn_name); @@ -605,15 +591,19 @@ static void render_node(AstRender *ar, AstNode *node) { break; } case NodeTypeFnDef: - if (node->data.fn_def.fn_proto->data.fn_proto.directives) { - for (int i = 0; i < node->data.fn_def.fn_proto->data.fn_proto.directives->length; i += 1) { - render_node(ar, node->data.fn_def.fn_proto->data.fn_proto.directives->at(i)); + { + ZigList<AstNode *> *directives = + node->data.fn_def.fn_proto->data.fn_proto.top_level_decl.directives; + if (directives) { + for (int 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); + break; } - render_node(ar, node->data.fn_def.fn_proto); - fprintf(ar->f, " "); - render_node(ar, node->data.fn_def.body); - break; case NodeTypeFnDecl: zig_panic("TODO"); case NodeTypeParamDecl: @@ -642,7 +632,7 @@ static void render_node(AstRender *ar, AstNode *node) { zig_panic("TODO"); case NodeTypeVariableDeclaration: { - const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); + const char *pub_str = visib_mod_string(node->data.variable_declaration.top_level_decl.visib_mod); const char *extern_str = extern_string(node->data.variable_declaration.is_extern); const char *var_name = buf_ptr(&node->data.variable_declaration.symbol); const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const); @@ -659,7 +649,7 @@ static void render_node(AstRender *ar, AstNode *node) { } case NodeTypeTypeDecl: { - const char *pub_str = visib_mod_string(node->data.type_decl.visib_mod); + const char *pub_str = visib_mod_string(node->data.type_decl.top_level_decl.visib_mod); const char *var_name = buf_ptr(&node->data.type_decl.symbol); fprintf(ar->f, "%stype %s = ", pub_str, var_name); render_node(ar, node->data.type_decl.child_type); @@ -748,9 +738,7 @@ static void render_node(AstRender *ar, AstNode *node) { fprintf(ar->f, ".%s", buf_ptr(rhs)); break; } - case NodeTypeImport: - zig_panic("TODO"); - case NodeTypeCImport: + case NodeTypeUse: zig_panic("TODO"); case NodeTypeBoolLiteral: zig_panic("TODO"); @@ -785,7 +773,7 @@ static void render_node(AstRender *ar, AstNode *node) { case NodeTypeStructDecl: { const char *struct_name = buf_ptr(&node->data.struct_decl.name); - const char *pub_str = visib_mod_string(node->data.struct_decl.visib_mod); + const char *pub_str = visib_mod_string(node->data.struct_decl.top_level_decl.visib_mod); const char *container_str = container_string(node->data.struct_decl.kind); fprintf(ar->f, "%s%s %s {\n", pub_str, container_str, struct_name); ar->indent += ar->indent_size; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8b8023a31f..4ee6074e21 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -47,19 +47,30 @@ static void init_darwin_native(CodeGen *g) { } } +static PackageTableEntry *new_package(const char *root_src_dir, const char *root_src_path) { + PackageTableEntry *entry = allocate<PackageTableEntry>(1); + entry->package_table.init(4); + buf_init_from_str(&entry->root_src_dir, root_src_dir); + buf_init_from_str(&entry->root_src_path, root_src_path); + return entry; +} + CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { CodeGen *g = allocate<CodeGen>(1); g->import_table.init(32); g->builtin_fn_table.init(32); g->primitive_type_table.init(32); - g->unresolved_top_level_decls.init(32); g->fn_type_table.init(32); g->error_table.init(16); g->is_release_build = false; g->is_test_build = false; - g->root_source_dir = root_source_dir; g->error_value_count = 1; + g->root_package = new_package(buf_ptr(root_source_dir), ""); + g->std_package = new_package(ZIG_STD_DIR, "index.zig"); + g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); + + if (target) { // cross compiling, so we can't rely on all the configured stuff since // that's for native compilation @@ -117,6 +128,10 @@ void codegen_set_verbose(CodeGen *g, bool verbose) { g->verbose = verbose; } +void codegen_set_check_unused(CodeGen *g, bool check_unused) { + g->check_unused = check_unused; +} + void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) { g->err_color = err_color; } @@ -157,6 +172,14 @@ void codegen_add_lib_dir(CodeGen *g, const char *dir) { g->lib_dirs.append(dir); } +void codegen_add_link_lib(CodeGen *g, const char *lib) { + if (strcmp(lib, "c") == 0) { + g->link_libc = true; + } else { + g->link_libs.append(buf_create_from_str(lib)); + } +} + void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole) { g->windows_subsystem_windows = mwindows; g->windows_subsystem_console = mconsole; @@ -316,6 +339,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { case BuiltinFnIdCInclude: case BuiltinFnIdCDefine: case BuiltinFnIdCUndef: + case BuiltinFnIdImport: + case BuiltinFnIdCImport: zig_unreachable(); case BuiltinFnIdCtz: case BuiltinFnIdClz: @@ -844,7 +869,7 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou LLVMValueRef struct_ptr; if (struct_expr_node->type == NodeTypeSymbol) { - VariableTableEntry *var = struct_expr_node->data.symbol_expr.variable; + VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable; assert(var); if (var->is_ptr && var->type->id == TypeTableEntryIdPointer) { @@ -983,6 +1008,17 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lva } } +static LLVMValueRef gen_variable(CodeGen *g, AstNode *source_node, VariableTableEntry *variable) { + if (!type_has_bits(variable->type)) { + return nullptr; + } else if (variable->is_ptr) { + assert(variable->value_ref); + return get_handle_value(g, source_node, variable->value_ref, variable->type); + } else { + return variable->value_ref; + } +} + static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) { assert(node->type == NodeTypeFieldAccessExpr); @@ -1016,6 +1052,10 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva } else { zig_unreachable(); } + } else if (struct_type->id == TypeTableEntryIdNamespace) { + VariableTableEntry *variable = get_resolved_expr(node)->variable; + assert(variable); + return gen_variable(g, node, variable); } else { zig_unreachable(); } @@ -1027,7 +1067,7 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, LLVMValueRef target_ref; if (node->type == NodeTypeSymbol) { - VariableTableEntry *var = node->data.symbol_expr.variable; + VariableTableEntry *var = get_resolved_expr(node)->variable; assert(var); *out_type_entry = var->type; @@ -2468,21 +2508,18 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeSymbol); - VariableTableEntry *variable = node->data.symbol_expr.variable; + VariableTableEntry *variable = get_resolved_expr(node)->variable; if (variable) { - if (!type_has_bits(variable->type)) { - return nullptr; - } else if (variable->is_ptr) { - assert(variable->value_ref); - return get_handle_value(g, node, variable->value_ref, variable->type); - } else { - return variable->value_ref; - } + return gen_variable(g, node, variable); } + zig_unreachable(); + + /* TODO delete FnTableEntry *fn_entry = node->data.symbol_expr.fn_entry; assert(fn_entry); return fn_entry->fn_value; + */ } static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { @@ -2703,14 +2740,12 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { // caught by constant expression eval codegen zig_unreachable(); case NodeTypeRoot: - case NodeTypeRootExportDecl: case NodeTypeFnProto: case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeDirective: - case NodeTypeImport: - case NodeTypeCImport: + case NodeTypeUse: case NodeTypeStructDecl: case NodeTypeStructField: case NodeTypeStructValueField: @@ -2891,6 +2926,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdVoid: + case TypeTableEntryIdNamespace: zig_unreachable(); } @@ -2943,14 +2979,14 @@ static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) { if (fn_entry == g->main_fn) { return true; } - return fn_entry->ref_count == 0; + return false; } if (fn_entry->is_test) { return true; } - return fn_entry->ref_count == 0; + return false; } static LLVMValueRef gen_test_fn_val(CodeGen *g, FnTableEntry *fn_entry) { @@ -3297,6 +3333,12 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_invalid = entry; } { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNamespace); + buf_init_from_str(&entry->name, "(namespace)"); + entry->zero_bits = true; + g->builtin_types.entry_namespace = entry; + } + { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat); buf_init_from_str(&entry->name, "(float literal)"); entry->zero_bits = true; @@ -3499,14 +3541,6 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_type = entry; g->primitive_type_table.put(&entry->name, entry); } - { - // partially complete the error type. we complete it later after we know - // error_value_count. - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError); - buf_init_from_str(&entry->name, "error"); - g->builtin_types.entry_pure_error = entry; - g->primitive_type_table.put(&entry->name, entry); - } g->builtin_types.entry_u8 = get_int_type(g, false, 8); g->builtin_types.entry_u16 = get_int_type(g, false, 16); @@ -3518,6 +3552,21 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_i64 = get_int_type(g, true, 64); { + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError); + buf_init_from_str(&entry->name, "error"); + + // TODO allow overriding this type and keep track of max value and emit an + // error if there are too many errors declared + g->err_tag_type = g->builtin_types.entry_u16; + + g->builtin_types.entry_pure_error = entry; + entry->type_ref = g->err_tag_type->type_ref; + entry->di_type = g->err_tag_type->di_type; + + g->primitive_type_table.put(&entry->name, entry); + } + + { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum); entry->zero_bits = true; // only allowed at compile time buf_init_from_str(&entry->name, "@OS"); @@ -3685,12 +3734,11 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn_with_arg_count(g, BuiltinFnIdConstEval, "const_eval", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdCtz, "ctz", 2); create_builtin_fn_with_arg_count(g, BuiltinFnIdClz, "clz", 2); + create_builtin_fn_with_arg_count(g, BuiltinFnIdImport, "import", 1); + create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1); } static void init(CodeGen *g, Buf *source_path) { - g->lib_search_paths.append(g->root_source_dir); - g->lib_search_paths.append(buf_create_from_str(ZIG_STD_DIR)); - g->module = LLVMModuleCreateWithName(buf_ptr(source_path)); get_target_triple(&g->triple_str, &g->zig_target); @@ -3741,7 +3789,7 @@ static void init(CodeGen *g, Buf *source_path) { const char *flags = ""; unsigned runtime_version = 0; g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(), - buf_ptr(source_path), buf_ptr(g->root_source_dir), + buf_ptr(source_path), buf_ptr(&g->root_package->root_src_dir), buf_ptr(producer), is_optimized, flags, runtime_version, "", 0, !g->strip_debug_symbols); @@ -3761,9 +3809,6 @@ void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source ImportTableEntry *import = allocate<ImportTableEntry>(1); import->source_code = source_code; import->path = full_path; - import->fn_table.init(32); - import->type_table.init(8); - import->error_table.init(8); g->root_import = import; init(g, full_path); @@ -3791,214 +3836,7 @@ void codegen_render_ast(CodeGen *g, FILE *f, int indent_size) { } -static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) { - char *dot1 = strstr(buf_ptr(buf), "."); - if (!dot1) - return ErrorInvalidFormat; - char *dot2 = strstr(dot1 + 1, "."); - if (!dot2) - return ErrorInvalidFormat; - - *major = (int)strtol(buf_ptr(buf), nullptr, 10); - *minor = (int)strtol(dot1 + 1, nullptr, 10); - *patch = (int)strtol(dot2 + 1, nullptr, 10); - - return ErrorNone; -} - -static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node) { - int err; - if ((err = parse_version_string(version_buf, &g->version_major, &g->version_minor, &g->version_patch))) { - add_node_error(g, node, - buf_sprintf("invalid version string")); - } -} - - -static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, - Buf *src_dirname, Buf *src_basename, Buf *source_code) -{ - int err; - Buf *full_path = buf_alloc(); - os_path_join(src_dirname, src_basename, full_path); - - if (g->verbose) { - fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path)); - fprintf(stderr, "----------------\n"); - fprintf(stderr, "%s\n", buf_ptr(source_code)); - - fprintf(stderr, "\nTokens:\n"); - fprintf(stderr, "---------\n"); - } - - Tokenization tokenization = {0}; - tokenize(source_code, &tokenization); - - if (tokenization.err) { - ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column, - source_code, tokenization.line_offsets, tokenization.err); - - print_err_msg(err, g->err_color); - exit(1); - } - - if (g->verbose) { - print_tokens(source_code, tokenization.tokens); - - fprintf(stderr, "\nAST:\n"); - fprintf(stderr, "------\n"); - } - - ImportTableEntry *import_entry = allocate<ImportTableEntry>(1); - import_entry->source_code = source_code; - import_entry->line_offsets = tokenization.line_offsets; - import_entry->path = full_path; - import_entry->fn_table.init(32); - import_entry->type_table.init(8); - import_entry->error_table.init(8); - - import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color, - &g->next_node_index); - assert(import_entry->root); - if (g->verbose) { - ast_print(stderr, import_entry->root, 0); - } - - import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); - g->import_table.put(abs_full_path, import_entry); - - import_entry->block_context = new_block_context(import_entry->root, nullptr); - import_entry->block_context->di_scope = LLVMZigFileToScope(import_entry->di_file); - - - assert(import_entry->root->type == NodeTypeRoot); - for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) { - AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i); - - if (top_level_decl->type == NodeTypeRootExportDecl) { - if (g->root_import) { - add_node_error(g, top_level_decl, - buf_sprintf("root export declaration only valid in root source file")); - } else { - ZigList<AstNode *> *directives = top_level_decl->data.root_export_decl.directives; - if (directives) { - for (int i = 0; i < directives->length; i += 1) { - AstNode *directive_node = directives->at(i); - Buf *name = &directive_node->data.directive.name; - AstNode *param_node = directive_node->data.directive.expr; - assert(param_node->type == NodeTypeStringLiteral); - Buf *param = ¶m_node->data.string_literal.buf; - - if (param) { - if (buf_eql_str(name, "version")) { - set_root_export_version(g, param, directive_node); - } else if (buf_eql_str(name, "link")) { - if (buf_eql_str(param, "c")) { - g->link_libc = true; - } else { - g->link_libs.append(param); - } - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); - } - } - } - } - - if (g->root_export_decl) { - add_node_error(g, top_level_decl, - buf_sprintf("only one root export declaration allowed")); - } else { - g->root_export_decl = top_level_decl; - - if (!g->root_out_name) - g->root_out_name = &top_level_decl->data.root_export_decl.name; - - Buf *out_type = &top_level_decl->data.root_export_decl.type; - OutType export_out_type; - if (buf_eql_str(out_type, "executable")) { - export_out_type = OutTypeExe; - } else if (buf_eql_str(out_type, "library")) { - export_out_type = OutTypeLib; - } else if (buf_eql_str(out_type, "object")) { - export_out_type = OutTypeObj; - } else { - add_node_error(g, top_level_decl, - buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); - } - if (g->out_type == OutTypeUnknown) { - g->out_type = export_out_type; - } - } - } - } else if (top_level_decl->type == NodeTypeImport) { - Buf *import_target_path = &top_level_decl->data.import.path; - Buf full_path = BUF_INIT; - Buf *import_code = buf_alloc(); - bool found_it = false; - - for (int path_i = 0; path_i < g->lib_search_paths.length; path_i += 1) { - Buf *search_path = g->lib_search_paths.at(path_i); - os_path_join(search_path, import_target_path, &full_path); - - Buf *abs_full_path = buf_alloc(); - if ((err = os_path_real(&full_path, abs_full_path))) { - if (err == ErrorFileNotFound) { - continue; - } else { - g->error_during_imports = true; - add_node_error(g, top_level_decl, - buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); - goto done_looking_at_imports; - } - } - - auto entry = g->import_table.maybe_get(abs_full_path); - if (entry) { - found_it = true; - top_level_decl->data.import.import = entry->value; - } else { - if ((err = os_fetch_file_path(abs_full_path, import_code))) { - if (err == ErrorFileNotFound) { - continue; - } else { - g->error_during_imports = true; - add_node_error(g, top_level_decl, - buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); - goto done_looking_at_imports; - } - } - top_level_decl->data.import.import = codegen_add_code(g, - abs_full_path, search_path, &top_level_decl->data.import.path, import_code); - found_it = true; - } - break; - } - if (!found_it) { - g->error_during_imports = true; - add_node_error(g, top_level_decl, - buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); - } - } else if (top_level_decl->type == NodeTypeFnDef) { - AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; - assert(proto_node->type == NodeTypeFnProto); - Buf *proto_name = &proto_node->data.fn_proto.name; - - bool is_private = (proto_node->data.fn_proto.visib_mod == VisibModPrivate); - - if (buf_eql_str(proto_name, "main") && !is_private) { - g->have_exported_main = true; - } - } - } - -done_looking_at_imports: - - return import_entry; -} - -static ImportTableEntry *add_special_code(CodeGen *g, const char *basename) { +static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) { Buf *std_dir = buf_create_from_str(ZIG_STD_DIR); Buf *code_basename = buf_create_from_str(basename); Buf path_to_code_src = BUF_INIT; @@ -4013,12 +3851,22 @@ static ImportTableEntry *add_special_code(CodeGen *g, const char *basename) { zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err)); } - return codegen_add_code(g, abs_full_path, std_dir, code_basename, import_code); + return add_source_file(g, package, abs_full_path, std_dir, code_basename, import_code); +} + +static PackageTableEntry *create_bootstrap_pkg(CodeGen *g) { + PackageTableEntry *package = new_package(ZIG_STD_DIR, ""); + package->package_table.put(buf_create_from_str("std"), g->std_package); + package->package_table.put(buf_create_from_str("@root"), g->root_package); + return package; } void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *source_code) { Buf source_path = BUF_INIT; os_path_join(src_dir, src_basename, &source_path); + + buf_init_from_buf(&g->root_package->root_src_path, src_basename); + init(g, &source_path); Buf *abs_full_path = buf_alloc(); @@ -4027,19 +3875,14 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou zig_panic("unable to open '%s': %s", buf_ptr(&source_path), err_str(err)); } - g->root_import = codegen_add_code(g, abs_full_path, src_dir, src_basename, source_code); + g->root_import = add_source_file(g, g->root_package, abs_full_path, src_dir, src_basename, source_code); - if (!g->root_out_name) { - add_node_error(g, g->root_import->root, - buf_sprintf("missing export declaration and output name not provided")); - } else if (g->out_type == OutTypeUnknown) { - add_node_error(g, g->root_import->root, - buf_sprintf("missing export declaration and export type not provided")); - } + assert(g->root_out_name); + assert(g->out_type != OutTypeUnknown); if (!g->link_libc && !g->is_test_build) { if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) { - g->bootstrap_import = add_special_code(g, "bootstrap.zig"); + g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g), "bootstrap.zig"); } } @@ -4120,7 +3963,7 @@ void codegen_generate_h_file(CodeGen *g) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; - if (fn_proto->visib_mod != VisibModExport) + if (fn_proto->top_level_decl.visib_mod != VisibModExport) continue; Buf return_type_c = BUF_INIT; diff --git a/src/codegen.hpp b/src/codegen.hpp index d802deeed3..dba52b8bfb 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -19,6 +19,7 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target); void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len); void codegen_set_is_release(CodeGen *codegen, bool is_release); void codegen_set_is_test(CodeGen *codegen, bool is_test); +void codegen_set_check_unused(CodeGen *codegen, bool check_unused); void codegen_set_is_static(CodeGen *codegen, bool is_static); void codegen_set_strip(CodeGen *codegen, bool strip); @@ -34,6 +35,7 @@ void codegen_set_linker_path(CodeGen *g, Buf *linker_path); void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole); void codegen_set_windows_unicode(CodeGen *g, bool municode); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); +void codegen_add_link_lib(CodeGen *codegen, const char *lib); void codegen_set_mlinker_version(CodeGen *g, Buf *darwin_linker_version); void codegen_set_rdynamic(CodeGen *g, bool rdynamic); void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min); diff --git a/src/errmsg.cpp b/src/errmsg.cpp index e8ab353efc..7920620418 100644 --- a/src/errmsg.cpp +++ b/src/errmsg.cpp @@ -4,36 +4,57 @@ #include <stdio.h> #define RED "\x1b[31;1m" -#define WHITE "\x1b[37;1m" #define GREEN "\x1b[32;1m" +#define CYAN "\x1b[36;1m" +#define WHITE "\x1b[37;1m" #define RESET "\x1b[0m" -void print_err_msg(ErrorMsg *err, ErrColor color) { +enum ErrType { + ErrTypeError, + ErrTypeNote, +}; + +static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) { + const char *path = buf_ptr(err->path); + int line = err->line_start + 1; + int col = err->column_start + 1; + const char *text = buf_ptr(err->msg); + + if (color == ErrColorOn || (color == ErrColorAuto && os_stderr_tty())) { - fprintf(stderr, WHITE "%s:%d:%d: " RED "error:" WHITE " %s" RESET "\n", - buf_ptr(err->path), - err->line_start + 1, err->column_start + 1, - buf_ptr(err->msg)); + if (err_type == ErrTypeError) { + fprintf(stderr, WHITE "%s:%d:%d: " RED "error:" WHITE " %s" RESET "\n", path, line, col, text); + } else if (err_type == ErrTypeNote) { + fprintf(stderr, WHITE "%s:%d:%d: " CYAN "note:" WHITE " %s" RESET "\n", path, line, col, text); + } else { + zig_unreachable(); + } fprintf(stderr, "%s\n", buf_ptr(&err->line_buf)); for (int i = 0; i < err->column_start; i += 1) { fprintf(stderr, " "); } fprintf(stderr, GREEN "^" RESET "\n"); - } else { - fprintf(stderr, "%s:%d:%d: error: %s\n", - buf_ptr(err->path), - err->line_start + 1, err->column_start + 1, - buf_ptr(err->msg)); + if (err_type == ErrTypeError) { + fprintf(stderr, "%s:%d:%d: error: %s\n", path, line, col, text); + } else if (err_type == ErrTypeNote) { + fprintf(stderr, " %s:%d:%d: note: %s\n", path, line, col, text); + } else { + zig_unreachable(); + } } for (int i = 0; i < err->notes.length; i += 1) { ErrorMsg *note = err->notes.at(i); - print_err_msg(note, color); + print_err_msg_type(note, color, ErrTypeNote); } } +void print_err_msg(ErrorMsg *err, ErrColor color) { + print_err_msg_type(err, color, ErrTypeError); +} + void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) { parent->notes.append(note); } diff --git a/src/main.cpp b/src/main.cpp index c58f3a0942..564cad1758 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,8 +18,8 @@ static int usage(const char *arg0) { fprintf(stderr, "Usage: %s [command] [options]\n" "Commands:\n" - " build [source] create executable, object, or library from source\n" - " test [source] create and run a test build\n" + " build [sources] create executable, object, or library from source\n" + " test [sources] create and run a test build\n" " parseh [source] convert a c header file to zig extern declarations\n" " version print version number and exit\n" " targets list available compilation targets\n" @@ -40,6 +40,7 @@ static int usage(const char *arg0) { " -isystem [dir] add additional search path for other .h files\n" " -dirafter [dir] same as -isystem but do it last\n" " --library-path [dir] add a directory to the library search path\n" + " --library [lib] link against lib\n" " --target-arch [name] specify target architecture\n" " --target-os [name] specify target operating system\n" " --target-environ [name] specify target environment\n" @@ -50,6 +51,7 @@ static int usage(const char *arg0) { " -rdynamic add all symbols to the dynamic symbol table\n" " -mmacosx-version-min [ver] (darwin only) set Mac OS X deployment target\n" " -mios-version-min [ver] (darwin only) set iOS deployment target\n" + " --check-unused perform semantic analysis on unused declarations\n" , arg0); return EXIT_FAILURE; } @@ -118,6 +120,7 @@ int main(int argc, char **argv) { const char *linker_path = nullptr; ZigList<const char *> clang_argv = {0}; ZigList<const char *> lib_dirs = {0}; + ZigList<const char *> link_libs = {0}; int err; const char *target_arch = nullptr; const char *target_os = nullptr; @@ -129,6 +132,7 @@ int main(int argc, char **argv) { bool rdynamic = false; const char *mmacosx_version_min = nullptr; const char *mios_version_min = nullptr; + bool check_unused = false; for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; @@ -150,6 +154,8 @@ int main(int argc, char **argv) { municode = true; } else if (strcmp(arg, "-rdynamic") == 0) { rdynamic = true; + } else if (strcmp(arg, "--check-unused") == 0) { + check_unused = true; } else if (i + 1 >= argc) { return usage(arg0); } else { @@ -198,6 +204,8 @@ int main(int argc, char **argv) { clang_argv.append(argv[i]); } else if (strcmp(arg, "--library-path") == 0) { lib_dirs.append(argv[i]); + } else if (strcmp(arg, "--library") == 0) { + link_libs.append(argv[i]); } else if (strcmp(arg, "--target-arch") == 0) { target_arch = argv[i]; } else if (strcmp(arg, "--target-os") == 0) { @@ -258,6 +266,16 @@ int main(int argc, char **argv) { if (!in_file) return usage(arg0); + if (cmd == CmdBuild && !out_name) { + fprintf(stderr, "--name [name] not provided\n\n"); + return usage(arg0); + } + + if (cmd == CmdBuild && out_type == OutTypeUnknown) { + fprintf(stderr, "--export [exe|lib|obj] not provided\n\n"); + return usage(arg0); + } + init_all_targets(); ZigTarget alloc_target; @@ -313,6 +331,8 @@ int main(int argc, char **argv) { codegen_set_is_release(g, is_release_build); codegen_set_is_test(g, cmd == CmdTest); + codegen_set_check_unused(g, check_unused); + codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); codegen_set_strip(g, strip); codegen_set_is_static(g, is_static); @@ -342,6 +362,9 @@ int main(int argc, char **argv) { for (int i = 0; i < lib_dirs.length; i += 1) { codegen_add_lib_dir(g, lib_dirs.at(i)); } + for (int i = 0; i < link_libs.length; i += 1) { + codegen_add_link_lib(g, link_libs.at(i)); + } codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_windows_unicode(g, municode); diff --git a/src/parseh.cpp b/src/parseh.cpp index 3513092e70..929a22c8b1 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -121,9 +121,9 @@ static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char AstNode *node = create_node(c, NodeTypeVariableDeclaration); buf_init_from_str(&node->data.variable_declaration.symbol, var_name); node->data.variable_declaration.is_const = is_const; - node->data.variable_declaration.visib_mod = c->visib_mod; + node->data.variable_declaration.top_level_decl.visib_mod = c->visib_mod; node->data.variable_declaration.expr = init_node; - node->data.variable_declaration.directives = nullptr; + node->data.variable_declaration.top_level_decl.directives = nullptr; node->data.variable_declaration.type = type_node; normalize_parent_ptrs(node); return node; @@ -146,7 +146,7 @@ static AstNode *create_struct_field_node(Context *c, const char *name, AstNode * assert(type_node); AstNode *node = create_node(c, NodeTypeStructField); buf_init_from_str(&node->data.struct_field.name, name); - node->data.struct_field.visib_mod = VisibModPub; + node->data.struct_field.top_level_decl.visib_mod = VisibModPub; node->data.struct_field.type = type_node; normalize_parent_ptrs(node); @@ -202,7 +202,7 @@ static AstNode *create_num_lit_signed(Context *c, int64_t x) { static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *child_type_node) { AstNode *node = create_node(c, NodeTypeTypeDecl); buf_init_from_str(&node->data.type_decl.symbol, name); - node->data.type_decl.visib_mod = c->visib_mod; + node->data.type_decl.top_level_decl.visib_mod = c->visib_mod; node->data.type_decl.child_type = child_type_node; normalize_parent_ptrs(node); @@ -219,7 +219,7 @@ static AstNode *create_fn_proto_node(Context *c, Buf *name, TypeTableEntry *fn_t assert(fn_type->id == TypeTableEntryIdFn); AstNode *node = create_node(c, NodeTypeFnProto); node->data.fn_proto.is_inline = true; - node->data.fn_proto.visib_mod = c->visib_mod; + node->data.fn_proto.top_level_decl.visib_mod = c->visib_mod; buf_init_from_buf(&node->data.fn_proto.name, name); node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); @@ -677,7 +677,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { buf_init_from_buf(&node->data.fn_proto.name, &fn_name); node->data.fn_proto.is_extern = fn_type->data.fn.fn_type_id.is_extern; - node->data.fn_proto.visib_mod = c->visib_mod; + node->data.fn_proto.top_level_decl.visib_mod = c->visib_mod; node->data.fn_proto.is_var_args = fn_type->data.fn.fn_type_id.is_var_args; node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); @@ -861,7 +861,7 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { AstNode *enum_node = create_node(c, NodeTypeStructDecl); buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name); enum_node->data.struct_decl.kind = ContainerKindEnum; - enum_node->data.struct_decl.visib_mod = VisibModExport; + enum_node->data.struct_decl.top_level_decl.visib_mod = VisibModExport; enum_node->data.struct_decl.type_entry = enum_type; for (uint32_t i = 0; i < field_count; i += 1) { @@ -1043,7 +1043,7 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) { AstNode *struct_node = create_node(c, NodeTypeStructDecl); buf_init_from_buf(&struct_node->data.struct_decl.name, &struct_type->name); struct_node->data.struct_decl.kind = ContainerKindStruct; - struct_node->data.struct_decl.visib_mod = VisibModExport; + struct_node->data.struct_decl.top_level_decl.visib_mod = VisibModExport; struct_node->data.struct_decl.type_entry = struct_type; for (uint32_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { diff --git a/src/parser.cpp b/src/parser.cpp index 1a5cc503cd..f9115181d1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -20,7 +20,6 @@ struct ParseContext { ZigList<Token> *tokens; ImportTableEntry *owner; ErrColor err_color; - bool parsed_root_export; uint32_t *next_node_index; }; @@ -1741,8 +1740,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, first_token); node->data.variable_declaration.is_const = is_const; - node->data.variable_declaration.visib_mod = visib_mod; - node->data.variable_declaration.directives = directives; + 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); ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol); @@ -2251,8 +2250,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeFnProto, first_token); - node->data.fn_proto.visib_mod = visib_mod; - node->data.fn_proto.directives = directives; + node->data.fn_proto.top_level_decl.visib_mod = visib_mod; + node->data.fn_proto.top_level_decl.directives = directives; Token *fn_name = &pc->tokens->at(*token_index); if (fn_name->id == TokenIdSymbol) { @@ -2345,76 +2344,23 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, int *token_index, bool m } /* -RootExportDecl : "export" "Symbol" "String" ";" +UseDecl = "use" Expression ";" */ -static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, - ZigList<AstNode*> *directives) -{ - Token *export_type = &pc->tokens->at(*token_index); - if (export_type->id != TokenIdSymbol) - return nullptr; - - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeRootExportDecl, export_type); - node->data.root_export_decl.directives = directives; - - ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type); - - Token *export_name = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, export_name, TokenIdStringLiteral); - - parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr, nullptr); - - Token *semicolon = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, semicolon, TokenIdSemicolon); - - normalize_parent_ptrs(node); - return node; -} - -/* -Import : "import" "String" ";" -*/ -static AstNode *ast_parse_import(ParseContext *pc, int *token_index, +static AstNode *ast_parse_use(ParseContext *pc, int *token_index, ZigList<AstNode*> *directives, VisibMod visib_mod) { - Token *import_kw = &pc->tokens->at(*token_index); - if (import_kw->id != TokenIdKeywordImport) + Token *use_kw = &pc->tokens->at(*token_index); + if (use_kw->id != TokenIdKeywordUse) return nullptr; *token_index += 1; - Token *import_name = ast_eat_token(pc, token_index, TokenIdStringLiteral); + 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); - AstNode *node = ast_create_node(pc, NodeTypeImport, import_kw); - node->data.import.visib_mod = visib_mod; - node->data.import.directives = directives; - - parse_string_literal(pc, import_name, &node->data.import.path, nullptr, nullptr); - normalize_parent_ptrs(node); - return node; -} - -/* -CImportDecl : "c_import" Block -*/ -static AstNode *ast_parse_c_import(ParseContext *pc, int *token_index, - ZigList<AstNode*> *directives, VisibMod visib_mod) -{ - Token *c_import_kw = &pc->tokens->at(*token_index); - if (c_import_kw->id != TokenIdKeywordCImport) - return nullptr; - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeCImport, c_import_kw); - node->data.c_import.visib_mod = visib_mod; - node->data.c_import.directives = directives; - node->data.c_import.block = ast_parse_block(pc, token_index, true); - normalize_parent_ptrs(node); return node; } @@ -2445,8 +2391,8 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index, AstNode *node = ast_create_node(pc, NodeTypeStructDecl, first_token); node->data.struct_decl.kind = kind; ast_buf_from_token(pc, struct_name, &node->data.struct_decl.name); - node->data.struct_decl.visib_mod = visib_mod; - node->data.struct_decl.directives = directives; + node->data.struct_decl.top_level_decl.visib_mod = visib_mod; + node->data.struct_decl.top_level_decl.directives = directives; ast_eat_token(pc, token_index, TokenIdLBrace); @@ -2486,8 +2432,8 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index, AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); *token_index += 1; - field_node->data.struct_field.visib_mod = visib_mod; - field_node->data.struct_field.directives = directive_list; + field_node->data.struct_field.top_level_decl.visib_mod = visib_mod; + field_node->data.struct_field.top_level_decl.directives = directive_list; ast_buf_from_token(pc, token, &field_node->data.struct_field.name); @@ -2529,8 +2475,8 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, ast_eat_token(pc, token_index, TokenIdSemicolon); AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token); - node->data.error_value_decl.visib_mod = visib_mod; - node->data.error_value_decl.directives = directives; + node->data.error_value_decl.top_level_decl.visib_mod = visib_mod; + node->data.error_value_decl.top_level_decl.directives = directives; ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name); normalize_parent_ptrs(node); @@ -2559,15 +2505,15 @@ static AstNode *ast_parse_type_decl(ParseContext *pc, int *token_index, ast_eat_token(pc, token_index, TokenIdSemicolon); - node->data.type_decl.visib_mod = visib_mod; - node->data.type_decl.directives = directives; + 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 | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl) +TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl) */ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) { for (;;) { @@ -2587,17 +2533,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis visib_mod = VisibModPrivate; } - bool try_to_parse_root_export = (visib_mod == VisibModExport && !pc->parsed_root_export); - pc->parsed_root_export = true; - - if (try_to_parse_root_export) { - AstNode *root_export_decl_node = ast_parse_root_export_decl(pc, token_index, directives); - if (root_export_decl_node) { - top_level_decls->append(root_export_decl_node); - continue; - } - } - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, directives, visib_mod); if (fn_def_node) { top_level_decls->append(fn_def_node); @@ -2610,15 +2545,9 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis continue; } - AstNode *import_node = ast_parse_import(pc, token_index, directives, visib_mod); - if (import_node) { - top_level_decls->append(import_node); - continue; - } - - AstNode *c_import_node = ast_parse_c_import(pc, token_index, directives, visib_mod); - if (c_import_node) { - top_level_decls->append(c_import_node); + AstNode *use_node = ast_parse_use(pc, token_index, directives, visib_mod); + if (use_node) { + top_level_decls->append(use_node); continue; } @@ -2706,12 +2635,9 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeRoot: set_list_fields(&node->data.root.top_level_decls); break; - case NodeTypeRootExportDecl: - set_list_fields(node->data.root_export_decl.directives); - break; case NodeTypeFnProto: set_field(&node->data.fn_proto.return_type); - set_list_fields(node->data.fn_proto.directives); + set_list_fields(node->data.fn_proto.top_level_decl.directives); set_list_fields(&node->data.fn_proto.params); break; case NodeTypeFnDef: @@ -2737,12 +2663,12 @@ void normalize_parent_ptrs(AstNode *node) { set_field(&node->data.defer.expr); break; case NodeTypeVariableDeclaration: - set_list_fields(node->data.variable_declaration.directives); + set_list_fields(node->data.variable_declaration.top_level_decl.directives); set_field(&node->data.variable_declaration.type); set_field(&node->data.variable_declaration.expr); break; case NodeTypeTypeDecl: - set_list_fields(node->data.type_decl.directives); + set_list_fields(node->data.type_decl.top_level_decl.directives); set_field(&node->data.type_decl.child_type); break; case NodeTypeErrorValueDecl: @@ -2788,12 +2714,9 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeFieldAccessExpr: set_field(&node->data.field_access_expr.struct_expr); break; - case NodeTypeImport: - set_list_fields(node->data.import.directives); - break; - case NodeTypeCImport: - set_list_fields(node->data.c_import.directives); - set_field(&node->data.c_import.block); + case NodeTypeUse: + set_field(&node->data.use.expr); + set_list_fields(node->data.use.top_level_decl.directives); break; case NodeTypeBoolLiteral: // none @@ -2863,11 +2786,11 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeStructDecl: set_list_fields(&node->data.struct_decl.fields); set_list_fields(&node->data.struct_decl.fns); - set_list_fields(node->data.struct_decl.directives); + set_list_fields(node->data.struct_decl.top_level_decl.directives); break; case NodeTypeStructField: set_field(&node->data.struct_field.type); - set_list_fields(node->data.struct_field.directives); + set_list_fields(node->data.struct_field.top_level_decl.directives); break; case NodeTypeContainerInitExpr: set_field(&node->data.container_init_expr.type); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f6d7ec6f22..17e31b0a18 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -99,7 +99,7 @@ const char * zig_keywords[] = { "true", "false", "null", "fn", "return", "var", "const", "extern", - "pub", "export", "import", "c_import", "if", "else", "goto", "asm", + "pub", "export", "use", "if", "else", "goto", "asm", "volatile", "struct", "enum", "while", "for", "continue", "break", "null", "noalias", "switch", "undefined", "error", "type", "inline", "defer", @@ -232,10 +232,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordPub; } else if (mem_eql_str(token_mem, token_len, "export")) { t->cur_tok->id = TokenIdKeywordExport; - } else if (mem_eql_str(token_mem, token_len, "c_import")) { - t->cur_tok->id = TokenIdKeywordCImport; - } else if (mem_eql_str(token_mem, token_len, "import")) { - t->cur_tok->id = TokenIdKeywordImport; + } else if (mem_eql_str(token_mem, token_len, "use")) { + t->cur_tok->id = TokenIdKeywordUse; } else if (mem_eql_str(token_mem, token_len, "true")) { t->cur_tok->id = TokenIdKeywordTrue; } else if (mem_eql_str(token_mem, token_len, "false")) { @@ -1071,8 +1069,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordExtern: return "extern"; case TokenIdKeywordPub: return "pub"; case TokenIdKeywordExport: return "export"; - case TokenIdKeywordImport: return "import"; - case TokenIdKeywordCImport: return "c_import"; + case TokenIdKeywordUse: return "use"; case TokenIdKeywordTrue: return "true"; case TokenIdKeywordFalse: return "false"; case TokenIdKeywordIf: return "if"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index de0010d0b9..0eeda70ef5 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -19,9 +19,8 @@ enum TokenId { TokenIdKeywordConst, TokenIdKeywordExtern, TokenIdKeywordPub, + TokenIdKeywordUse, TokenIdKeywordExport, - TokenIdKeywordImport, - TokenIdKeywordCImport, TokenIdKeywordTrue, TokenIdKeywordFalse, TokenIdKeywordIf, diff --git a/std/bootstrap.zig b/std/bootstrap.zig index f0f177dbfd..a047500d48 100644 --- a/std/bootstrap.zig +++ b/std/bootstrap.zig @@ -1,7 +1,7 @@ -import "syscall.zig"; +// This file is in a package which has the root source file exposed as "@root". -// The compiler treats this file special by implicitly importing the function `main` -// from the root source file as the symbol `zig_user_main`. +const root = @import("@root"); +const syscall = @import("syscall.zig"); const want_start_symbol = switch(@compile_var("os")) { linux => true, @@ -26,7 +26,7 @@ export fn _start() -> unreachable { }, else => unreachable{}, } - call_main() + call_main_and_exit() } fn strlen(ptr: &const u8) -> isize { @@ -37,23 +37,24 @@ fn strlen(ptr: &const u8) -> isize { return count; } -fn call_main() -> unreachable { +fn call_main() -> %void { var args: [argc][]u8 = undefined; for (args) |arg, i| { const ptr = argv[i]; args[i] = ptr[0...strlen(ptr)]; } - zig_user_main(args) %% exit(1); - exit(0); + return root.main(args); +} + +fn call_main_and_exit() -> unreachable { + call_main() %% syscall.exit(1); + syscall.exit(0); } #condition(want_main_symbol) -export fn main(argc: i32, argv: &&u8) -> i32 { - var args: [argc][]u8 = undefined; - for (args) |arg, i| { - const ptr = argv[i]; - args[i] = ptr[0...strlen(ptr)]; - } - zig_user_main(args) %% return 1; +export fn main(c_argc: i32, c_argv: &&u8) -> i32 { + argc = c_argc; + argv = c_argv; + call_main() %% return 1; return 0; } diff --git a/std/index.zig b/std/index.zig new file mode 100644 index 0000000000..88827dc798 --- /dev/null +++ b/std/index.zig @@ -0,0 +1,4 @@ +pub const Rand = @import("rand.zig").Rand; +pub const io = @import("io.zig"); +pub const os = @import("os.zig"); +pub const math = @import("math.zig"); diff --git a/std/std.zig b/std/io.zig index 5d1e7a78ca..272459e4e7 100644 --- a/std/std.zig +++ b/std/io.zig @@ -1,6 +1,6 @@ -import "syscall.zig"; -import "errno.zig"; -import "math.zig"; +const syscall = @import("syscall.zig"); +const errno = @import("errno.zig"); +const math = @import("math.zig"); pub const stdin_fileno = 0; pub const stdout_fileno = 1; @@ -55,7 +55,7 @@ pub struct OutStream { const dest_space_left = os.buffer.len - os.index; while (src_bytes_left > 0) { - const copy_amt = min_isize(dest_space_left, src_bytes_left); + const copy_amt = math.min_isize(dest_space_left, src_bytes_left); @memcpy(&os.buffer[os.index], &str[src_index], copy_amt); os.index += copy_amt; if (os.index == os.buffer.len) { @@ -105,19 +105,19 @@ pub struct OutStream { } pub fn flush(os: &OutStream) -> %void { - const amt_written = write(os.fd, &os.buffer[0], os.index); + const amt_written = syscall.write(os.fd, &os.buffer[0], os.index); os.index = 0; if (amt_written < 0) { return switch (-amt_written) { - EINVAL => unreachable{}, - EDQUOT => error.DiskQuota, - EFBIG => error.FileTooBig, - EINTR => error.SigInterrupt, - EIO => error.Io, - ENOSPC => error.NoSpaceLeft, - EPERM => error.BadPerm, - EPIPE => error.PipeFail, - else => error.Unexpected, + errno.EINVAL => unreachable{}, + errno.EDQUOT => error.DiskQuota, + errno.EFBIG => error.FileTooBig, + errno.EINTR => error.SigInterrupt, + errno.EIO => error.Io, + errno.ENOSPC => error.NoSpaceLeft, + errno.EPERM => error.BadPerm, + errno.EPIPE => error.PipeFail, + else => error.Unexpected, } } } @@ -139,15 +139,15 @@ pub struct InStream { fd: isize, pub fn read(is: &InStream, buf: []u8) -> %isize { - const amt_read = read(is.fd, &buf[0], buf.len); + const amt_read = syscall.read(is.fd, &buf[0], buf.len); if (amt_read < 0) { return switch (-amt_read) { - EINVAL => unreachable{}, - EFAULT => unreachable{}, - EBADF => error.BadFd, - EINTR => error.SigInterrupt, - EIO => error.Io, - else => error.Unexpected, + errno.EINVAL => unreachable{}, + errno.EFAULT => unreachable{}, + errno.EBADF => error.BadFd, + errno.EINTR => error.SigInterrupt, + errno.EIO => error.Io, + else => error.Unexpected, } } return amt_read; @@ -168,8 +168,8 @@ pub struct InStream { #attribute("cold") pub fn abort() -> unreachable { - raise(SIGABRT); - raise(SIGKILL); + syscall.raise(syscall.SIGABRT); + syscall.raise(syscall.SIGKILL); while (true) {} } @@ -253,15 +253,15 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { decs = max_u64_base10_digits - 1; } - if (x == f64_get_pos_inf()) { + if (x == math.f64_get_pos_inf()) { const buf2 = "+Inf"; @memcpy(&out_buf[0], &buf2[0], buf2.len); return 4; - } else if (x == f64_get_neg_inf()) { + } else if (x == math.f64_get_neg_inf()) { const buf2 = "-Inf"; @memcpy(&out_buf[0], &buf2[0], buf2.len); return 4; - } else if (f64_is_nan(x)) { + } else if (math.f64_is_nan(x)) { const buf2 = "NaN"; @memcpy(&out_buf[0], &buf2[0], buf2.len); return 3; @@ -275,7 +275,7 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize { // 11 exponent bits // 52 significand bits (+ 1 implicit always non-zero bit) - const bits = f64_to_bits(x); + const bits = math.f64_to_bits(x); if (bits & (1 << 63) != 0) { buf[0] = '-'; len += 1; diff --git a/std/os.zig b/std/os.zig index 75cc163f21..287b76191d 100644 --- a/std/os.zig +++ b/std/os.zig @@ -1,19 +1,19 @@ -import "syscall.zig"; -import "errno.zig"; +const syscall = @import("syscall.zig"); +const errno = @import("errno.zig"); pub error SigInterrupt; pub error Unexpected; -pub fn os_get_random_bytes(buf: []u8) -> %void { +pub fn get_random_bytes(buf: []u8) -> %void { switch (@compile_var("os")) { linux => { - const amt_got = getrandom(buf.ptr, buf.len, 0); + const amt_got = syscall.getrandom(buf.ptr, buf.len, 0); if (amt_got < 0) { return switch (-amt_got) { - EINVAL => unreachable{}, - EFAULT => unreachable{}, - EINTR => error.SigInterrupt, - else => error.Unexpected, + errno.EINVAL => unreachable{}, + errno.EFAULT => unreachable{}, + errno.EINTR => error.SigInterrupt, + else => error.Unexpected, } } }, diff --git a/std/rand.zig b/std/rand.zig index 9149304cf8..5d2d547652 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -84,26 +84,26 @@ pub struct Rand { } return bytes_left; } -} -/// Initialize random state with the given seed. -pub fn rand_new(seed: u32) -> Rand { - var r: Rand = undefined; - r.index = 0; - r.array[0] = seed; - var i : isize = 1; - var prev_value: u64 = seed; - while (i < ARRAY_SIZE) { - r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i)); - prev_value = r.array[i]; - i += 1; + /// Initialize random state with the given seed. + pub fn init(seed: u32) -> Rand { + var r: Rand = undefined; + r.index = 0; + r.array[0] = seed; + var i : isize = 1; + var prev_value: u64 = seed; + while (i < ARRAY_SIZE) { + r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i)); + prev_value = r.array[i]; + i += 1; + } + return r; } - return r; } #attribute("test") fn test_float32() { - var r = rand_new(42); + var r = Rand.init(42); // TODO for loop with range var i: i32 = 0; diff --git a/std/test_runner.zig b/std/test_runner.zig index 6715371cd2..5123eff8e2 100644 --- a/std/test_runner.zig +++ b/std/test_runner.zig @@ -1,4 +1,4 @@ -import "std.zig"; +const io = @import("std").io; struct TestFn { name: []u8, @@ -9,19 +9,19 @@ extern var zig_test_fn_list: []TestFn; pub fn run_tests() -> %void { for (zig_test_fn_list) |test_fn, i| { - %%stderr.print_str("Test "); - %%stderr.print_i64(i + 1); - %%stderr.print_str("/"); - %%stderr.print_i64(zig_test_fn_list.len); - %%stderr.print_str(" "); - %%stderr.print_str(test_fn.name); - %%stderr.print_str("..."); - %%stderr.flush(); + %%io.stderr.print_str("Test "); + %%io.stderr.print_i64(i + 1); + %%io.stderr.print_str("/"); + %%io.stderr.print_i64(zig_test_fn_list.len); + %%io.stderr.print_str(" "); + %%io.stderr.print_str(test_fn.name); + %%io.stderr.print_str("..."); + %%io.stderr.flush(); test_fn.func(); - %%stderr.print_str("OK\n"); - %%stderr.flush(); + %%io.stderr.print_str("OK\n"); + %%io.stderr.flush(); } } diff --git a/std/test_runner_libc.zig b/std/test_runner_libc.zig index 9f7c9e7faa..ff39c3325c 100644 --- a/std/test_runner_libc.zig +++ b/std/test_runner_libc.zig @@ -1,6 +1,6 @@ -import "test_runner.zig"; +const test_runner = @import("test_runner.zig"); export fn main(argc: c_int, argv: &&u8) -> c_int { - run_tests() %% return -1; + test_runner.run_tests() %% return -1; return 0; } diff --git a/std/test_runner_nolibc.zig b/std/test_runner_nolibc.zig index 4e27ca551b..b8b5a2c2d9 100644 --- a/std/test_runner_nolibc.zig +++ b/std/test_runner_nolibc.zig @@ -1,5 +1,5 @@ -import "test_runner.zig"; +const test_runner = @import("test_runner.zig"); pub fn main(args: [][]u8) -> %void { - return run_tests(); + return test_runner.run_tests(); } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 3a8a2ef629..a87fe78c13 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -70,12 +70,20 @@ static TestCase *add_simple_case(const char *case_name, const char *source, cons test_case->compiler_args.append("--strip"); test_case->compiler_args.append("--color"); test_case->compiler_args.append("on"); + test_case->compiler_args.append("--check-unused"); test_cases.append(test_case); return test_case; } +static TestCase *add_simple_case_libc(const char *case_name, const char *source, const char *output) { + TestCase *tc = add_simple_case(case_name, source, output); + tc->compiler_args.append("--library"); + tc->compiler_args.append("c"); + return tc; +} + static TestCase *add_compile_fail_case(const char *case_name, const char *source, int count, ...) { va_list ap; va_start(ap, count); @@ -93,11 +101,19 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source test_case->compiler_args.append("build"); test_case->compiler_args.append(tmp_source_path); + + test_case->compiler_args.append("--name"); + test_case->compiler_args.append("test"); + + test_case->compiler_args.append("--export"); + test_case->compiler_args.append("obj"); + test_case->compiler_args.append("--output"); test_case->compiler_args.append(tmp_exe_path); + test_case->compiler_args.append("--release"); test_case->compiler_args.append("--strip"); - //test_case->compiler_args.append("--verbose"); + test_case->compiler_args.append("--check-unused"); test_cases.append(test_case); @@ -134,43 +150,18 @@ static TestCase *add_parseh_case(const char *case_name, const char *source, int } static void add_compiling_test_cases(void) { - add_simple_case("hello world with libc", R"SOURCE( -#link("c") -export executable "test"; - -c_import { - @c_include("stdio.h"); -} - + add_simple_case_libc("hello world with libc", R"SOURCE( +const c = @c_import(@c_include("stdio.h")); export fn main(argc: c_int, argv: &&u8) -> c_int { - puts(c"Hello, world!"); + c.puts(c"Hello, world!"); return 0; } )SOURCE", "Hello, world!" NL); - add_simple_case("function call", R"SOURCE( -import "std.zig"; -import "syscall.zig"; - -fn empty_function_1() {} -fn empty_function_2() { return; } - -pub fn main(args: [][]u8) -> %void { - empty_function_1(); - empty_function_2(); - this_is_a_function(); -} - -fn this_is_a_function() -> unreachable { - %%stdout.printf("OK\n"); - exit(0); -} - )SOURCE", "OK\n"); - { TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE( -import "std.zig"; -import "foo.zig"; +use @import("std").io; +use @import("foo.zig"); pub fn main(args: [][]u8) -> %void { private_function(); @@ -183,7 +174,7 @@ fn private_function() { )SOURCE", "OK 1\nOK 2\n"); add_source_file(tc, "foo.zig", R"SOURCE( -import "std.zig"; +use @import("std").io; // purposefully conflicting function with main.zig // but it's private so it should be OK @@ -199,8 +190,8 @@ pub fn print_text() { { TestCase *tc = add_simple_case("import segregation", R"SOURCE( -import "foo.zig"; -import "bar.zig"; +use @import("foo.zig"); +use @import("bar.zig"); pub fn main(args: [][]u8) -> %void { foo_function(); @@ -209,15 +200,15 @@ pub fn main(args: [][]u8) -> %void { )SOURCE", "OK\nOK\n"); add_source_file(tc, "foo.zig", R"SOURCE( -import "std.zig"; +use @import("std").io; pub fn foo_function() { %%stdout.printf("OK\n"); } )SOURCE"); add_source_file(tc, "bar.zig", R"SOURCE( -import "other.zig"; -import "std.zig"; +use @import("other.zig"); +use @import("std").io; pub fn bar_function() { if (foo_function()) { @@ -234,8 +225,35 @@ pub fn foo_function() -> bool { )SOURCE"); } + { + TestCase *tc = add_simple_case("two files use import each other", R"SOURCE( +use @import("a.zig"); + +pub fn main(args: [][]u8) -> %void { + ok(); +} + )SOURCE", "OK\n"); + + add_source_file(tc, "a.zig", R"SOURCE( +use @import("b.zig"); +const io = @import("std").io; + +pub const a_text = "OK\n"; + +pub fn ok() { + %%io.stdout.printf(b_text); +} + )SOURCE"); + + add_source_file(tc, "b.zig", R"SOURCE( +use @import("a.zig"); + +pub const b_text = a_text; + )SOURCE"); + } + add_simple_case("params", R"SOURCE( -import "std.zig"; +const io = @import("std").io; fn add(a: i32, b: i32) -> i32 { a + b @@ -243,13 +261,13 @@ fn add(a: i32, b: i32) -> i32 { pub fn main(args: [][]u8) -> %void { if (add(22, 11) == 33) { - %%stdout.printf("pass\n"); + %%io.stdout.printf("pass\n"); } } )SOURCE", "pass\n"); add_simple_case("void parameters", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { void_fun(1, void{}, 2); @@ -258,28 +276,28 @@ pub fn main(args: [][]u8) -> %void { fn void_fun(a : i32, b : void, c : i32) { const v = b; const vv : void = if (a == 1) {v} else {}; - if (a + c == 3) { %%stdout.printf("OK\n"); } + if (a + c == 3) { %%io.stdout.printf("OK\n"); } return vv; } )SOURCE", "OK\n"); add_simple_case("mutable local variables", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var zero : i32 = 0; - if (zero == 0) { %%stdout.printf("zero\n"); } + if (zero == 0) { %%io.stdout.printf("zero\n"); } var i = i32(0); while (i != 3) { - %%stdout.printf("loop\n"); + %%io.stdout.printf("loop\n"); i += 1; } } )SOURCE", "zero\nloop\nloop\nloop\n"); add_simple_case("arrays", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var array : [5]i32 = undefined; @@ -299,11 +317,11 @@ pub fn main(args: [][]u8) -> %void { } if (accumulator == 15) { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } if (get_array_len(array) != 5) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } } fn get_array_len(a: []i32) -> isize { @@ -313,144 +331,139 @@ fn get_array_len(a: []i32) -> isize { add_simple_case("hello world without libc", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("Hello, world!\n"); + %%io.stdout.printf("Hello, world!\n"); } )SOURCE", "Hello, world!\n"); add_simple_case("short circuit", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - if (true || { %%stdout.printf("BAD 1\n"); false }) { - %%stdout.printf("OK 1\n"); + if (true || { %%io.stdout.printf("BAD 1\n"); false }) { + %%io.stdout.printf("OK 1\n"); } - if (false || { %%stdout.printf("OK 2\n"); false }) { - %%stdout.printf("BAD 2\n"); + if (false || { %%io.stdout.printf("OK 2\n"); false }) { + %%io.stdout.printf("BAD 2\n"); } - if (true && { %%stdout.printf("OK 3\n"); false }) { - %%stdout.printf("BAD 3\n"); + if (true && { %%io.stdout.printf("OK 3\n"); false }) { + %%io.stdout.printf("BAD 3\n"); } - if (false && { %%stdout.printf("BAD 4\n"); false }) { + if (false && { %%io.stdout.printf("BAD 4\n"); false }) { } else { - %%stdout.printf("OK 4\n"); + %%io.stdout.printf("OK 4\n"); } } )SOURCE", "OK 1\nOK 2\nOK 3\nOK 4\n"); add_simple_case("modify operators", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var i : i32 = 0; - i += 5; if (i != 5) { %%stdout.printf("BAD +=\n"); } - i -= 2; if (i != 3) { %%stdout.printf("BAD -=\n"); } - i *= 20; if (i != 60) { %%stdout.printf("BAD *=\n"); } - i /= 3; if (i != 20) { %%stdout.printf("BAD /=\n"); } - i %= 11; if (i != 9) { %%stdout.printf("BAD %=\n"); } - i <<= 1; if (i != 18) { %%stdout.printf("BAD <<=\n"); } - i >>= 2; if (i != 4) { %%stdout.printf("BAD >>=\n"); } + i += 5; if (i != 5) { %%io.stdout.printf("BAD +=\n"); } + i -= 2; if (i != 3) { %%io.stdout.printf("BAD -=\n"); } + i *= 20; if (i != 60) { %%io.stdout.printf("BAD *=\n"); } + i /= 3; if (i != 20) { %%io.stdout.printf("BAD /=\n"); } + i %= 11; if (i != 9) { %%io.stdout.printf("BAD %=\n"); } + i <<= 1; if (i != 18) { %%io.stdout.printf("BAD <<=\n"); } + i >>= 2; if (i != 4) { %%io.stdout.printf("BAD >>=\n"); } i = 6; - i &= 5; if (i != 4) { %%stdout.printf("BAD &=\n"); } - i ^= 6; if (i != 2) { %%stdout.printf("BAD ^=\n"); } + i &= 5; if (i != 4) { %%io.stdout.printf("BAD &=\n"); } + i ^= 6; if (i != 2) { %%io.stdout.printf("BAD ^=\n"); } i = 6; - i |= 3; if (i != 7) { %%stdout.printf("BAD |=\n"); } + i |= 3; if (i != 7) { %%io.stdout.printf("BAD |=\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); - add_simple_case("number literals", R"SOURCE( -#link("c") -export executable "test"; - -c_import { - @c_include("stdio.h"); -} + add_simple_case_libc("number literals", R"SOURCE( +const c = @c_import(@c_include("stdio.h")); export fn main(argc: c_int, argv: &&u8) -> c_int { - printf(c"\n"); + c.printf(c"\n"); - printf(c"0: %llu\n", + c.printf(c"0: %llu\n", u64(0)); - printf(c"320402575052271: %llu\n", + c.printf(c"320402575052271: %llu\n", u64(320402575052271)); - printf(c"0x01236789abcdef: %llu\n", + c.printf(c"0x01236789abcdef: %llu\n", u64(0x01236789abcdef)); - printf(c"0xffffffffffffffff: %llu\n", + c.printf(c"0xffffffffffffffff: %llu\n", u64(0xffffffffffffffff)); - printf(c"0x000000ffffffffffffffff: %llu\n", + c.printf(c"0x000000ffffffffffffffff: %llu\n", u64(0x000000ffffffffffffffff)); - printf(c"0o1777777777777777777777: %llu\n", + c.printf(c"0o1777777777777777777777: %llu\n", u64(0o1777777777777777777777)); - printf(c"0o0000001777777777777777777777: %llu\n", + c.printf(c"0o0000001777777777777777777777: %llu\n", u64(0o0000001777777777777777777777)); - printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n", + c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n", u64(0b1111111111111111111111111111111111111111111111111111111111111111)); - printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n", + c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n", u64(0b0000001111111111111111111111111111111111111111111111111111111111111111)); - printf(c"\n"); + c.printf(c"\n"); - printf(c"0.0: %a\n", + c.printf(c"0.0: %a\n", f64(0.0)); - printf(c"0e0: %a\n", + c.printf(c"0e0: %a\n", f64(0e0)); - printf(c"0.0e0: %a\n", + c.printf(c"0.0e0: %a\n", f64(0.0e0)); - printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n", + c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n", f64(000000000000000000000000000000000000000000000000000000000.0e0)); - printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n", + c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n", f64(0.000000000000000000000000000000000000000000000000000000000e0)); - printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n", + c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n", f64(0.0e000000000000000000000000000000000000000000000000000000000)); - printf(c"1.0: %a\n", + c.printf(c"1.0: %a\n", f64(1.0)); - printf(c"10.0: %a\n", + c.printf(c"10.0: %a\n", f64(10.0)); - printf(c"10.5: %a\n", + c.printf(c"10.5: %a\n", f64(10.5)); - printf(c"10.5e5: %a\n", + c.printf(c"10.5e5: %a\n", f64(10.5e5)); - printf(c"10.5e+5: %a\n", + c.printf(c"10.5e+5: %a\n", f64(10.5e+5)); - printf(c"50.0e-2: %a\n", + c.printf(c"50.0e-2: %a\n", f64(50.0e-2)); - printf(c"50e-2: %a\n", + c.printf(c"50e-2: %a\n", f64(50e-2)); - printf(c"\n"); + c.printf(c"\n"); - printf(c"0x1.0: %a\n", + c.printf(c"0x1.0: %a\n", f64(0x1.0)); - printf(c"0x10.0: %a\n", + c.printf(c"0x10.0: %a\n", f64(0x10.0)); - printf(c"0x100.0: %a\n", + c.printf(c"0x100.0: %a\n", f64(0x100.0)); - printf(c"0x103.0: %a\n", + c.printf(c"0x103.0: %a\n", f64(0x103.0)); - printf(c"0x103.7: %a\n", + c.printf(c"0x103.7: %a\n", f64(0x103.7)); - printf(c"0x103.70: %a\n", + c.printf(c"0x103.70: %a\n", f64(0x103.70)); - printf(c"0x103.70p4: %a\n", + c.printf(c"0x103.70p4: %a\n", f64(0x103.70p4)); - printf(c"0x103.70p5: %a\n", + c.printf(c"0x103.70p5: %a\n", f64(0x103.70p5)); - printf(c"0x103.70p+5: %a\n", + c.printf(c"0x103.70p+5: %a\n", f64(0x103.70p+5)); - printf(c"0x103.70p-5: %a\n", + c.printf(c"0x103.70p-5: %a\n", f64(0x103.70p-5)); - printf(c"\n"); + c.printf(c"\n"); - printf(c"0b10100.00010e0: %a\n", + c.printf(c"0b10100.00010e0: %a\n", f64(0b10100.00010e0)); - printf(c"0o10700.00010e0: %a\n", + c.printf(c"0o10700.00010e0: %a\n", f64(0o10700.00010e0)); return 0; @@ -496,7 +509,7 @@ export fn main(argc: c_int, argv: &&u8) -> c_int { )OUTPUT"); add_simple_case("structs", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var foo : Foo = undefined; @@ -506,12 +519,12 @@ pub fn main(args: [][]u8) -> %void { test_foo(foo); test_mutation(&foo); if (foo.c != 100) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } test_point_to_self(); test_byval_assign(); test_initializer(); - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } struct Foo { a : i32, @@ -520,7 +533,7 @@ struct Foo { } fn test_foo(foo : Foo) { if (!foo.b) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } } fn test_mutation(foo : &Foo) { @@ -545,7 +558,7 @@ fn test_point_to_self() { root.next = &node; if (node.next.next.next.val.x != 1) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } } fn test_byval_assign() { @@ -554,38 +567,38 @@ fn test_byval_assign() { foo1.a = 1234; - if (foo2.a != 0) { %%stdout.printf("BAD\n"); } + if (foo2.a != 0) { %%io.stdout.printf("BAD\n"); } foo2 = foo1; - if (foo2.a != 1234) { %%stdout.printf("BAD - byval assignment failed\n"); } + if (foo2.a != 1234) { %%io.stdout.printf("BAD - byval assignment failed\n"); } } fn test_initializer() { const val = Val { .x = 42 }; - if (val.x != 42) { %%stdout.printf("BAD\n"); } + if (val.x != 42) { %%io.stdout.printf("BAD\n"); } } )SOURCE", "OK\n"); add_simple_case("global variables", R"SOURCE( -import "std.zig"; +const io = @import("std").io; const g1 : i32 = 1233 + 1; var g2 : i32 = 0; pub fn main(args: [][]u8) -> %void { - if (g2 != 0) { %%stdout.printf("BAD\n"); } + if (g2 != 0) { %%io.stdout.printf("BAD\n"); } g2 = g1; - if (g2 != 1234) { %%stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + if (g2 != 1234) { %%io.stdout.printf("BAD\n"); } + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("while loop", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var i : i32 = 0; while (i < 4) { - %%stdout.printf("loop\n"); + %%io.stdout.printf("loop\n"); i += 1; } g(); @@ -601,11 +614,11 @@ fn f() -> i32 { )SOURCE", "loop\nloop\nloop\nloop\n"); add_simple_case("continue and break", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var i : i32 = 0; while (true) { - %%stdout.printf("loop\n"); + %%io.stdout.printf("loop\n"); i += 1; if (i < 4) { continue; @@ -616,11 +629,11 @@ pub fn main(args: [][]u8) -> %void { )SOURCE", "loop\nloop\nloop\nloop\n"); add_simple_case("implicit cast after unreachable", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { const x = outer(); if (x == 1234) { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } } fn inner() -> i32 { 1234 } @@ -630,18 +643,18 @@ fn outer() -> isize { )SOURCE", "OK\n"); add_simple_case("@sizeof() and @typeof()", R"SOURCE( -import "std.zig"; +const io = @import("std").io; const x: u16 = 13; const z: @typeof(x) = 19; pub fn main(args: [][]u8) -> %void { const y: @typeof(x) = 120; - %%stdout.print_u64(@sizeof(@typeof(y))); - %%stdout.printf("\n"); + %%io.stdout.print_u64(@sizeof(@typeof(y))); + %%io.stdout.printf("\n"); } )SOURCE", "2\n"); add_simple_case("member functions", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Rand { seed: u32, pub fn get_seed(r: Rand) -> u32 { @@ -651,14 +664,14 @@ struct Rand { pub fn main(args: [][]u8) -> %void { const r = Rand {.seed = 1234}; if (r.get_seed() != 1234) { - %%stdout.printf("BAD seed\n"); + %%io.stdout.printf("BAD seed\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("pointer dereferencing", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var x = i32(3); @@ -667,93 +680,93 @@ pub fn main(args: [][]u8) -> %void { *y += 1; if (x != 4) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } if (*y != 4) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("constant expressions", R"SOURCE( -import "std.zig"; +const io = @import("std").io; const ARRAY_SIZE : i8 = 20; pub fn main(args: [][]u8) -> %void { var array : [ARRAY_SIZE]u8 = undefined; - %%stdout.print_u64(@sizeof(@typeof(array))); - %%stdout.printf("\n"); + %%io.stdout.print_u64(@sizeof(@typeof(array))); + %%io.stdout.printf("\n"); } )SOURCE", "20\n"); add_simple_case("@min_value() and @max_value()", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("max u8: "); - %%stdout.print_u64(@max_value(u8)); - %%stdout.printf("\n"); + %%io.stdout.printf("max u8: "); + %%io.stdout.print_u64(@max_value(u8)); + %%io.stdout.printf("\n"); - %%stdout.printf("max u16: "); - %%stdout.print_u64(@max_value(u16)); - %%stdout.printf("\n"); + %%io.stdout.printf("max u16: "); + %%io.stdout.print_u64(@max_value(u16)); + %%io.stdout.printf("\n"); - %%stdout.printf("max u32: "); - %%stdout.print_u64(@max_value(u32)); - %%stdout.printf("\n"); + %%io.stdout.printf("max u32: "); + %%io.stdout.print_u64(@max_value(u32)); + %%io.stdout.printf("\n"); - %%stdout.printf("max u64: "); - %%stdout.print_u64(@max_value(u64)); - %%stdout.printf("\n"); + %%io.stdout.printf("max u64: "); + %%io.stdout.print_u64(@max_value(u64)); + %%io.stdout.printf("\n"); - %%stdout.printf("max i8: "); - %%stdout.print_i64(@max_value(i8)); - %%stdout.printf("\n"); + %%io.stdout.printf("max i8: "); + %%io.stdout.print_i64(@max_value(i8)); + %%io.stdout.printf("\n"); - %%stdout.printf("max i16: "); - %%stdout.print_i64(@max_value(i16)); - %%stdout.printf("\n"); + %%io.stdout.printf("max i16: "); + %%io.stdout.print_i64(@max_value(i16)); + %%io.stdout.printf("\n"); - %%stdout.printf("max i32: "); - %%stdout.print_i64(@max_value(i32)); - %%stdout.printf("\n"); + %%io.stdout.printf("max i32: "); + %%io.stdout.print_i64(@max_value(i32)); + %%io.stdout.printf("\n"); - %%stdout.printf("max i64: "); - %%stdout.print_i64(@max_value(i64)); - %%stdout.printf("\n"); + %%io.stdout.printf("max i64: "); + %%io.stdout.print_i64(@max_value(i64)); + %%io.stdout.printf("\n"); - %%stdout.printf("min u8: "); - %%stdout.print_u64(@min_value(u8)); - %%stdout.printf("\n"); + %%io.stdout.printf("min u8: "); + %%io.stdout.print_u64(@min_value(u8)); + %%io.stdout.printf("\n"); - %%stdout.printf("min u16: "); - %%stdout.print_u64(@min_value(u16)); - %%stdout.printf("\n"); + %%io.stdout.printf("min u16: "); + %%io.stdout.print_u64(@min_value(u16)); + %%io.stdout.printf("\n"); - %%stdout.printf("min u32: "); - %%stdout.print_u64(@min_value(u32)); - %%stdout.printf("\n"); + %%io.stdout.printf("min u32: "); + %%io.stdout.print_u64(@min_value(u32)); + %%io.stdout.printf("\n"); - %%stdout.printf("min u64: "); - %%stdout.print_u64(@min_value(u64)); - %%stdout.printf("\n"); + %%io.stdout.printf("min u64: "); + %%io.stdout.print_u64(@min_value(u64)); + %%io.stdout.printf("\n"); - %%stdout.printf("min i8: "); - %%stdout.print_i64(@min_value(i8)); - %%stdout.printf("\n"); + %%io.stdout.printf("min i8: "); + %%io.stdout.print_i64(@min_value(i8)); + %%io.stdout.printf("\n"); - %%stdout.printf("min i16: "); - %%stdout.print_i64(@min_value(i16)); - %%stdout.printf("\n"); + %%io.stdout.printf("min i16: "); + %%io.stdout.print_i64(@min_value(i16)); + %%io.stdout.printf("\n"); - %%stdout.printf("min i32: "); - %%stdout.print_i64(@min_value(i32)); - %%stdout.printf("\n"); + %%io.stdout.printf("min i32: "); + %%io.stdout.print_i64(@min_value(i32)); + %%io.stdout.printf("\n"); - %%stdout.printf("min i64: "); - %%stdout.print_i64(@min_value(i64)); - %%stdout.printf("\n"); + %%io.stdout.printf("min i64: "); + %%io.stdout.print_i64(@min_value(i64)); + %%io.stdout.printf("\n"); } )SOURCE", "max u8: 255\n" @@ -775,10 +788,10 @@ pub fn main(args: [][]u8) -> %void { add_simple_case("else if expression", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { if (f(1) == 1) { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } } fn f(c: u8) -> u8 { @@ -793,82 +806,82 @@ fn f(c: u8) -> u8 { )SOURCE", "OK\n"); add_simple_case("overflow intrinsics", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var result: u8 = undefined; if (!@add_with_overflow(u8, 250, 100, &result)) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } if (@add_with_overflow(u8, 100, 150, &result)) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } if (result != 250) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("order-independent declarations", R"SOURCE( -import "std.zig"; -const z = stdin_fileno; +const io = @import("std").io; +const z = io.stdin_fileno; const x : @typeof(y) = 1234; const y : u16 = 5678; pub fn main(args: [][]u8) -> %void { - var x : i32 = print_ok(x); + var x_local : i32 = print_ok(x); } fn print_ok(val: @typeof(x)) -> @typeof(foo) { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); return 0; } const foo : i32 = 0; )SOURCE", "OK\n"); add_simple_case("nested arrays", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { const array_of_strings = [][]u8 {"hello", "this", "is", "my", "thing"}; for (array_of_strings) |str| { - %%stdout.printf(str); - %%stdout.printf("\n"); + %%io.stdout.printf(str); + %%io.stdout.printf("\n"); } } )SOURCE", "hello\nthis\nis\nmy\nthing\n"); add_simple_case("for loops", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { const array = []u8 {9, 8, 7, 6}; for (array) |item| { - %%stdout.print_u64(item); - %%stdout.printf("\n"); + %%io.stdout.print_u64(item); + %%io.stdout.printf("\n"); } for (array) |item, index| { - %%stdout.print_i64(index); - %%stdout.printf("\n"); + %%io.stdout.print_i64(index); + %%io.stdout.printf("\n"); } const unknown_size: []u8 = array; for (unknown_size) |item| { - %%stdout.print_u64(item); - %%stdout.printf("\n"); + %%io.stdout.print_u64(item); + %%io.stdout.printf("\n"); } for (unknown_size) |item, index| { - %%stdout.print_i64(index); - %%stdout.printf("\n"); + %%io.stdout.print_i64(index); + %%io.stdout.printf("\n"); } } )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n"); add_simple_case("function pointers", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { const fns = []@typeof(fn1) { fn1, fn2, fn3, fn4, }; for (fns) |f| { - %%stdout.print_u64(f()); - %%stdout.printf("\n"); + %%io.stdout.print_u64(f()); + %%io.stdout.printf("\n"); } } @@ -879,7 +892,7 @@ fn fn4() -> u32 {8} )SOURCE", "5\n6\n7\n8\n"); add_simple_case("statically initialized struct", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Foo { x: i32, y: bool, @@ -888,38 +901,38 @@ var foo = Foo { .x = 13, .y = true, }; pub fn main(args: [][]u8) -> %void { foo.x += 1; if (foo.x != 14) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("statically initialized array literal", R"SOURCE( -import "std.zig"; +const io = @import("std").io; const x = []u8{1,2,3,4}; pub fn main(args: [][]u8) -> %void { const y : [4]u8 = x; if (y[3] != 4) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("return with implicit cast from while loop", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { while (true) { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); return; } } )SOURCE", "OK\n"); add_simple_case("return struct byval from function", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Foo { x: i32, y: i32, @@ -933,14 +946,14 @@ fn make_foo(x: i32, y: i32) -> Foo { pub fn main(args: [][]u8) -> %void { const foo = make_foo(1234, 5678); if (foo.y != 5678) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("%% binary operator", R"SOURCE( -import "std.zig"; +const io = @import("std").io; error ItBroke; fn g(x: bool) -> %isize { if (x) { @@ -953,24 +966,24 @@ pub fn main(args: [][]u8) -> %void { const a = g(true) %% 3; const b = g(false) %% 3; if (a != 3) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } if (b != 10) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("string concatenation", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("OK" ++ " IT " ++ "WORKED\n"); + %%io.stdout.printf("OK" ++ " IT " ++ "WORKED\n"); } )SOURCE", "OK IT WORKED\n"); add_simple_case("constant struct with negation", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Vertex { x: f32, y: f32, @@ -985,30 +998,30 @@ const vertices = []Vertex { }; pub fn main(args: [][]u8) -> %void { if (vertices[0].x != -0.6) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("int to ptr cast", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { const x = isize(13); const y = (&u8)(x); const z = usize(y); if (z != 13) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("pointer to void return type", R"SOURCE( -import "std.zig"; +const io = @import("std").io; const x = void{}; fn f() -> &void { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); return &x; } pub fn main(args: [][]u8) -> %void { @@ -1018,7 +1031,7 @@ pub fn main(args: [][]u8) -> %void { )SOURCE", "OK\n"); add_simple_case("unwrap simple value from error", R"SOURCE( -import "std.zig"; +const io = @import("std").io; fn do() -> %isize { 13 } @@ -1026,14 +1039,14 @@ fn do() -> %isize { pub fn main(args: [][]u8) -> %void { const i = %%do(); if (i != 13) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("store member function in variable", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Foo { x: i32, fn member(foo: Foo) -> i32 { foo.x } @@ -1043,14 +1056,14 @@ pub fn main(args: [][]u8) -> %void { const member_fn = Foo.member; const result = member_fn(instance); if (result != 1234) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("call member function directly", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Foo { x: i32, fn member(foo: Foo) -> i32 { foo.x } @@ -1059,18 +1072,18 @@ pub fn main(args: [][]u8) -> %void { const instance = Foo { .x = 1234, }; const result = Foo.member(instance); if (result != 1234) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("call result of if else expression", R"SOURCE( -import "std.zig"; +const io = @import("std").io; fn a() -> []u8 { "a\n" } fn b() -> []u8 { "b\n" } fn f(x: bool) { - %%stdout.printf((if (x) a else b)()); + %%io.stdout.printf((if (x) a else b)()); } pub fn main(args: [][]u8) -> %void { f(true); @@ -1079,15 +1092,10 @@ pub fn main(args: [][]u8) -> %void { )SOURCE", "a\nb\n"); - add_simple_case("expose function pointer to C land", R"SOURCE( -#link("c") -export executable "test"; - -c_import { - @c_include("stdlib.h"); -} + add_simple_case_libc("expose function pointer to C land", R"SOURCE( +const c = @c_import(@c_include("stdlib.h")); -export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int { +export fn compare_fn(a: ?&const c.c_void, b: ?&const c.c_void) -> c_int { const a_int = (&i32)(a ?? unreachable{}); const b_int = (&i32)(b ?? unreachable{}); if (*a_int < *b_int) { @@ -1102,11 +1110,11 @@ export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int { export fn main(args: c_int, argv: &&u8) -> c_int { var array = []i32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; - qsort((&c_void)(&array[0]), c_ulong(array.len), @sizeof(i32), compare_fn); + c.qsort((&c.c_void)(&array[0]), c_ulong(array.len), @sizeof(i32), compare_fn); for (array) |item, i| { if (item != i) { - abort(); + c.abort(); } } @@ -1116,37 +1124,33 @@ export fn main(args: c_int, argv: &&u8) -> c_int { - add_simple_case("casting between float and integer types", R"SOURCE( -#link("c") -export executable "test"; -c_import { - @c_include("stdio.h"); -} + add_simple_case_libc("casting between float and integer types", R"SOURCE( +const c = @c_import(@c_include("stdio.h")); export fn main(argc: c_int, argv: &&u8) -> c_int { const small: f32 = 3.25; const x: f64 = small; const y = i32(x); const z = f64(y); - printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4)); + c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4)); return 0; } )SOURCE", "3.25\n3\n3.00\n-0.40\n"); add_simple_case("const expression eval handling of variables", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { var x = true; while (x) { x = false; } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("incomplete struct parameter top level decl", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct A { b: B, } @@ -1159,7 +1163,7 @@ struct C { x: i32, fn d(c: C) { - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } } @@ -1182,7 +1186,7 @@ pub fn main(args: [][]u8) -> %void { add_simple_case("same named methods in incomplete struct", R"SOURCE( -import "std.zig"; +const io = @import("std").io; struct Foo { field1: Bar, @@ -1200,53 +1204,53 @@ pub fn main(args: [][]u8) -> %void { const bar = Bar {.field2 = 13,}; const foo = Foo {.field1 = bar,}; if (!foo.method()) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } if (!bar.method()) { - %%stdout.printf("BAD\n"); + %%io.stdout.printf("BAD\n"); } - %%stdout.printf("OK\n"); + %%io.stdout.printf("OK\n"); } )SOURCE", "OK\n"); add_simple_case("defer with only fallthrough", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("before\n"); - defer %%stdout.printf("defer1\n"); - defer %%stdout.printf("defer2\n"); - defer %%stdout.printf("defer3\n"); - %%stdout.printf("after\n"); + %%io.stdout.printf("before\n"); + defer %%io.stdout.printf("defer1\n"); + defer %%io.stdout.printf("defer2\n"); + defer %%io.stdout.printf("defer3\n"); + %%io.stdout.printf("after\n"); } )SOURCE", "before\nafter\ndefer3\ndefer2\ndefer1\n"); add_simple_case("defer with return", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { - %%stdout.printf("before\n"); - defer %%stdout.printf("defer1\n"); - defer %%stdout.printf("defer2\n"); + %%io.stdout.printf("before\n"); + defer %%io.stdout.printf("defer1\n"); + defer %%io.stdout.printf("defer2\n"); if (args.len == 1) return; - defer %%stdout.printf("defer3\n"); - %%stdout.printf("after\n"); + defer %%io.stdout.printf("defer3\n"); + %%io.stdout.printf("after\n"); } )SOURCE", "before\ndefer2\ndefer1\n"); add_simple_case("%defer and it fails", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { do_test() %% return; } fn do_test() -> %void { - %%stdout.printf("before\n"); - defer %%stdout.printf("defer1\n"); - %defer %%stdout.printf("deferErr\n"); + %%io.stdout.printf("before\n"); + defer %%io.stdout.printf("defer1\n"); + %defer %%io.stdout.printf("deferErr\n"); %return its_gonna_fail(); - defer %%stdout.printf("defer3\n"); - %%stdout.printf("after\n"); + defer %%io.stdout.printf("defer3\n"); + %%io.stdout.printf("after\n"); } error IToldYouItWouldFail; fn its_gonna_fail() -> %void { @@ -1256,17 +1260,17 @@ fn its_gonna_fail() -> %void { add_simple_case("%defer and it passes", R"SOURCE( -import "std.zig"; +const io = @import("std").io; pub fn main(args: [][]u8) -> %void { do_test() %% return; } fn do_test() -> %void { - %%stdout.printf("before\n"); - defer %%stdout.printf("defer1\n"); - %defer %%stdout.printf("deferErr\n"); + %%io.stdout.printf("before\n"); + defer %%io.stdout.printf("defer1\n"); + %defer %%io.stdout.printf("deferErr\n"); %return its_gonna_pass(); - defer %%stdout.printf("defer3\n"); - %%stdout.printf("after\n"); + defer %%io.stdout.printf("defer3\n"); + %%io.stdout.printf("after\n"); } fn its_gonna_pass() -> %void { } )SOURCE", "before\nafter\ndefer3\ndefer1\n"); @@ -1327,14 +1331,9 @@ fn a() { fn b() {} )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); - add_compile_fail_case("bad version string", R"SOURCE( -#version("aoeu") -export executable "test"; - )SOURCE", 1, ".tmp_source.zig:2:1: error: invalid version string"); - add_compile_fail_case("bad import", R"SOURCE( -import "bogus-does-not-exist.zig"; - )SOURCE", 1, ".tmp_source.zig:2:1: error: unable to find 'bogus-does-not-exist.zig'"); +const bogus = @import("bogus-does-not-exist.zig"); + )SOURCE", 1, ".tmp_source.zig:2:15: error: unable to find 'bogus-does-not-exist.zig'"); add_compile_fail_case("undeclared identifier", R"SOURCE( fn a() { @@ -1450,13 +1449,13 @@ fn f() { add_compile_fail_case("direct struct loop", R"SOURCE( struct A { a : A, } - )SOURCE", 1, ".tmp_source.zig:2:1: error: struct has infinite size"); + )SOURCE", 1, ".tmp_source.zig:2:1: error: 'A' depends on itself"); add_compile_fail_case("indirect struct loop", R"SOURCE( struct A { b : B, } struct B { c : C, } struct C { a : A, } - )SOURCE", 1, ".tmp_source.zig:4:1: error: struct has infinite size"); + )SOURCE", 1, ".tmp_source.zig:2:1: error: 'A' depends on itself"); add_compile_fail_case("invalid struct field", R"SOURCE( struct A { x : i32, } @@ -1568,7 +1567,7 @@ fn f() -> @bogus(foo) { add_compile_fail_case("top level decl dependency loop", R"SOURCE( const a : @typeof(b) = 0; const b : @typeof(a) = 0; - )SOURCE", 1, ".tmp_source.zig:3:19: error: use of undeclared identifier 'a'"); + )SOURCE", 1, ".tmp_source.zig:2:1: error: 'a' depends on itself"); add_compile_fail_case("noalias on non pointer param", R"SOURCE( fn f(noalias x: i32) {} @@ -1589,8 +1588,11 @@ struct Bar {} fn f(Foo: i32) { var Bar : i32 = undefined; } - )SOURCE", 2, ".tmp_source.zig:5:6: error: variable shadows type 'Foo'", - ".tmp_source.zig:6:5: error: variable shadows type 'Bar'"); + )SOURCE", 4, + ".tmp_source.zig:5:6: error: redefinition of 'Foo'", + ".tmp_source.zig:2:1: note: previous definition is here", + ".tmp_source.zig:6:5: error: redefinition of 'Bar'", + ".tmp_source.zig:3:1: note: previous definition is here"); add_compile_fail_case("multiple else prongs in a switch", R"SOURCE( fn f(x: u32) { @@ -1614,14 +1616,9 @@ fn f(s: []u8) -> []u8 { )SOURCE", 1, ".tmp_source.zig:3:5: error: string concatenation requires constant expression"); add_compile_fail_case("c_import with bogus include", R"SOURCE( -c_import { - @c_include("bogus.h"); -} - )SOURCE", 2, ".tmp_source.zig:2:1: error: C import failed", - ".h:1:10: error: 'bogus.h' file not found"); - - add_compile_fail_case("empty file", "", - 1, ".tmp_source.zig:1:1: error: missing export declaration and output name not provided"); +const c = @c_import(@c_include("bogus.h")); + )SOURCE", 2, ".tmp_source.zig:2:11: error: C import failed", + ".h:1:10: note: 'bogus.h' file not found"); add_compile_fail_case("address of number literal", R"SOURCE( const x = 3; diff --git a/test/self_hosted.zig b/test/self_hosted.zig index 38338cfaf0..a475dad7ed 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -1,4 +1,5 @@ -import "test_std.zig"; +// test std library +const std = @import("std"); #attribute("test") fn empty_function() {} diff --git a/test/test_std.zig b/test/test_std.zig deleted file mode 100644 index 342d7e15a7..0000000000 --- a/test/test_std.zig +++ /dev/null @@ -1 +0,0 @@ -import "std.zig"; |
