diff options
45 files changed, 1354 insertions, 725 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d8cf0c507d..998da172fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -209,7 +209,7 @@ else() else() set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Wno-comment") if(MINGW) - set(ZIG_LLD_COMPILE_FLAGS "${ZIG_LLD_COMPILE_FLAGS} -D__STDC_FORMAT_MACROS -D__USE_MINGW_ANSI_STDIO -Wno-pedantic-ms-format") + set(ZIG_LLD_COMPILE_FLAGS "${ZIG_LLD_COMPILE_FLAGS} -D__STDC_FORMAT_MACROS -D__USE_MINGW_ANSI_STDIO") endif() endif() set_target_properties(embedded_lld_lib PROPERTIES @@ -511,19 +511,23 @@ set(OPTIMIZED_C_FLAGS "-std=c99 -O3") set(EXE_LDFLAGS " ") if(MSVC) - set(EXE_LDFLAGS "/STACK:16777216") + set(EXE_LDFLAGS "${EXE_LDFLAGS} /STACK:16777216") elseif(MINGW) set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216") endif() if(ZIG_STATIC) if(APPLE) - set(EXE_LDFLAGS "-static-libgcc -static-libstdc++") + set(EXE_LDFLAGS "${EXE_LDFLAGS} -static-libgcc -static-libstdc++") elseif(MINGW) - set(EXE_LDFLAGS "-static-libgcc -static-libstdc++ -Wl,-Bstatic,--whole-archive -lwinpthread -lz3 -lz -lgomp -Wl,--no-whole-archive") - else() - set(EXE_LDFLAGS "-static") + set(EXE_LDFLAGS "${EXE_LDFLAGS} -static-libgcc -static-libstdc++ -Wl,-Bstatic, -lwinpthread -lz3 -lz -lgomp") + elseif(NOT MSVC) + set(EXE_LDFLAGS "${EXE_LDFLAGS} -static") endif() +else() + if(MINGW) + set(EXE_LDFLAGS "${EXE_LDFLAGS} -lz3") + endif() endif() if(ZIG_TEST_COVERAGE) @@ -559,11 +563,6 @@ if(NOT MSVC) target_link_libraries(compiler LINK_PUBLIC ${LIBXML2}) endif() -if(MINGW) - find_library(Z3_LIBRARIES NAMES z3 z3.dll) - target_link_libraries(compiler LINK_PUBLIC ${Z3_LIBRARIES}) -endif() - if(ZIG_DIA_GUIDS_LIB) target_link_libraries(compiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) endif() diff --git a/doc/docgen.zig b/doc/docgen.zig index bff110449c..3d3dcba76d 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -742,101 +742,101 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok const token = tokenizer.next(); try writeEscaped(out, src[index..token.start]); switch (token.id) { - std.zig.Token.Id.Eof => break, - - std.zig.Token.Id.Keyword_align, - std.zig.Token.Id.Keyword_and, - std.zig.Token.Id.Keyword_asm, - std.zig.Token.Id.Keyword_async, - std.zig.Token.Id.Keyword_await, - std.zig.Token.Id.Keyword_break, - std.zig.Token.Id.Keyword_cancel, - std.zig.Token.Id.Keyword_catch, - std.zig.Token.Id.Keyword_comptime, - std.zig.Token.Id.Keyword_const, - std.zig.Token.Id.Keyword_continue, - std.zig.Token.Id.Keyword_defer, - std.zig.Token.Id.Keyword_else, - std.zig.Token.Id.Keyword_enum, - std.zig.Token.Id.Keyword_errdefer, - std.zig.Token.Id.Keyword_error, - std.zig.Token.Id.Keyword_export, - std.zig.Token.Id.Keyword_extern, - std.zig.Token.Id.Keyword_for, - std.zig.Token.Id.Keyword_if, - std.zig.Token.Id.Keyword_inline, - std.zig.Token.Id.Keyword_nakedcc, - std.zig.Token.Id.Keyword_noalias, - std.zig.Token.Id.Keyword_or, - std.zig.Token.Id.Keyword_orelse, - std.zig.Token.Id.Keyword_packed, - std.zig.Token.Id.Keyword_promise, - std.zig.Token.Id.Keyword_pub, - std.zig.Token.Id.Keyword_resume, - std.zig.Token.Id.Keyword_return, - std.zig.Token.Id.Keyword_linksection, - std.zig.Token.Id.Keyword_stdcallcc, - std.zig.Token.Id.Keyword_struct, - std.zig.Token.Id.Keyword_suspend, - std.zig.Token.Id.Keyword_switch, - std.zig.Token.Id.Keyword_test, - std.zig.Token.Id.Keyword_threadlocal, - std.zig.Token.Id.Keyword_try, - std.zig.Token.Id.Keyword_union, - std.zig.Token.Id.Keyword_unreachable, - std.zig.Token.Id.Keyword_usingnamespace, - std.zig.Token.Id.Keyword_var, - std.zig.Token.Id.Keyword_volatile, - std.zig.Token.Id.Keyword_allowzero, - std.zig.Token.Id.Keyword_while, + .Eof => break, + + .Keyword_align, + .Keyword_and, + .Keyword_asm, + .Keyword_async, + .Keyword_await, + .Keyword_break, + .Keyword_cancel, + .Keyword_catch, + .Keyword_comptime, + .Keyword_const, + .Keyword_continue, + .Keyword_defer, + .Keyword_else, + .Keyword_enum, + .Keyword_errdefer, + .Keyword_error, + .Keyword_export, + .Keyword_extern, + .Keyword_for, + .Keyword_if, + .Keyword_inline, + .Keyword_nakedcc, + .Keyword_noalias, + .Keyword_or, + .Keyword_orelse, + .Keyword_packed, + .Keyword_promise, + .Keyword_pub, + .Keyword_resume, + .Keyword_return, + .Keyword_linksection, + .Keyword_stdcallcc, + .Keyword_struct, + .Keyword_suspend, + .Keyword_switch, + .Keyword_test, + .Keyword_threadlocal, + .Keyword_try, + .Keyword_union, + .Keyword_unreachable, + .Keyword_usingnamespace, + .Keyword_var, + .Keyword_volatile, + .Keyword_allowzero, + .Keyword_while, => { try out.write("<span class=\"tok-kw\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); }, - std.zig.Token.Id.Keyword_fn => { + .Keyword_fn => { try out.write("<span class=\"tok-kw\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); next_tok_is_fn = true; }, - std.zig.Token.Id.Keyword_undefined, - std.zig.Token.Id.Keyword_null, - std.zig.Token.Id.Keyword_true, - std.zig.Token.Id.Keyword_false, + .Keyword_undefined, + .Keyword_null, + .Keyword_true, + .Keyword_false, => { try out.write("<span class=\"tok-null\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); }, - std.zig.Token.Id.StringLiteral, - std.zig.Token.Id.MultilineStringLiteralLine, - std.zig.Token.Id.CharLiteral, + .StringLiteral, + .MultilineStringLiteralLine, + .CharLiteral, => { try out.write("<span class=\"tok-str\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); }, - std.zig.Token.Id.Builtin => { + .Builtin => { try out.write("<span class=\"tok-builtin\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); }, - std.zig.Token.Id.LineComment, - std.zig.Token.Id.DocComment, - std.zig.Token.Id.ShebangLine, + .LineComment, + .DocComment, + .ShebangLine, => { try out.write("<span class=\"tok-comment\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); }, - std.zig.Token.Id.Identifier => { + .Identifier => { if (prev_tok_was_fn) { try out.write("<span class=\"tok-fn\">"); try writeEscaped(out, src[token.start..token.end]); @@ -864,72 +864,72 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok } }, - std.zig.Token.Id.IntegerLiteral, - std.zig.Token.Id.FloatLiteral, + .IntegerLiteral, + .FloatLiteral, => { try out.write("<span class=\"tok-number\">"); try writeEscaped(out, src[token.start..token.end]); try out.write("</span>"); }, - std.zig.Token.Id.Bang, - std.zig.Token.Id.Pipe, - std.zig.Token.Id.PipePipe, - std.zig.Token.Id.PipeEqual, - std.zig.Token.Id.Equal, - std.zig.Token.Id.EqualEqual, - std.zig.Token.Id.EqualAngleBracketRight, - std.zig.Token.Id.BangEqual, - std.zig.Token.Id.LParen, - std.zig.Token.Id.RParen, - std.zig.Token.Id.Semicolon, - std.zig.Token.Id.Percent, - std.zig.Token.Id.PercentEqual, - std.zig.Token.Id.LBrace, - std.zig.Token.Id.RBrace, - std.zig.Token.Id.LBracket, - std.zig.Token.Id.RBracket, - std.zig.Token.Id.Period, - std.zig.Token.Id.Ellipsis2, - std.zig.Token.Id.Ellipsis3, - std.zig.Token.Id.Caret, - std.zig.Token.Id.CaretEqual, - std.zig.Token.Id.Plus, - std.zig.Token.Id.PlusPlus, - std.zig.Token.Id.PlusEqual, - std.zig.Token.Id.PlusPercent, - std.zig.Token.Id.PlusPercentEqual, - std.zig.Token.Id.Minus, - std.zig.Token.Id.MinusEqual, - std.zig.Token.Id.MinusPercent, - std.zig.Token.Id.MinusPercentEqual, - std.zig.Token.Id.Asterisk, - std.zig.Token.Id.AsteriskEqual, - std.zig.Token.Id.AsteriskAsterisk, - std.zig.Token.Id.AsteriskPercent, - std.zig.Token.Id.AsteriskPercentEqual, - std.zig.Token.Id.Arrow, - std.zig.Token.Id.Colon, - std.zig.Token.Id.Slash, - std.zig.Token.Id.SlashEqual, - std.zig.Token.Id.Comma, - std.zig.Token.Id.Ampersand, - std.zig.Token.Id.AmpersandEqual, - std.zig.Token.Id.QuestionMark, - std.zig.Token.Id.AngleBracketLeft, - std.zig.Token.Id.AngleBracketLeftEqual, - std.zig.Token.Id.AngleBracketAngleBracketLeft, - std.zig.Token.Id.AngleBracketAngleBracketLeftEqual, - std.zig.Token.Id.AngleBracketRight, - std.zig.Token.Id.AngleBracketRightEqual, - std.zig.Token.Id.AngleBracketAngleBracketRight, - std.zig.Token.Id.AngleBracketAngleBracketRightEqual, - std.zig.Token.Id.Tilde, - std.zig.Token.Id.BracketStarBracket, - std.zig.Token.Id.BracketStarCBracket, + .Bang, + .Pipe, + .PipePipe, + .PipeEqual, + .Equal, + .EqualEqual, + .EqualAngleBracketRight, + .BangEqual, + .LParen, + .RParen, + .Semicolon, + .Percent, + .PercentEqual, + .LBrace, + .RBrace, + .LBracket, + .RBracket, + .Period, + .Ellipsis2, + .Ellipsis3, + .Caret, + .CaretEqual, + .Plus, + .PlusPlus, + .PlusEqual, + .PlusPercent, + .PlusPercentEqual, + .Minus, + .MinusEqual, + .MinusPercent, + .MinusPercentEqual, + .Asterisk, + .AsteriskEqual, + .AsteriskAsterisk, + .AsteriskPercent, + .AsteriskPercentEqual, + .Arrow, + .Colon, + .Slash, + .SlashEqual, + .Comma, + .Ampersand, + .AmpersandEqual, + .QuestionMark, + .AngleBracketLeft, + .AngleBracketLeftEqual, + .AngleBracketAngleBracketLeft, + .AngleBracketAngleBracketLeftEqual, + .AngleBracketRight, + .AngleBracketRightEqual, + .AngleBracketAngleBracketRight, + .AngleBracketAngleBracketRightEqual, + .Tilde, + .BracketStarBracket, + .BracketStarCBracket, => try writeEscaped(out, src[token.start..token.end]), - std.zig.Token.Id.Invalid => return parseError( + .Invalid, .Invalid_ampersands => return parseError( docgen_tokenizer, source_token, "syntax error", diff --git a/doc/langref.html.in b/doc/langref.html.in index 2372d32afa..7b418efef3 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6330,6 +6330,22 @@ comptime { TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe we can remove this restriction </p> + <p> + Supported operations: + </p> + <ul> + <li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.</li> + <li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition. + Also supports {#link|Floats#}.</li> + <li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction. + Also supports {#link|Floats#}.</li> + <li>{#syntax#}.And{#endsyntax#} - bitwise and</li> + <li>{#syntax#}.Nand{#endsyntax#} - bitwise nand</li> + <li>{#syntax#}.Or{#endsyntax#} - bitwise or</li> + <li>{#syntax#}.Xor{#endsyntax#} - bitwise xor</li> + <li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li> + <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li> + </ul> {#header_close#} {#header_open|@bitCast#} <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre> diff --git a/src/all_types.hpp b/src/all_types.hpp index 79ade39ef7..4c3aeade9e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -366,6 +366,7 @@ enum TldId { TldIdFn, TldIdContainer, TldIdCompTime, + TldIdUsingNamespace, }; enum TldResolution { @@ -413,6 +414,12 @@ struct TldCompTime { Tld base; }; +struct TldUsingNamespace { + Tld base; + + ConstExprValue *using_namespace_value; +}; + struct TypeEnumField { Buf *name; BigInt value; @@ -453,7 +460,7 @@ enum NodeType { NodeTypeFieldAccessExpr, NodeTypePtrDeref, NodeTypeUnwrapOptional, - NodeTypeUse, + NodeTypeUsingNamespace, NodeTypeBoolLiteral, NodeTypeNullLiteral, NodeTypeUndefinedLiteral, @@ -715,9 +722,6 @@ struct AstNodeArrayType { struct AstNodeUsingNamespace { VisibMod visib_mod; AstNode *expr; - - TldResolution resolution; - ConstExprValue *using_namespace_value; }; struct AstNodeIfBoolExpr { @@ -1745,8 +1749,6 @@ struct CodeGen { ZigList<Tld *> resolve_queue; size_t resolve_queue_index; - ZigList<AstNode *> use_queue; - size_t use_queue_index; ZigList<TimeEvent> timing_events; ZigList<AstNode *> tld_ref_source_node_stack; ZigList<ZigFn *> inline_fns; @@ -2005,7 +2007,7 @@ struct ScopeDecls { Scope base; HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table; - ZigList<AstNode *> use_decls; + ZigList<TldUsingNamespace *> use_decls; AstNode *safety_set_node; AstNode *fast_math_set_node; ZigType *import; @@ -2541,6 +2543,7 @@ struct IrInstructionLoadPtrGen { struct IrInstructionStorePtr { IrInstruction base; + bool allow_write_through_const; IrInstruction *ptr; IrInstruction *value; }; @@ -3705,6 +3708,7 @@ enum ResultLocId { struct ResultLoc { ResultLocId id; bool written; + bool allow_write_through_const; IrInstruction *resolved_loc; // result ptr IrInstruction *source_instruction; IrInstruction *gen_instruction; // value to store to the result loc diff --git a/src/analyze.cpp b/src/analyze.cpp index 797451c8f8..0af1baec35 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -28,6 +28,8 @@ static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *uni static Error ATTRIBUTE_MUST_USE resolve_union_alignment(CodeGen *g, ZigType *union_type); static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status); +static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope); +static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope); static bool is_top_level_struct(ZigType *import) { return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr; @@ -2854,6 +2856,8 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body")); return; } + } else if (tld->id == TldIdUsingNamespace) { + g->resolve_queue.append(tld); } if (is_export) { g->resolve_queue.append(tld); @@ -2867,7 +2871,7 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { } } - { + if (tld->name != nullptr) { auto entry = decls_scope->decl_table.put_unique(tld->name, tld); if (entry) { Tld *other_tld = entry->value; @@ -2875,9 +2879,7 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition is here")); return; } - } - { ZigType *type; if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(g, tld->source_node, @@ -2977,12 +2979,14 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { break; } - case NodeTypeUse: - { - g->use_queue.append(node); - decls_scope->use_decls.append(node); - break; - } + case NodeTypeUsingNamespace: { + VisibMod visib_mod = node->data.using_namespace.visib_mod; + TldUsingNamespace *tld_using_namespace = allocate<TldUsingNamespace>(1); + init_tld(&tld_using_namespace->base, TldIdUsingNamespace, nullptr, visib_mod, node, &decls_scope->base); + add_top_level_decl(g, decls_scope, &tld_using_namespace->base); + decls_scope->use_decls.append(tld_using_namespace); + break; + } case NodeTypeTestDecl: preview_test_decl(g, node, decls_scope); break; @@ -3266,6 +3270,118 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { g->global_vars.append(tld_var); } +static void add_symbols_from_container(CodeGen *g, TldUsingNamespace *src_using_namespace, + TldUsingNamespace *dst_using_namespace, ScopeDecls* dest_decls_scope) +{ + if (src_using_namespace->base.resolution == TldResolutionUnresolved || + src_using_namespace->base.resolution == TldResolutionResolving) + { + assert(src_using_namespace->base.parent_scope->id == ScopeIdDecls); + ScopeDecls *src_decls_scope = (ScopeDecls *)src_using_namespace->base.parent_scope; + preview_use_decl(g, src_using_namespace, src_decls_scope); + if (src_using_namespace != dst_using_namespace) { + resolve_use_decl(g, src_using_namespace, src_decls_scope); + } + } + + ConstExprValue *use_expr = src_using_namespace->using_namespace_value; + if (type_is_invalid(use_expr->type)) { + dest_decls_scope->any_imports_failed = true; + return; + } + + dst_using_namespace->base.resolution = TldResolutionOk; + + assert(use_expr->special != ConstValSpecialRuntime); + + // The source scope for the imported symbols + ScopeDecls *src_scope = get_container_scope(use_expr->data.x_type); + // The top-level container where the symbols are defined, it's used in the + // loop below in order to exclude the ones coming from an import statement + ZigType *src_import = get_scope_import(&src_scope->base); + assert(src_import != nullptr); + + if (src_scope->any_imports_failed) { + dest_decls_scope->any_imports_failed = true; + } + + auto it = src_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Buf *target_tld_name = entry->key; + Tld *target_tld = entry->value; + + if (target_tld->visib_mod == VisibModPrivate) { + continue; + } + + if (target_tld->import != src_import) { + continue; + } + + auto existing_entry = dest_decls_scope->decl_table.put_unique(target_tld_name, target_tld); + if (existing_entry) { + Tld *existing_decl = existing_entry->value; + if (existing_decl != target_tld) { + ErrorMsg *msg = add_node_error(g, dst_using_namespace->base.source_node, + buf_sprintf("import of '%s' overrides existing definition", + buf_ptr(target_tld_name))); + add_error_note(g, msg, existing_decl->source_node, buf_sprintf("previous definition here")); + add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here")); + } + } + } + + for (size_t i = 0; i < src_scope->use_decls.length; i += 1) { + TldUsingNamespace *tld_using_namespace = src_scope->use_decls.at(i); + if (tld_using_namespace->base.visib_mod != VisibModPrivate) + add_symbols_from_container(g, tld_using_namespace, dst_using_namespace, dest_decls_scope); + } +} + +static void resolve_use_decl(CodeGen *g, TldUsingNamespace *tld_using_namespace, ScopeDecls *dest_decls_scope) { + if (tld_using_namespace->base.resolution == TldResolutionOk || + tld_using_namespace->base.resolution == TldResolutionInvalid) + { + return; + } + add_symbols_from_container(g, tld_using_namespace, tld_using_namespace, dest_decls_scope); +} + +static void preview_use_decl(CodeGen *g, TldUsingNamespace *using_namespace, ScopeDecls *dest_decls_scope) { + if (using_namespace->base.resolution == TldResolutionOk || + using_namespace->base.resolution == TldResolutionInvalid || + using_namespace->using_namespace_value != nullptr) + { + return; + } + + using_namespace->base.resolution = TldResolutionResolving; + assert(using_namespace->base.source_node->type == NodeTypeUsingNamespace); + ConstExprValue *result = analyze_const_value(g, &dest_decls_scope->base, + using_namespace->base.source_node->data.using_namespace.expr, g->builtin_types.entry_type, nullptr); + using_namespace->using_namespace_value = result; + + if (type_is_invalid(result->type)) { + dest_decls_scope->any_imports_failed = true; + using_namespace->base.resolution = TldResolutionInvalid; + using_namespace->using_namespace_value = &g->invalid_instruction->value; + return; + } + + if (!is_container(result->data.x_type)) { + add_node_error(g, using_namespace->base.source_node, + buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&result->data.x_type->name))); + dest_decls_scope->any_imports_failed = true; + using_namespace->base.resolution = TldResolutionInvalid; + using_namespace->using_namespace_value = &g->invalid_instruction->value; + return; + } +} + void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) { if (tld->resolution != TldResolutionUnresolved) return; @@ -3299,6 +3415,14 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) { resolve_decl_comptime(g, tld_comptime); break; } + case TldIdUsingNamespace: { + TldUsingNamespace *tld_using_namespace = (TldUsingNamespace *)tld; + assert(tld_using_namespace->base.parent_scope->id == ScopeIdDecls); + ScopeDecls *dest_decls_scope = (ScopeDecls *)tld_using_namespace->base.parent_scope; + preview_use_decl(g, tld_using_namespace, dest_decls_scope); + resolve_use_decl(g, tld_using_namespace, dest_decls_scope); + break; + } } tld->resolution = TldResolutionOk; @@ -3308,10 +3432,10 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) { Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) { // resolve all the using_namespace decls for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) { - AstNode *use_decl_node = decls_scope->use_decls.at(i); - if (use_decl_node->data.using_namespace.resolution == TldResolutionUnresolved) { - preview_use_decl(g, use_decl_node, decls_scope); - resolve_use_decl(g, use_decl_node, decls_scope); + TldUsingNamespace *tld_using_namespace = decls_scope->use_decls.at(i); + if (tld_using_namespace->base.resolution == TldResolutionUnresolved) { + preview_use_decl(g, tld_using_namespace, decls_scope); + resolve_use_decl(g, tld_using_namespace, decls_scope); } } @@ -3752,110 +3876,6 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { analyze_fn_ir(g, fn_table_entry, return_type_node); } -static void add_symbols_from_container(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node, ScopeDecls* decls_scope) { - if (src_use_node->data.using_namespace.resolution == TldResolutionUnresolved) { - preview_use_decl(g, src_use_node, decls_scope); - } - - ConstExprValue *use_expr = src_use_node->data.using_namespace.using_namespace_value; - if (type_is_invalid(use_expr->type)) { - decls_scope->any_imports_failed = true; - return; - } - - dst_use_node->data.using_namespace.resolution = TldResolutionOk; - - assert(use_expr->special != ConstValSpecialRuntime); - - // The source struct for the imported symbols - ZigType *src_ty = use_expr->data.x_type; - assert(src_ty); - - if (!is_container(src_ty)) { - add_node_error(g, dst_use_node, - buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&src_ty->name))); - decls_scope->any_imports_failed = true; - return; - } - - // The source scope for the imported symbols - ScopeDecls *src_scope = get_container_scope(src_ty); - // The top-level container where the symbols are defined, it's used in the - // loop below in order to exclude the ones coming from an import statement - ZigType *src_import = get_scope_import(&src_scope->base); - assert(src_import != nullptr); - - if (src_scope->any_imports_failed) { - decls_scope->any_imports_failed = true; - } - - auto it = src_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Buf *target_tld_name = entry->key; - Tld *target_tld = entry->value; - - if (target_tld->visib_mod == VisibModPrivate) { - continue; - } - - if (target_tld->import != src_import) { - continue; - } - - auto existing_entry = decls_scope->decl_table.put_unique(target_tld_name, target_tld); - if (existing_entry) { - Tld *existing_decl = existing_entry->value; - if (existing_decl != target_tld) { - 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->source_node, buf_sprintf("previous definition here")); - add_error_note(g, msg, target_tld->source_node, buf_sprintf("imported definition here")); - } - } - } - - for (size_t i = 0; i < src_scope->use_decls.length; i += 1) { - AstNode *use_decl_node = src_scope->use_decls.at(i); - if (use_decl_node->data.using_namespace.visib_mod != VisibModPrivate) - add_symbols_from_container(g, use_decl_node, dst_use_node, decls_scope); - } -} - -void resolve_use_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { - assert(node->type == NodeTypeUse); - - if (node->data.using_namespace.resolution == TldResolutionOk || - node->data.using_namespace.resolution == TldResolutionInvalid) - { - return; - } - add_symbols_from_container(g, node, node, decls_scope); -} - -void preview_use_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { - assert(node->type == NodeTypeUse); - - if (node->data.using_namespace.resolution == TldResolutionOk || - node->data.using_namespace.resolution == TldResolutionInvalid) - { - return; - } - - node->data.using_namespace.resolution = TldResolutionResolving; - ConstExprValue *result = analyze_const_value(g, &decls_scope->base, - node->data.using_namespace.expr, g->builtin_types.entry_type, nullptr); - - if (type_is_invalid(result->type)) - decls_scope->any_imports_failed = true; - - node->data.using_namespace.using_namespace_value = result; -} - ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code, SourceKind source_kind) { @@ -3975,18 +3995,8 @@ ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Bu void semantic_analyze(CodeGen *g) { while (g->resolve_queue_index < g->resolve_queue.length || - g->fn_defs_index < g->fn_defs.length || - g->use_queue_index < g->use_queue.length) + g->fn_defs_index < g->fn_defs.length) { - 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); - // Get the top-level scope where `using_namespace` is used - ScopeDecls *decls_scope = get_container_scope(use_decl_node->owner); - if (use_decl_node->data.using_namespace.resolution == TldResolutionUnresolved) { - preview_use_decl(g, use_decl_node, decls_scope); - resolve_use_decl(g, use_decl_node, decls_scope); - } - } for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { Tld *tld = g->resolve_queue.at(g->resolve_queue_index); AstNode *source_node = nullptr; diff --git a/src/analyze.hpp b/src/analyze.hpp index a6ad92110e..b9e9f2df7d 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -86,8 +86,6 @@ bool is_array_ref(ZigType *type_entry); bool is_container_ref(ZigType *type_entry); bool is_valid_vector_elem_type(ZigType *elem_type); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); -void preview_use_decl(CodeGen *g, AstNode *node, ScopeDecls* decls_scope); -void resolve_use_decl(CodeGen *g, AstNode *node, ScopeDecls* decls_scope); ZigFn *scope_fn_entry(Scope *scope); ZigPackage *scope_package(Scope *scope); ZigType *get_scope_import(Scope *scope); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 154803f884..af134d29b5 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -193,8 +193,8 @@ static const char *node_type_str(NodeType node_type) { return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; - case NodeTypeUse: - return "Use"; + case NodeTypeUsingNamespace: + return "UsingNamespace"; case NodeTypeBoolLiteral: return "BoolLiteral"; case NodeTypeNullLiteral: @@ -319,6 +319,9 @@ static bool is_digit(uint8_t c) { } static bool is_printable(uint8_t c) { + if (c == 0) { + return false; + } static const uint8_t printables[] = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.~`!@#$%^&*()_-+=\\{}[];'\"?/<>,:"; for (size_t i = 0; i < array_length(printables); i += 1) { @@ -337,20 +340,12 @@ static void string_literal_escape(Buf *source, Buf *dest) { buf_append_str(dest, "\\\""); } else if (c == '\\') { buf_append_str(dest, "\\\\"); - } else if (c == '\a') { - buf_append_str(dest, "\\a"); - } else if (c == '\b') { - buf_append_str(dest, "\\b"); - } else if (c == '\f') { - buf_append_str(dest, "\\f"); } else if (c == '\n') { buf_append_str(dest, "\\n"); } else if (c == '\r') { buf_append_str(dest, "\\r"); } else if (c == '\t') { buf_append_str(dest, "\\t"); - } else if (c == '\v') { - buf_append_str(dest, "\\v"); } else if (is_printable(c)) { buf_append_char(dest, c); } else { @@ -630,7 +625,19 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeCharLiteral: { uint8_t c = node->data.char_literal.value; - if (is_printable(c)) { + if (c == '\'') { + fprintf(ar->f, "'\\''"); + } else if (c == '\"') { + fprintf(ar->f, "'\\\"'"); + } else if (c == '\\') { + fprintf(ar->f, "'\\\\'"); + } else if (c == '\n') { + fprintf(ar->f, "'\\n'"); + } else if (c == '\r') { + fprintf(ar->f, "'\\r'"); + } else if (c == '\t') { + fprintf(ar->f, "'\\t'"); + } else if (is_printable(c)) { fprintf(ar->f, "'%c'", c); } else { fprintf(ar->f, "'\\x%02x'", (int)c); @@ -791,7 +798,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { AstNode *decls_node = node->data.container_decl.decls.at(decl_i); render_node_grouped(ar, decls_node); - if (decls_node->type == NodeTypeUse || + if (decls_node->type == NodeTypeUsingNamespace || decls_node->type == NodeTypeVariableDeclaration || decls_node->type == NodeTypeFnProto) { @@ -1170,7 +1177,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeParamDecl: case NodeTypeTestDecl: case NodeTypeStructField: - case NodeTypeUse: + case NodeTypeUsingNamespace: zig_panic("TODO more ast rendering"); } } diff --git a/src/codegen.cpp b/src/codegen.cpp index 2170da10ac..49aeb8fb87 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -89,126 +89,6 @@ static const char *symbols_that_llvm_depends_on[] = { // TODO probably all of compiler-rt needs to go here }; -CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, - OutType out_type, BuildMode build_mode, Buf *override_lib_dir, Buf *override_std_dir, - ZigLibCInstallation *libc, Buf *cache_dir) -{ - CodeGen *g = allocate<CodeGen>(1); - - codegen_add_time_event(g, "Initialize"); - - g->subsystem = TargetSubsystemAuto; - g->libc = libc; - g->zig_target = target; - g->cache_dir = cache_dir; - - if (override_lib_dir == nullptr) { - g->zig_lib_dir = get_zig_lib_dir(); - } else { - g->zig_lib_dir = override_lib_dir; - } - - if (override_std_dir == nullptr) { - g->zig_std_dir = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); - } else { - g->zig_std_dir = override_std_dir; - } - - g->zig_c_headers_dir = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); - - g->build_mode = build_mode; - g->out_type = out_type; - g->import_table.init(32); - g->builtin_fn_table.init(32); - g->primitive_type_table.init(32); - g->type_table.init(32); - g->fn_type_table.init(32); - g->error_table.init(16); - g->generic_table.init(16); - g->llvm_fn_table.init(16); - g->memoized_fn_eval_table.init(16); - g->exported_symbol_names.init(8); - g->external_prototypes.init(8); - g->string_literals_table.init(16); - g->type_info_cache.init(32); - g->is_test_build = false; - g->is_single_threaded = false; - buf_resize(&g->global_asm, 0); - - for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { - g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); - } - - if (root_src_path) { - Buf *root_pkg_path; - Buf *rel_root_src_path; - if (main_pkg_path == nullptr) { - Buf *src_basename = buf_alloc(); - Buf *src_dir = buf_alloc(); - os_path_split(root_src_path, src_dir, src_basename); - - if (buf_len(src_basename) == 0) { - fprintf(stderr, "Invalid root source path: %s\n", buf_ptr(root_src_path)); - exit(1); - } - root_pkg_path = src_dir; - rel_root_src_path = src_basename; - } else { - Buf resolved_root_src_path = os_path_resolve(&root_src_path, 1); - Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1); - - if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) { - fprintf(stderr, "Root source path '%s' outside main package path '%s'", - buf_ptr(root_src_path), buf_ptr(main_pkg_path)); - exit(1); - } - root_pkg_path = main_pkg_path; - rel_root_src_path = buf_create_from_mem( - buf_ptr(&resolved_root_src_path) + buf_len(&resolved_main_pkg_path) + 1, - buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1); - } - - g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); - g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); - g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); - } else { - g->root_package = new_package(".", "", ""); - } - - g->root_package->package_table.put(buf_create_from_str("root"), g->root_package); - - g->zig_std_special_dir = buf_alloc(); - os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); - - assert(target != nullptr); - if (!target->is_native) { - g->each_lib_rpath = false; - } else { - g->each_lib_rpath = true; - - if (target_os_is_darwin(g->zig_target->os)) { - init_darwin_native(g); - } - - } - - if (target_os_requires_libc(g->zig_target->os)) { - g->libc_link_lib = create_link_lib(buf_create_from_str("c")); - g->link_libs_list.append(g->libc_link_lib); - } - - target_triple_llvm(&g->llvm_triple_str, g->zig_target); - g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8; - - if (!target_has_debug_info(g->zig_target)) { - g->strip_debug_symbols = true; - } - - return g; -} - void codegen_set_clang_argv(CodeGen *g, const char **args, size_t len) { g->clang_argv = args; g->clang_argv_len = len; @@ -233,10 +113,6 @@ void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patc g->version_patch = patch; } -void codegen_set_is_test(CodeGen *g, bool is_test_build) { - g->is_test_build = is_test_build; -} - void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type) { g->emit_file_type = emit_file_type; } @@ -4582,8 +4458,14 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(get_llvm_type(g, child_type)), payload_val, ""); } + // When the cmpxchg is discarded, the result location will have no bits. + if (!type_has_bits(instruction->result_loc->value.type)) { + return nullptr; + } + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - assert(type_has_bits(child_type)); + src_assert(result_loc != nullptr, instruction->base.source_node); + src_assert(type_has_bits(child_type), instruction->base.source_node); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); @@ -5486,7 +5368,8 @@ static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executab LLVMValueRef vector = ir_llvm_value(g, instruction->vector); LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, result_loc, LLVMPointerType(get_llvm_type(g, instruction->vector->value.type), 0), ""); - gen_store_untyped(g, vector, casted_ptr, get_ptr_align(g, instruction->result_loc->value.type), false); + uint32_t alignment = get_ptr_align(g, instruction->result_loc->value.type); + gen_store_untyped(g, vector, casted_ptr, alignment, false); return result_loc; } @@ -5499,7 +5382,10 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, LLVMPointerType(get_llvm_type(g, vector_type), 0), ""); - return gen_load_untyped(g, casted_ptr, 0, false, ""); + ZigType *array_type = instruction->array->value.type; + assert(array_type->id == ZigTypeIdArray); + uint32_t alignment = get_abi_alignment(g, array_type->data.array.child_type); + return gen_load_untyped(g, casted_ptr, alignment, false, ""); } static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable, @@ -7994,6 +7880,14 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { return contents; } +static ZigPackage *create_test_runner_pkg(CodeGen *g) { + return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); +} + +static ZigPackage *create_panic_pkg(CodeGen *g) { + return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "panic.zig", "std.special"); +} + static Error define_builtin_compile_vars(CodeGen *g) { if (g->std_package == nullptr) return ErrorNone; @@ -8078,8 +7972,16 @@ static Error define_builtin_compile_vars(CodeGen *g) { g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); - g->std_package->package_table.put(buf_create_from_str("root"), - g->is_test_build ? g->test_runner_package : g->root_package); + ZigPackage *root_pkg; + if (g->is_test_build) { + if (g->test_runner_package == nullptr) { + g->test_runner_package = create_test_runner_pkg(g); + } + root_pkg = g->test_runner_package; + } else { + root_pkg = g->root_package; + } + g->std_package->package_table.put(buf_create_from_str("root"), root_pkg); g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents, SourceKindPkgMain); @@ -8586,14 +8488,6 @@ static ZigPackage *create_start_pkg(CodeGen *g, ZigPackage *pkg_with_main) { return package; } -static ZigPackage *create_test_runner_pkg(CodeGen *g) { - return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); -} - -static ZigPackage *create_panic_pkg(CodeGen *g) { - return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "panic.zig", "std.special"); -} - static void create_test_compile_var_and_add_test_runner(CodeGen *g) { Error err; @@ -8643,7 +8537,7 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { ConstExprValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true); update_compile_var(g, buf_create_from_str("test_functions"), test_fn_slice); - g->test_runner_package = create_test_runner_pkg(g); + assert(g->test_runner_package != nullptr); g->test_runner_import = add_special_code(g, g->test_runner_package, "test_runner.zig"); } @@ -9757,7 +9651,8 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o ZigLibCInstallation *libc) { CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, libc, get_stage1_cache_path()); + parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, libc, get_stage1_cache_path(), + false); child_gen->disable_gen_h = true; child_gen->want_stack_check = WantStackCheckDisabled; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; @@ -9784,3 +9679,123 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o return child_gen; } +CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, + OutType out_type, BuildMode build_mode, Buf *override_lib_dir, Buf *override_std_dir, + ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build) +{ + CodeGen *g = allocate<CodeGen>(1); + + codegen_add_time_event(g, "Initialize"); + + g->subsystem = TargetSubsystemAuto; + g->libc = libc; + g->zig_target = target; + g->cache_dir = cache_dir; + + if (override_lib_dir == nullptr) { + g->zig_lib_dir = get_zig_lib_dir(); + } else { + g->zig_lib_dir = override_lib_dir; + } + + if (override_std_dir == nullptr) { + g->zig_std_dir = buf_alloc(); + os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); + } else { + g->zig_std_dir = override_std_dir; + } + + g->zig_c_headers_dir = buf_alloc(); + os_path_join(g->zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); + + g->build_mode = build_mode; + g->out_type = out_type; + g->import_table.init(32); + g->builtin_fn_table.init(32); + g->primitive_type_table.init(32); + g->type_table.init(32); + g->fn_type_table.init(32); + g->error_table.init(16); + g->generic_table.init(16); + g->llvm_fn_table.init(16); + g->memoized_fn_eval_table.init(16); + g->exported_symbol_names.init(8); + g->external_prototypes.init(8); + g->string_literals_table.init(16); + g->type_info_cache.init(32); + g->is_test_build = is_test_build; + g->is_single_threaded = false; + buf_resize(&g->global_asm, 0); + + for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { + g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); + } + + if (root_src_path) { + Buf *root_pkg_path; + Buf *rel_root_src_path; + if (main_pkg_path == nullptr) { + Buf *src_basename = buf_alloc(); + Buf *src_dir = buf_alloc(); + os_path_split(root_src_path, src_dir, src_basename); + + if (buf_len(src_basename) == 0) { + fprintf(stderr, "Invalid root source path: %s\n", buf_ptr(root_src_path)); + exit(1); + } + root_pkg_path = src_dir; + rel_root_src_path = src_basename; + } else { + Buf resolved_root_src_path = os_path_resolve(&root_src_path, 1); + Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1); + + if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) { + fprintf(stderr, "Root source path '%s' outside main package path '%s'", + buf_ptr(root_src_path), buf_ptr(main_pkg_path)); + exit(1); + } + root_pkg_path = main_pkg_path; + rel_root_src_path = buf_create_from_mem( + buf_ptr(&resolved_root_src_path) + buf_len(&resolved_main_pkg_path) + 1, + buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1); + } + + g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); + g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); + g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); + } else { + g->root_package = new_package(".", "", ""); + } + + g->root_package->package_table.put(buf_create_from_str("root"), g->root_package); + + g->zig_std_special_dir = buf_alloc(); + os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); + + assert(target != nullptr); + if (!target->is_native) { + g->each_lib_rpath = false; + } else { + g->each_lib_rpath = true; + + if (target_os_is_darwin(g->zig_target->os)) { + init_darwin_native(g); + } + + } + + if (target_os_requires_libc(g->zig_target->os)) { + g->libc_link_lib = create_link_lib(buf_create_from_str("c")); + g->link_libs_list.append(g->libc_link_lib); + } + + target_triple_llvm(&g->llvm_triple_str, g->zig_target); + g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8; + + if (!target_has_debug_info(g->zig_target)) { + g->strip_debug_symbols = true; + } + + return g; +} + diff --git a/src/codegen.hpp b/src/codegen.hpp index 5de36c1aab..cdff61a26f 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -18,14 +18,13 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, Buf *override_std_dir, - ZigLibCInstallation *libc, Buf *cache_dir); + ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build); CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, ZigLibCInstallation *libc); void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); -void codegen_set_is_test(CodeGen *codegen, bool is_test); void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath); void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type); diff --git a/src/ir.cpp b/src/ir.cpp index 5193a63ec4..65a21a418d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -189,7 +189,8 @@ static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr, ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime); static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime); + ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, + bool non_null_comptime, bool allow_discard); static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool safety_check_on, bool initializing); static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruction *source_instr, @@ -197,7 +198,7 @@ static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *base_ptr, bool initializing); static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *ptr, IrInstruction *uncasted_value); + IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const); static IrInstruction *ir_gen_union_init_expr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *union_type, IrInstruction *field_name, AstNode *expr_node, LVal lval, ResultLoc *parent_result_loc); @@ -1612,7 +1613,7 @@ static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode return &unreachable_instruction->base; } -static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstructionStorePtr *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, IrInstruction *value) { IrInstructionStorePtr *instruction = ir_build_instruction<IrInstructionStorePtr>(irb, scope, source_node); @@ -1624,7 +1625,7 @@ static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode * ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(value, irb->current_basic_block); - return &instruction->base; + return instruction; } static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, @@ -4001,12 +4002,20 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) { IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr); - IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + if (lvalue == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1); + result_loc_inst->base.id = ResultLocIdInstruction; + result_loc_inst->base.source_instruction = lvalue; + ir_ref_instruction(lvalue, irb->current_basic_block); + ir_build_reset_result(irb, scope, node, &result_loc_inst->base); - if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction) + IrInstruction *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone, + &result_loc_inst->base); + if (rvalue == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - ir_build_store_ptr(irb, scope, node, lvalue, rvalue); return ir_build_const_void(irb, scope, node); } @@ -6042,6 +6051,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = field_ptr; + result_loc_inst->base.allow_write_through_const = true; ir_ref_instruction(field_ptr, irb->current_basic_block); ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); @@ -6080,6 +6090,7 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1); result_loc_inst->base.id = ResultLocIdInstruction; result_loc_inst->base.source_instruction = elem_ptr; + result_loc_inst->base.allow_write_through_const = true; ir_ref_instruction(elem_ptr, irb->current_basic_block); ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); @@ -6637,7 +6648,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo ir_set_cursor_at_end_and_append_block(irb, continue_block); IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); - ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)); + ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true; ir_build_br(irb, child_scope, node, cond_block, is_comptime); IrInstruction *else_result = nullptr; @@ -8430,7 +8441,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop switch (node->type) { case NodeTypeStructValueField: case NodeTypeParamDecl: - case NodeTypeUse: + case NodeTypeUsingNamespace: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeStructField: @@ -11155,7 +11166,8 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc } if (result_loc == nullptr) result_loc = no_result_loc(); - IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false); + IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, + false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -11615,7 +11627,7 @@ static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *so } IrInstruction *result_loc_inst = nullptr; if (result_loc != nullptr) { - result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false); + result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -11658,7 +11670,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction IrInstruction *result_loc_inst; if (handle_is_ptr(wanted_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); - result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false); + result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -11743,7 +11755,7 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so IrInstruction *result_loc_inst; if (handle_is_ptr(wanted_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); - result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false); + result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -11816,7 +11828,8 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi IrInstruction *result_loc; if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) { - result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), value->value.type, nullptr, true, false); + result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), value->value.type, nullptr, true, + false, true); } else { result_loc = nullptr; } @@ -11860,7 +11873,8 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); if (result_loc == nullptr) result_loc = no_result_loc(); - IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false); + IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, + true, false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -12516,7 +12530,8 @@ static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction * if (result_loc == nullptr) { result_loc = no_result_loc(); } - IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr, true, false); + IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr, + true, false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -13097,7 +13112,8 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc IrInstruction *result_loc_inst; if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) { if (result_loc == nullptr) result_loc = no_result_loc(); - result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr, true, false); + result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr, + true, false, true); if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) { return result_loc_inst; } @@ -14597,7 +14613,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * for (uint64_t x = 0; x < mult_amt; x += 1) { for (uint64_t y = 0; y < old_array_len; y += 1) { copy_const_val(&out_val->data.x_array.data.s_none.elements[i], - &array_val->data.x_array.data.s_none.elements[y], true); + &array_val->data.x_array.data.s_none.elements[y], false); i += 1; } } @@ -14834,7 +14850,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, // instruction. assert(deref->value.special != ConstValSpecialRuntime); var_ptr->value.special = ConstValSpecialRuntime; - ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref); + ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref, false); } if (var_ptr->value.special == ConstValSpecialStatic && var->mem_slot_index != SIZE_MAX) { @@ -15352,7 +15368,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe if (peer_parent->peers.length == 1) { IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - value_type, value, force_runtime, non_null_comptime); + value_type, value, force_runtime, non_null_comptime, true); result_peer->suspend_pos.basic_block_index = SIZE_MAX; result_peer->suspend_pos.instruction_index = SIZE_MAX; if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) || @@ -15372,7 +15388,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe if (peer_parent->skipped) { if (non_null_comptime) { return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - value_type, value, force_runtime, non_null_comptime); + value_type, value, force_runtime, non_null_comptime, true); } return nullptr; } @@ -15390,7 +15406,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe } IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - peer_parent->resolved_type, nullptr, force_runtime, non_null_comptime); + peer_parent->resolved_type, nullptr, force_runtime, non_null_comptime, true); if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) || parent_result_loc->value.type->id == ZigTypeIdUnreachable) { @@ -15440,7 +15456,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe } IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent, - dest_type, bitcasted_value, force_runtime, non_null_comptime); + dest_type, bitcasted_value, force_runtime, non_null_comptime, true); if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) || parent_result_loc->value.type->id == ZigTypeIdUnreachable) { @@ -15469,8 +15485,15 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr, ResultLoc *result_loc_pass1, ZigType *value_type, IrInstruction *value, bool force_runtime, - bool non_null_comptime) + bool non_null_comptime, bool allow_discard) { + if (!allow_discard && result_loc_pass1->id == ResultLocIdInstruction && + instr_is_comptime(result_loc_pass1->source_instruction) && + result_loc_pass1->source_instruction->value.type->id == ZigTypeIdPointer && + result_loc_pass1->source_instruction->value.data.x_ptr.special == ConstPtrSpecialDiscard) + { + result_loc_pass1 = no_result_loc(); + } IrInstruction *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type, value, force_runtime, non_null_comptime); if (result_loc == nullptr || (instr_is_unreachable(result_loc) || type_is_invalid(result_loc->value.type))) @@ -15525,7 +15548,7 @@ static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrIn if (type_is_invalid(implicit_elem_type)) return ira->codegen->invalid_instruction; IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - implicit_elem_type, nullptr, false, true); + implicit_elem_type, nullptr, false, true, true); if (result_loc != nullptr) return result_loc; @@ -15534,7 +15557,7 @@ static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrIn instruction->result_loc->id == ResultLocIdReturn) { result_loc = ir_resolve_result(ira, &instruction->base, no_result_loc(), - implicit_elem_type, nullptr, false, true); + implicit_elem_type, nullptr, false, true, true); if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) { @@ -15623,7 +15646,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type); IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, no_result_loc(), - async_return_type, nullptr, true, true); + async_return_type, nullptr, true, true, false); if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { return result_loc; } @@ -15841,7 +15864,7 @@ no_mem_slot: } static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, - IrInstruction *ptr, IrInstruction *uncasted_value) + IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const) { assert(ptr->value.type->id == ZigTypeIdPointer); @@ -15857,7 +15880,7 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source ZigType *child_type = ptr->value.type->data.pointer.child_type; - if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + if (ptr->value.type->data.pointer.is_const && !allow_write_through_const) { ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); return ira->codegen->invalid_instruction; } @@ -15936,10 +15959,9 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source break; } - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, - ptr, value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, ptr, value); + return &store_ptr->base; } static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, @@ -16382,7 +16404,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstruction *result_loc; if (handle_is_ptr(impl_fn_type_id->return_type)) { result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, - impl_fn_type_id->return_type, nullptr, true, true); + impl_fn_type_id->return_type, nullptr, true, true, false); if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) { @@ -16504,7 +16526,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstruction *result_loc; if (handle_is_ptr(return_type)) { result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, - return_type, nullptr, true, true); + return_type, nullptr, true, true, false); if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) { return result_loc; } @@ -17020,7 +17042,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh // In case resolving the parent activates a suspend, do it now IrInstruction *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base, peer_parent->parent, - peer_parent->resolved_type, nullptr, false, false); + peer_parent->resolved_type, nullptr, false, false, true); if (parent_result_loc != nullptr && (type_is_invalid(parent_result_loc->value.type) || instr_is_unreachable(parent_result_loc))) { @@ -17477,6 +17499,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct return result; } else if (is_slice(array_type)) { ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index]; + ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base); if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope, elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, false, @@ -17663,7 +17686,7 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; if (type_is_invalid(struct_val->type)) return ira->codegen->invalid_instruction; - if (struct_val->special == ConstValSpecialUndef && initializing) { + if (initializing && struct_val->special == ConstValSpecialUndef) { struct_val->data.x_struct.fields = create_const_vals(struct_type->data.structure.src_field_count); struct_val->special = ConstValSpecialStatic; for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { @@ -17847,6 +17870,7 @@ static IrInstruction *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_ switch (tld->id) { case TldIdContainer: case TldIdCompTime: + case TldIdUsingNamespace: zig_unreachable(); case TldIdVar: { @@ -18260,7 +18284,7 @@ static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstruc if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value, instruction->allow_write_through_const); } static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { @@ -18763,7 +18787,7 @@ static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstr if (optional_val == nullptr) return ira->codegen->invalid_instruction; - if (initializing && optional_val->special == ConstValSpecialUndef) { + if (initializing) { switch (type_has_one_possible_value(ira->codegen, child_type)) { case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; @@ -19668,7 +19692,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, instruction, field, result_loc, container_type, true); - ir_analyze_store_ptr(ira, instruction, field_ptr, runtime_inst); + ir_analyze_store_ptr(ira, instruction, field_ptr, runtime_inst, false); if (instr_is_comptime(field_ptr) && field_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) { const_ptrs.append(field_ptr); } else { @@ -19685,7 +19709,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstruction *field_result_loc = const_ptrs.at(i); IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc, nullptr); field_result_loc->value.special = ConstValSpecialRuntime; - ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref); + ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref, false); } } } @@ -19812,7 +19836,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, assert(elem_result_loc->value.special == ConstValSpecialStatic); IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc, nullptr); elem_result_loc->value.special = ConstValSpecialRuntime; - ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref); + ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref, false); } } } @@ -21531,7 +21555,7 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi IrInstruction *result_loc; if (handle_is_ptr(result_type)) { result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - result_type, nullptr, true, false); + result_type, nullptr, true, false, true); if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { return result_loc; } @@ -21788,7 +21812,7 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru } IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - dest_slice_type, nullptr, true, false); + dest_slice_type, nullptr, true, false, true); if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) { return result_loc; } @@ -21865,7 +21889,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct } IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - dest_slice_type, nullptr, true, false); + dest_slice_type, nullptr, true, false, true); if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { return result_loc; } @@ -22607,7 +22631,7 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction } IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - return_type, nullptr, true, false); + return_type, nullptr, true, false, true); if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { return result_loc; } @@ -23259,7 +23283,7 @@ static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruct ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); if (err_union_val == nullptr) return ira->codegen->invalid_instruction; - if (err_union_val->special == ConstValSpecialUndef && initializing) { + if (initializing && err_union_val->special == ConstValSpecialUndef) { ConstExprValue *vals = create_const_vals(2); ConstExprValue *err_set_val = &vals[0]; ConstExprValue *payload_val = &vals[1]; @@ -24734,10 +24758,11 @@ static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } - if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) { + uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); + if (operand_type->data.integral.bit_count > max_atomic_bits) { ir_add_error(ira, op, - buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type", - operand_type->data.integral.bit_count)); + buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type", + max_atomic_bits, operand_type->data.integral.bit_count)); return ira->codegen->builtin_types.entry_invalid; } if (!is_power_of_2(operand_type->data.integral.bit_count)) { @@ -25386,7 +25411,7 @@ static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstruct bool was_written = instruction->result_loc->written; IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc, - value->value.type, value, false, false); + value->value.type, value, false, false, true); if (result_loc != nullptr) { if (type_is_invalid(result_loc->value.type)) return ira->codegen->invalid_instruction; @@ -25394,7 +25419,8 @@ static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstruct return result_loc; if (!was_written) { - IrInstruction *store_ptr = ir_analyze_store_ptr(ira, &instruction->base, result_loc, value); + IrInstruction *store_ptr = ir_analyze_store_ptr(ira, &instruction->base, result_loc, value, + instruction->result_loc->allow_write_through_const); if (type_is_invalid(store_ptr->value.type)) { return ira->codegen->invalid_instruction; } @@ -25418,7 +25444,7 @@ static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInst return operand; IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, - &instruction->result_loc_bit_cast->base, operand->value.type, operand, false, false); + &instruction->result_loc_bit_cast->base, operand->value.type, operand, false, false, true); if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) return result_loc; diff --git a/src/main.cpp b/src/main.cpp index ce68e53d85..42d0850046 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -583,7 +583,7 @@ int main(int argc, char **argv) { } CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe, - BuildModeDebug, override_lib_dir, override_std_dir, nullptr, &full_cache_dir); + BuildModeDebug, override_lib_dir, override_std_dir, nullptr, &full_cache_dir, false); g->valgrind_support = valgrind_support; g->enable_time_report = timing_info; codegen_set_out_name(g, buf_create_from_str("build")); @@ -1011,7 +1011,7 @@ int main(int argc, char **argv) { } case CmdBuiltin: { CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, - out_type, build_mode, override_lib_dir, override_std_dir, nullptr, nullptr); + out_type, build_mode, override_lib_dir, override_std_dir, nullptr, nullptr, false); codegen_set_strip(g, strip); for (size_t i = 0; i < link_libs.length; i += 1) { LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); @@ -1115,7 +1115,7 @@ int main(int argc, char **argv) { cache_dir_buf = buf_create_from_str(cache_dir); } CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode, - override_lib_dir, override_std_dir, libc, cache_dir_buf); + override_lib_dir, override_std_dir, libc, cache_dir_buf, cmd == CmdTest); if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2); g->valgrind_support = valgrind_support; g->want_pic = want_pic; @@ -1125,7 +1125,6 @@ int main(int argc, char **argv) { g->enable_time_report = timing_info; codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); - codegen_set_is_test(g, cmd == CmdTest); g->want_single_threaded = want_single_threaded; codegen_set_linker_script(g, linker_script); g->version_script_path = version_script; diff --git a/src/parser.cpp b/src/parser.cpp index 0783d4ec10..fe1f89ac92 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -676,7 +676,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { AstNode *expr = ast_expect(pc, ast_parse_expr); expect_token(pc, TokenIdSemicolon); - AstNode *res = ast_create_node(pc, NodeTypeUse, usingnamespace); + AstNode *res = ast_create_node(pc, NodeTypeUsingNamespace, usingnamespace); res->data.using_namespace.visib_mod = visib_mod; res->data.using_namespace.expr = expr; return res; @@ -2938,7 +2938,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeUnwrapOptional: visit_field(&node->data.unwrap_optional.expr, visit, context); break; - case NodeTypeUse: + case NodeTypeUsingNamespace: visit_field(&node->data.using_namespace.expr, visit, context); break; case NodeTypeBoolLiteral: diff --git a/src/target.cpp b/src/target.cpp index 5a418aa547..4de7c9ce23 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -878,6 +878,72 @@ uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch) { zig_unreachable(); } +uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + + case ZigLLVM_avr: + case ZigLLVM_msp430: + return 16; + + case ZigLLVM_arc: + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_hexagon: + case ZigLLVM_le32: + case ZigLLVM_mips: + case ZigLLVM_mipsel: + case ZigLLVM_nvptx: + case ZigLLVM_ppc: + case ZigLLVM_r600: + case ZigLLVM_riscv32: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + case ZigLLVM_x86: + case ZigLLVM_xcore: + case ZigLLVM_amdil: + case ZigLLVM_hsail: + case ZigLLVM_spir: + case ZigLLVM_kalimba: + case ZigLLVM_lanai: + case ZigLLVM_shave: + case ZigLLVM_wasm32: + case ZigLLVM_renderscript32: + return 32; + + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + case ZigLLVM_aarch64_32: + case ZigLLVM_amdgcn: + case ZigLLVM_bpfel: + case ZigLLVM_bpfeb: + case ZigLLVM_le64: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_nvptx64: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + case ZigLLVM_riscv64: + case ZigLLVM_sparcv9: + case ZigLLVM_systemz: + case ZigLLVM_amdil64: + case ZigLLVM_hsail64: + case ZigLLVM_spir64: + case ZigLLVM_wasm64: + case ZigLLVM_renderscript64: + return 64; + + case ZigLLVM_x86_64: + return 128; + } + zig_unreachable(); +} + uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { switch (target->os) { case OsFreestanding: @@ -1524,9 +1590,9 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { case OsKFreeBSD: case OsNetBSD: case OsHurd: - case OsWindows: return ZigLLVM_GNU; case OsUefi: + case OsWindows: return ZigLLVM_MSVC; case OsLinux: case OsWASI: diff --git a/src/target.hpp b/src/target.hpp index a8c0c890eb..5a9e119af1 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -195,6 +195,7 @@ const char *target_arch_musl_name(ZigLLVM_ArchType arch); bool target_supports_libunwind(const ZigTarget *target); uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch); +uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch); size_t target_libc_count(void); void target_libc_enum(size_t index, ZigTarget *out_target); diff --git a/src/util.hpp b/src/util.hpp index 1fa33b30f9..1248635de9 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -43,7 +43,7 @@ ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF(1, 2) void zig_panic(const char *format, ...); -#ifdef WIN32 +#ifdef _WIN32 #define __func__ __FUNCTION__ #endif diff --git a/src/zig_clang.h b/src/zig_clang.h index 3891b0fac6..7f773230f3 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -50,7 +50,7 @@ enum ZigClangAPValueKind { struct ZigClangAPValue { enum ZigClangAPValueKind Kind; // experimentally-derived size of clang::APValue::DataType -#if defined(WIN32) && defined(_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) char Data[52]; #else char Data[68]; diff --git a/std/build.zig b/std/build.zig index f4e9c2b53b..9393d72c15 100644 --- a/std/build.zig +++ b/std/build.zig @@ -41,9 +41,11 @@ pub const Builder = struct { env_map: *BufMap, top_level_steps: ArrayList(*TopLevelStep), install_prefix: ?[]const u8, - search_prefixes: ArrayList([]const u8), + dest_dir: ?[]const u8, lib_dir: ?[]const u8, exe_dir: ?[]const u8, + install_path: []const u8, + search_prefixes: ArrayList([]const u8), installed_files: ArrayList(InstalledFile), build_root: []const u8, cache_root: []const u8, @@ -125,10 +127,11 @@ pub const Builder = struct { .top_level_steps = ArrayList(*TopLevelStep).init(allocator), .default_step = undefined, .env_map = env_map, - .install_prefix = null, .search_prefixes = ArrayList([]const u8).init(allocator), + .install_prefix = null, .lib_dir = null, .exe_dir = null, + .dest_dir = env_map.get("DESTDIR"), .installed_files = ArrayList(InstalledFile).init(allocator), .install_tls = TopLevelStep{ .step = Step.initNoOp("install", allocator), @@ -142,6 +145,7 @@ pub const Builder = struct { .is_release = false, .override_std_dir = null, .override_lib_dir = null, + .install_path = undefined, }; try self.top_level_steps.append(&self.install_tls); try self.top_level_steps.append(&self.uninstall_tls); @@ -164,14 +168,19 @@ pub const Builder = struct { } fn resolveInstallPrefix(self: *Builder) void { - const prefix = if (self.install_prefix) |prefix| prefix else blk: { - const prefix = self.cache_root; - self.install_prefix = prefix; - break :blk prefix; - }; - - self.lib_dir = fs.path.join(self.allocator, [_][]const u8{ prefix, "lib" }) catch unreachable; - self.exe_dir = fs.path.join(self.allocator, [_][]const u8{ prefix, "bin" }) catch unreachable; + if (self.dest_dir) |dest_dir| { + const install_prefix = self.install_prefix orelse "/usr"; + self.install_path = fs.path.join(self.allocator, [_][]const u8{ dest_dir, install_prefix }) catch unreachable; + } else { + const install_prefix = self.install_prefix orelse blk: { + const p = self.cache_root; + self.install_prefix = p; + break :blk p; + }; + self.install_path = install_prefix; + } + self.lib_dir = fs.path.join(self.allocator, [_][]const u8{ self.install_path, "lib" }) catch unreachable; + self.exe_dir = fs.path.join(self.allocator, [_][]const u8{ self.install_path, "bin" }) catch unreachable; } pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { @@ -796,11 +805,7 @@ pub const Builder = struct { return name; } const full_path = try fs.path.join(self.allocator, [_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); - if (fs.path.real(self.allocator, full_path)) |real_path| { - return real_path; - } else |_| { - continue; - } + return fs.realpathAlloc(self.allocator, full_path) catch continue; } } if (self.env_map.get("PATH")) |PATH| { @@ -808,14 +813,10 @@ pub const Builder = struct { if (fs.path.isAbsolute(name)) { return name; } - var it = mem.tokenize(PATH, []u8{fs.path.delimiter}); + var it = mem.tokenize(PATH, [_]u8{fs.path.delimiter}); while (it.next()) |path| { const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); - if (fs.path.real(self.allocator, full_path)) |real_path| { - return real_path; - } else |_| { - continue; - } + return fs.realpathAlloc(self.allocator, full_path) catch continue; } } } @@ -825,11 +826,7 @@ pub const Builder = struct { } for (paths) |path| { const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); - if (fs.path.real(self.allocator, full_path)) |real_path| { - return real_path; - } else |_| { - continue; - } + return fs.realpathAlloc(self.allocator, full_path) catch continue; } } return error.FileNotFound; @@ -884,7 +881,7 @@ pub const Builder = struct { fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { const base_dir = switch (dir) { - .Prefix => self.install_prefix.?, + .Prefix => self.install_path, .Bin => self.exe_dir.?, .Lib => self.lib_dir.?, }; @@ -895,6 +892,15 @@ pub const Builder = struct { } }; +test "builder.findProgram compiles" { + //allocator: *Allocator, + //zig_exe: []const u8, + //build_root: []const u8, + //cache_root: []const u8, + const builder = try Builder.create(std.heap.direct_allocator, "zig", "zig-cache", "zig-cache"); + _ = builder.findProgram([_][]const u8{}, [_][]const u8{}) catch null; +} + pub const Version = struct { major: u32, minor: u32, @@ -1113,6 +1119,9 @@ pub const Target = union(enum) { } pub fn libPrefix(self: Target) []const u8 { + if (self.isWasm()) { + return ""; + } switch (self.getAbi()) { .msvc => return "", else => return "lib", @@ -1255,6 +1264,8 @@ pub const LibExeObjStep = struct { libc_file: ?[]const u8 = null, target_glibc: ?Version = null, + valgrind_support: ?bool = null, + const LinkObject = union(enum) { StaticPath: []const u8, OtherStep: *LibExeObjStep, @@ -1791,7 +1802,7 @@ pub const LibExeObjStep = struct { try zig_args.append("--bundle-compiler-rt"); } if (self.disable_stack_probing) { - try zig_args.append("--disable-stack-probing"); + try zig_args.append("-fno-stack-check"); } switch (self.target) { @@ -1882,6 +1893,14 @@ pub const LibExeObjStep = struct { try zig_args.append("--system-linker-hack"); } + if (self.valgrind_support) |valgrind_support| { + if (valgrind_support) { + try zig_args.append("--enable-valgrind"); + } else { + try zig_args.append("--disable-valgrind"); + } + } + if (self.override_std_dir) |dir| { try zig_args.append("--override-std-dir"); try zig_args.append(builder.pathFromRoot(dir)); diff --git a/std/coff.zig b/std/coff.zig index 9fdc368878..3890151d09 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -19,6 +19,7 @@ const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; +const IMAGE_DEBUG_TYPE_CODEVIEW = 2; const DEBUG_DIRECTORY = 6; pub const CoffError = error{ @@ -28,6 +29,7 @@ pub const CoffError = error{ MissingCoffSection, }; +// Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format pub const Coff = struct { in_file: File, allocator: *mem.Allocator, @@ -120,16 +122,43 @@ pub const Coff = struct { pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { try self.loadSections(); - const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header; - // The linker puts a chunk that contains the .pdb path right after the - // debug_directory. + const header = blk: { + if (self.getSection(".buildid")) |section| { + break :blk section.header; + } else if (self.getSection(".rdata")) |section| { + break :blk section.header; + } else { + return error.MissingCoffSection; + } + }; + const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; - try self.in_file.seekTo(file_offset + debug_dir.size); var file_stream = self.in_file.inStream(); const in = &file_stream.stream; + try self.in_file.seekTo(file_offset); + + // Find the correct DebugDirectoryEntry, and where its data is stored. + // It can be in any section. + const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); + var i: u32 = 0; + blk: while (i < debug_dir_entry_count) : (i += 1) { + const debug_dir_entry = try in.readStruct(DebugDirectoryEntry); + if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) { + for (self.sections.toSlice()) |*section| { + const section_start = section.header.virtual_address; + const section_size = section.header.misc.virtual_size; + const rva = debug_dir_entry.address_of_raw_data; + const offset = rva - section_start; + if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) { + try self.in_file.seekTo(section.header.pointer_to_raw_data + offset); + break :blk; + } + } + } + } var cv_signature: [4]u8 = undefined; // CodeView signature try in.readNoEof(cv_signature[0..]); @@ -141,7 +170,7 @@ pub const Coff = struct { // Finally read the null-terminated string. var byte = try in.readByte(); - var i: usize = 0; + i = 0; while (byte != 0 and i < buffer.len) : (i += 1) { buffer[i] = byte; byte = try in.readByte(); @@ -170,7 +199,7 @@ pub const Coff = struct { try self.sections.append(Section{ .header = SectionHeader{ .name = name, - .misc = SectionHeader.Misc{ .physical_address = try in.readIntLittle(u32) }, + .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) }, .virtual_address = try in.readIntLittle(u32), .size_of_raw_data = try in.readIntLittle(u32), .pointer_to_raw_data = try in.readIntLittle(u32), @@ -214,6 +243,17 @@ const OptionalHeader = struct { data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, }; +const DebugDirectoryEntry = packed struct { + characteristiccs: u32, + time_date_stamp: u32, + major_version: u16, + minor_version: u16, + @"type": u32, + size_of_data: u32, + address_of_raw_data: u32, + pointer_to_raw_data: u32, +}; + pub const Section = struct { header: SectionHeader, }; diff --git a/std/debug.zig b/std/debug.zig index d81e62901a..32f96d3e15 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -12,6 +12,7 @@ const coff = std.coff; const pdb = std.pdb; const ArrayList = std.ArrayList; const builtin = @import("builtin"); +const root = @import("root"); const maxInt = std.math.maxInt; const File = std.fs.File; const windows = std.os.windows; @@ -217,6 +218,12 @@ var panicking: u8 = 0; // TODO make this a bool pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { @setCold(true); + if (enable_segfault_handler) { + // If a segfault happens while panicking, we want it to actually segfault, not trigger + // the handler. + resetSegfaultHandler(); + } + if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { // Panicked during a panic. @@ -368,7 +375,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres const obj_basename = fs.path.basename(mod.obj_file_name); var symbol_i: usize = 0; - const symbol_name = while (symbol_i != mod.symbols.len) { + const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); if (prefix.RecordLen < 2) return error.InvalidDebugInfo; @@ -851,8 +858,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const age = try pdb_stream.stream.readIntLittle(u32); var guid: [16]u8 = undefined; try pdb_stream.stream.readNoEof(guid[0..]); + if (version != 20000404) // VC70, only value observed by LLVM team + return error.UnknownPDBVersion; if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) - return error.InvalidDebugInfo; + return error.PDBMismatch; // We validated the executable and pdb match. const string_table_index = str_tab_index: { @@ -896,13 +905,18 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { return error.MissingDebugInfo; }; - di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; + di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; const dbi = di.pdb.dbi; // Dbi Header const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); + if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team + return error.UnknownPDBVersion; + if (dbi_stream_header.Age != age) + return error.UnmatchingPDB; + const mod_info_size = dbi_stream_header.ModInfoSize; const section_contrib_size = dbi_stream_header.SectionContributionSize; @@ -2312,39 +2326,58 @@ fn getDebugInfoAllocator() *mem.Allocator { /// Whether or not the current target can print useful debug information when a segfault occurs. pub const have_segfault_handling_support = (builtin.arch == builtin.Arch.x86_64 and builtin.os == .linux) or builtin.os == .windows; +pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler")) + root.enable_segfault_handler +else + runtime_safety and have_segfault_handling_support; + +pub fn maybeEnableSegfaultHandler() void { + if (enable_segfault_handler) { + std.debug.attachSegfaultHandler(); + } +} + +var windows_segfault_handle: ?windows.HANDLE = null; /// Attaches a global SIGSEGV handler which calls @panic("segmentation fault"); pub fn attachSegfaultHandler() void { if (!have_segfault_handling_support) { @compileError("segfault handler not supported for this target"); } - switch (builtin.os) { - .linux => { - var act = os.Sigaction{ - .sigaction = handleSegfaultLinux, - .mask = os.empty_sigset, - .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND), - }; - - os.sigaction(os.SIGSEGV, &act, null); - }, - .windows => { - _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); - }, - else => unreachable, + if (windows.is_the_target) { + windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); + return; } + var act = os.Sigaction{ + .sigaction = handleSegfaultLinux, + .mask = os.empty_sigset, + .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND), + }; + + os.sigaction(os.SIGSEGV, &act, null); } -extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn { - // Reset to the default handler so that if a segfault happens in this handler it will crash - // the process. Also when this handler returns, the original instruction will be repeated - // and the resulting segfault will crash the process rather than continually dump stack traces. +fn resetSegfaultHandler() void { + if (windows.is_the_target) { + if (windows_segfault_handle) |handle| { + assert(windows.kernel32.RemoveVectoredExceptionHandler(handle) != 0); + windows_segfault_handle = null; + } + return; + } var act = os.Sigaction{ .sigaction = os.SIG_DFL, .mask = os.empty_sigset, .flags = 0, }; os.sigaction(os.SIGSEGV, &act, null); +} + +extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn { + // Reset to the default handler so that if a segfault happens in this handler it will crash + // the process. Also when this handler returns, the original instruction will be repeated + // and the resulting segfault will crash the process rather than continually dump stack traces. + resetSegfaultHandler(); const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]); diff --git a/std/fmt.zig b/std/fmt.zig index 2e9527f4ca..7c08cd14ee 100644 --- a/std/fmt.zig +++ b/std/fmt.zig @@ -374,9 +374,10 @@ pub fn formatType( return output(context, "{ ... }"); } comptime var field_i = 0; + try output(context, "{"); inline while (field_i < @memberCount(T)) : (field_i += 1) { if (field_i == 0) { - try output(context, "{ ."); + try output(context, " ."); } else { try output(context, ", ."); } @@ -425,6 +426,9 @@ pub fn formatType( if (info.child == u8) { return formatText(value, fmt, options, context, Errors, output); } + if (value.len == 0) { + return format(context, Errors, output, "[0]{}", @typeName(T.Child)); + } return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); }, .Fn => { @@ -1439,6 +1443,21 @@ test "struct.self-referential" { try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); } +test "struct.zero-size" { + const A = struct { + fn foo() void {} + }; + const B = struct { + a: A, + c: i32, + }; + + const a = A{}; + const b = B{ .a = a, .c = 0 }; + + try testFmt("B{ .a = A{ }, .c = 0 }", "{}", b); +} + test "bytes.hex" { const some_bytes = "\xCA\xFE\xBA\xBE"; try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); diff --git a/std/os.zig b/std/os.zig index 190f02101e..c2010bf6a9 100644 --- a/std/os.zig +++ b/std/os.zig @@ -133,6 +133,11 @@ fn getRandomBytesDevURandom(buf: []u8) !void { const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0); defer close(fd); + const st = try fstat(fd); + if (!S_ISCHR(st.mode)) { + return error.NoDevice; + } + const stream = &std.fs.File.openHandle(fd).inStream().stream; stream.readNoEof(buf) catch return error.Unexpected; } @@ -2053,6 +2058,22 @@ pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { } } +/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. +/// Otherwise use `access` or `accessC`. +/// TODO currently this ignores `mode`. +pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!void { + const ret = try windows.GetFileAttributesW(path); + if (ret != windows.INVALID_FILE_ATTRIBUTES) { + return; + } + switch (windows.kernel32.GetLastError()) { + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, + else => |err| return windows.unexpectedError(err), + } +} + pub const PipeError = error{ SystemFdQuotaExceeded, ProcessFdQuotaExceeded, diff --git a/std/os/bits/darwin.zig b/std/os/bits/darwin.zig index b8d229dbe9..483d4cda90 100644 --- a/std/os/bits/darwin.zig +++ b/std/os/bits/darwin.zig @@ -1116,3 +1116,62 @@ pub const stack_t = extern struct { ss_size: isize, ss_flags: i32, }; + +pub const S_IFMT = 0o170000; + +pub const S_IFIFO = 0o010000; +pub const S_IFCHR = 0o020000; +pub const S_IFDIR = 0o040000; +pub const S_IFBLK = 0o060000; +pub const S_IFREG = 0o100000; +pub const S_IFLNK = 0o120000; +pub const S_IFSOCK = 0o140000; +pub const S_IFWHT = 0o160000; + +pub const S_ISUID = 0o4000; +pub const S_ISGID = 0o2000; +pub const S_ISVTX = 0o1000; +pub const S_IRWXU = 0o700; +pub const S_IRUSR = 0o400; +pub const S_IWUSR = 0o200; +pub const S_IXUSR = 0o100; +pub const S_IRWXG = 0o070; +pub const S_IRGRP = 0o040; +pub const S_IWGRP = 0o020; +pub const S_IXGRP = 0o010; +pub const S_IRWXO = 0o007; +pub const S_IROTH = 0o004; +pub const S_IWOTH = 0o002; +pub const S_IXOTH = 0o001; + +pub fn S_ISFIFO(m: u32) bool { + return m & S_IFMT == S_IFIFO; +} + +pub fn S_ISCHR(m: u32) bool { + return m & S_IFMT == S_IFCHR; +} + +pub fn S_ISDIR(m: u32) bool { + return m & S_IFMT == S_IFDIR; +} + +pub fn S_ISBLK(m: u32) bool { + return m & S_IFMT == S_IFBLK; +} + +pub fn S_ISREG(m: u32) bool { + return m & S_IFMT == S_IFREG; +} + +pub fn S_ISLNK(m: u32) bool { + return m & S_IFMT == S_IFLNK; +} + +pub fn S_ISSOCK(m: u32) bool { + return m & S_IFMT == S_IFSOCK; +} + +pub fn S_IWHT(m: u32) bool { + return m & S_IFMT == S_IFWHT; +} diff --git a/std/os/bits/freebsd.zig b/std/os/bits/freebsd.zig index 198857983e..45432a6c07 100644 --- a/std/os/bits/freebsd.zig +++ b/std/os/bits/freebsd.zig @@ -876,3 +876,62 @@ pub const stack_t = extern struct { ss_size: isize, ss_flags: i32, }; + +pub const S_IFMT = 0o170000; + +pub const S_IFIFO = 0o010000; +pub const S_IFCHR = 0o020000; +pub const S_IFDIR = 0o040000; +pub const S_IFBLK = 0o060000; +pub const S_IFREG = 0o100000; +pub const S_IFLNK = 0o120000; +pub const S_IFSOCK = 0o140000; +pub const S_IFWHT = 0o160000; + +pub const S_ISUID = 0o4000; +pub const S_ISGID = 0o2000; +pub const S_ISVTX = 0o1000; +pub const S_IRWXU = 0o700; +pub const S_IRUSR = 0o400; +pub const S_IWUSR = 0o200; +pub const S_IXUSR = 0o100; +pub const S_IRWXG = 0o070; +pub const S_IRGRP = 0o040; +pub const S_IWGRP = 0o020; +pub const S_IXGRP = 0o010; +pub const S_IRWXO = 0o007; +pub const S_IROTH = 0o004; +pub const S_IWOTH = 0o002; +pub const S_IXOTH = 0o001; + +pub fn S_ISFIFO(m: u32) bool { + return m & S_IFMT == S_IFIFO; +} + +pub fn S_ISCHR(m: u32) bool { + return m & S_IFMT == S_IFCHR; +} + +pub fn S_ISDIR(m: u32) bool { + return m & S_IFMT == S_IFDIR; +} + +pub fn S_ISBLK(m: u32) bool { + return m & S_IFMT == S_IFBLK; +} + +pub fn S_ISREG(m: u32) bool { + return m & S_IFMT == S_IFREG; +} + +pub fn S_ISLNK(m: u32) bool { + return m & S_IFMT == S_IFLNK; +} + +pub fn S_ISSOCK(m: u32) bool { + return m & S_IFMT == S_IFSOCK; +} + +pub fn S_IWHT(m: u32) bool { + return m & S_IFMT == S_IFWHT; +} diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 67ce9a06cf..c2b6801e22 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -5,3 +5,4 @@ pub const is_the_target = switch (builtin.os) { else => false, }; pub usingnamespace std.c; +pub usingnamespace @import("bits.zig");
\ No newline at end of file diff --git a/std/os/freebsd.zig b/std/os/freebsd.zig index d418ccd415..e9efe64920 100644 --- a/std/os/freebsd.zig +++ b/std/os/freebsd.zig @@ -2,3 +2,4 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); pub const is_the_target = builtin.os == .freebsd; pub usingnamespace std.c; +pub usingnamespace @import("bits.zig");
\ No newline at end of file diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index e4edc349ab..2ae73ad45a 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -1,6 +1,7 @@ usingnamespace @import("bits.zig"); pub extern "kernel32" stdcallcc fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) ?*c_void; +pub extern "kernel32" stdcallcc fn RemoveVectoredExceptionHandler(Handle: HANDLE) c_ulong; pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; diff --git a/std/pdb.zig b/std/pdb.zig index ffe2120296..7a2b2c6b6b 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -499,45 +499,78 @@ const Msf = struct { const superblock = try in.readStruct(SuperBlock); + // Sanity checks if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) return error.InvalidDebugInfo; - + if (superblock.FreeBlockMapBlock != 1 and superblock.FreeBlockMapBlock != 2) + return error.InvalidDebugInfo; + if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) + return error.InvalidDebugInfo; switch (superblock.BlockSize) { // llvm only supports 4096 but we can handle any of these values 512, 1024, 2048, 4096 => {}, else => return error.InvalidDebugInfo, } - if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) - return error.InvalidDebugInfo; + const dir_block_count = blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize); + if (dir_block_count > superblock.BlockSize / @sizeOf(u32)) + return error.UnhandledBigDirectoryStream; // cf. BlockMapAddr comment. - self.directory = try MsfStream.init( + try file.seekTo(superblock.BlockSize * superblock.BlockMapAddr); + var dir_blocks = try allocator.alloc(u32, dir_block_count); + for (dir_blocks) |*b| { + b.* = try in.readIntLittle(u32); + } + self.directory = MsfStream.init( superblock.BlockSize, - blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize), - superblock.BlockSize * superblock.BlockMapAddr, file, - allocator, + dir_blocks, ); + const begin = self.directory.pos; const stream_count = try self.directory.stream.readIntLittle(u32); - const stream_sizes = try allocator.alloc(u32, stream_count); - for (stream_sizes) |*s| { + defer allocator.free(stream_sizes); + + // Microsoft's implementation uses u32(-1) for inexistant streams. + // These streams are not used, but still participate in the file + // and must be taken into account when resolving stream indices. + const Nil = 0xFFFFFFFF; + for (stream_sizes) |*s, i| { const size = try self.directory.stream.readIntLittle(u32); - s.* = blockCountFromSize(size, superblock.BlockSize); + s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize); } self.streams = try allocator.alloc(MsfStream, stream_count); for (self.streams) |*stream, i| { - stream.* = try MsfStream.init( - superblock.BlockSize, - stream_sizes[i], - // MsfStream.init expects the file to be at the part where it reads [N]u32 - try file.getPos(), - file, - allocator, - ); + const size = stream_sizes[i]; + if (size == 0) { + stream.* = MsfStream{ + .blocks = [_]u32{}, + }; + } else { + var blocks = try allocator.alloc(u32, size); + var j: u32 = 0; + while (j < size) : (j += 1) { + const block_id = try self.directory.stream.readIntLittle(u32); + const n = (block_id % superblock.BlockSize); + // 0 is for SuperBlock, 1 and 2 for FPMs. + if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos()) + return error.InvalidBlockIndex; + blocks[j] = block_id; + } + + stream.* = MsfStream.init( + superblock.BlockSize, + file, + blocks, + ); + } } + + const end = self.directory.pos; + if (end - begin != superblock.NumDirectoryBytes) + return error.InvalidStreamDirectory; } }; @@ -574,7 +607,6 @@ const SuperBlock = packed struct { NumDirectoryBytes: u32, Unknown: u32, - /// The index of a block within the MSF file. At this block is an array of /// ulittle32_t’s listing the blocks that the stream directory resides on. /// For large MSF files, the stream directory (which describes the block @@ -584,45 +616,41 @@ const SuperBlock = packed struct { /// and the stream directory itself can be stitched together accordingly. /// The number of ulittle32_t’s in this array is given by /// ceil(NumDirectoryBytes / BlockSize). + // Note: microsoft-pdb code actually suggests this is a variable-length + // array. If the indices of blocks occupied by the Stream Directory didn't + // fit in one page, there would be other u32 following it. + // This would mean the Stream Directory is bigger than BlockSize / sizeof(u32) + // blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't + // implement it so we're kind of safe making this assumption for now. BlockMapAddr: u32, }; const MsfStream = struct { - in_file: File, - pos: u64, - blocks: []u32, - block_size: u32, + in_file: File = undefined, + pos: u64 = undefined, + blocks: []u32 = undefined, + block_size: u32 = undefined, /// Implementation of InStream trait for Pdb.MsfStream - stream: Stream, + stream: Stream = undefined, pub const Error = @typeOf(read).ReturnType.ErrorSet; pub const Stream = io.InStream(Error); - fn init(block_size: u32, block_count: u32, pos: u64, file: File, allocator: *mem.Allocator) !MsfStream { - var stream = MsfStream{ + fn init(block_size: u32, file: File, blocks: []u32) MsfStream { + const stream = MsfStream{ .in_file = file, .pos = 0, - .blocks = try allocator.alloc(u32, block_count), + .blocks = blocks, .block_size = block_size, .stream = Stream{ .readFn = readFn }, }; - var file_stream = file.inStream(); - const in = &file_stream.stream; - try file.seekTo(pos); - - var i: u32 = 0; - while (i < block_count) : (i += 1) { - stream.blocks[i] = try in.readIntLittle(u32); - } - return stream; } fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 { var list = ArrayList(u8).init(allocator); - defer list.deinit(); while (true) { const byte = try self.stream.readByte(); if (byte == 0) { @@ -642,11 +670,12 @@ const MsfStream = struct { const in = &file_stream.stream; var size: usize = 0; - for (buffer) |*byte| { - byte.* = try in.readByte(); - - offset += 1; - size += 1; + var rem_buffer = buffer; + while (size < buffer.len) { + const size_to_read = math.min(self.block_size - offset, rem_buffer.len); + size += try in.read(rem_buffer[0..size_to_read]); + rem_buffer = buffer[size..]; + offset += size_to_read; // If we're at the end of a block, go to the next one. if (offset == self.block_size) { @@ -657,8 +686,8 @@ const MsfStream = struct { } } - self.pos += size; - return size; + self.pos += buffer.len; + return buffer.len; } fn seekBy(self: *MsfStream, len: i64) !void { diff --git a/std/process.zig b/std/process.zig index b39c6e1196..c74e8c43be 100644 --- a/std/process.zig +++ b/std/process.zig @@ -146,13 +146,12 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned error.Unexpected => return error.EnvironmentVariableNotFound, else => |e| return e, }; - if (result > buf.len) { buf = try allocator.realloc(buf, result); continue; } - return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { + return std.unicode.utf16leToUtf8Alloc(allocator, buf[0..result]) catch |err| switch (err) { error.DanglingSurrogateHalf => return error.InvalidUtf8, error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, diff --git a/std/rb.zig b/std/rb.zig index 0b28550a13..0b84950544 100644 --- a/std/rb.zig +++ b/std/rb.zig @@ -93,7 +93,8 @@ pub const Node = struct { comptime { assert(@alignOf(*Node) >= 2); } - return @intToPtr(*Node, node.parent_and_color & ~mask); + const maybe_ptr = node.parent_and_color & ~mask; + return if (maybe_ptr == 0) null else @intToPtr(*Node, maybe_ptr); } fn setColor(node: *Node, color: Color) void { @@ -233,10 +234,13 @@ pub const Tree = struct { return null; } + /// lookup searches for the value of key, using binary search. It will + /// return a pointer to the node if it is there, otherwise it will return null. + /// Complexity guaranteed O(log n), where n is the number of nodes book-kept + /// by tree. pub fn lookup(tree: *Tree, key: *Node) ?*Node { - var parent: *Node = undefined; + var parent: ?*Node = undefined; var is_left: bool = undefined; - return doLookup(key, tree, &parent, &is_left); } @@ -544,3 +548,47 @@ test "rb" { num = testGetNumber(num.node.next().?); } } + + +test "inserting and looking up" { + var tree: Tree = undefined; + tree.init(testCompare); + var number: testNumber = undefined; + number.value = 1000; + _ = tree.insert(&number.node); + var dup: testNumber = undefined; + //Assert that tuples with identical value fields finds the same pointer + dup.value = 1000; + assert(tree.lookup(&dup.node) == &number.node); + //Assert that tuples with identical values do not clobber when inserted. + _ = tree.insert(&dup.node); + assert(tree.lookup(&dup.node) == &number.node); + assert(tree.lookup(&number.node) != &dup.node); + assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value); + //Assert that if looking for a non-existing value, return null. + var non_existing_value: testNumber = undefined; + non_existing_value.value = 1234; + assert(tree.lookup(&non_existing_value.node) == null); +} + +test "multiple inserts, followed by calling first and last" { + var tree: Tree = undefined; + tree.init(testCompare); + var zeroth: testNumber = undefined; + zeroth.value = 0; + var first: testNumber = undefined; + first.value = 1; + var second: testNumber = undefined; + second.value = 2; + var third: testNumber = undefined; + third.value = 3; + _ = tree.insert(&zeroth.node); + _ = tree.insert(&first.node); + _ = tree.insert(&second.node); + _ = tree.insert(&third.node); + assert(testGetNumber(tree.first().?).value == 0); + assert(testGetNumber(tree.last().?).value == 3); + var lookupNode: testNumber = undefined; + lookupNode.value = 3; + assert(tree.lookup(&lookupNode.node) == &third.node); +} diff --git a/std/segmented_list.zig b/std/segmented_list.zig index e0b84d5c0d..3bbbde782e 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -77,15 +77,19 @@ const Allocator = std.mem.Allocator; pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type { return struct { const Self = @This(); - const prealloc_exp = blk: { - // we don't use the prealloc_exp constant when prealloc_item_count is 0. - assert(prealloc_item_count != 0); - assert(std.math.isPowerOfTwo(prealloc_item_count)); + const ShelfIndex = std.math.Log2Int(usize); - const value = std.math.log2_int(usize, prealloc_item_count); - break :blk @typeOf(1)(value); + const prealloc_exp: ShelfIndex = blk: { + // we don't use the prealloc_exp constant when prealloc_item_count is 0 + // but lazy-init may still be triggered by other code so supply a value + if (prealloc_item_count == 0) { + break :blk 0; + } else { + assert(std.math.isPowerOfTwo(prealloc_item_count)); + const value = std.math.log2_int(usize, prealloc_item_count); + break :blk value; + } }; - const ShelfIndex = std.math.Log2Int(usize); prealloc_segment: [prealloc_item_count]T, dynamic_segments: [][*]T, @@ -157,11 +161,12 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type /// Grows or shrinks capacity to match usage. pub fn setCapacity(self: *Self, new_capacity: usize) !void { - if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) { - return self.shrinkCapacity(new_capacity); - } else { - return self.growCapacity(new_capacity); + if (prealloc_item_count != 0) { + if (new_capacity <= usize(1) << (prealloc_exp + @intCast(ShelfIndex, self.dynamic_segments.len))) { + return self.shrinkCapacity(new_capacity); + } } + return self.growCapacity(new_capacity); } /// Only grows capacity, or retains current capacity @@ -399,4 +404,6 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void { testing.expect(item == i); list.shrinkCapacity(list.len); } + + try list.setCapacity(0); } diff --git a/std/special/start.zig b/std/special/start.zig index 0366e088d9..91e5cfa958 100644 --- a/std/special/start.zig +++ b/std/special/start.zig @@ -24,16 +24,6 @@ comptime { } } -fn enableSegfaultHandler() void { - const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler")) - root.enable_segfault_handler - else - std.debug.runtime_safety and std.debug.have_segfault_handling_support; - if (enable_segfault_handler) { - std.debug.attachSegfaultHandler(); - } -} - extern fn wasm_freestanding_start() void { _ = callMain(); } @@ -45,13 +35,13 @@ nakedcc fn _start() noreturn { switch (builtin.arch) { .x86_64 => { - argc_ptr = asm ("lea (%%rsp), %[argc]" - : [argc] "=r" (-> [*]usize) + argc_ptr = asm ("" + : [argc] "={rsp}" (-> [*]usize) ); }, .i386 => { - argc_ptr = asm ("lea (%%esp), %[argc]" - : [argc] "=r" (-> [*]usize) + argc_ptr = asm ("" + : [argc] "={esp}" (-> [*]usize) ); }, .aarch64, .aarch64_be => { @@ -77,7 +67,7 @@ extern fn WinMainCRTStartup() noreturn { _ = @import("start_windows_tls.zig"); } - enableSegfaultHandler(); + std.debug.maybeEnableSegfaultHandler(); std.os.windows.kernel32.ExitProcess(callMain()); } @@ -118,7 +108,7 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; - enableSegfaultHandler(); + std.debug.maybeEnableSegfaultHandler(); return callMain(); } diff --git a/std/std.zig b/std/std.zig index 350f0fb437..e48586d873 100644 --- a/std/std.zig +++ b/std/std.zig @@ -105,6 +105,7 @@ test "std" { _ = @import("packed_int_array.zig"); _ = @import("priority_queue.zig"); _ = @import("rand.zig"); + _ = @import("rb.zig"); _ = @import("sort.zig"); _ = @import("testing.zig"); _ = @import("thread.zig"); diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 79d1ae8dad..38bd94339f 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -159,101 +159,99 @@ pub const Error = union(enum) { pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void { switch (self.*) { - // TODO https://github.com/ziglang/zig/issues/683 - @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedContainerMembers => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedStringLiteral => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedIntegerLiteral => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedPubItem => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedIdentifier => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedStatement => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedVarDecl => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedReturnType => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream), - @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedSemiOrElse => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedLBrace => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedColonOrRParen => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedLabelable => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedInlinable => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedCall => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedCallOrFnProto => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), - @TagType(Error).ExtraAlignQualifier => |*x| return x.render(tokens, stream), - @TagType(Error).ExtraConstQualifier => |*x| return x.render(tokens, stream), - @TagType(Error).ExtraVolatileQualifier => |*x| return x.render(tokens, stream), - @TagType(Error).ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedTypeExpr => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedParamType => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedExpr => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedToken => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedParamList => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedPayload => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedBlockOrExpression => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedExprOrAssignment => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedPrefixExpr => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedLoopExpr => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream), - @TagType(Error).ExpectedSuffixOp => |*x| return x.render(tokens, stream), + .InvalidToken => |*x| return x.render(tokens, stream), + .ExpectedContainerMembers => |*x| return x.render(tokens, stream), + .ExpectedStringLiteral => |*x| return x.render(tokens, stream), + .ExpectedIntegerLiteral => |*x| return x.render(tokens, stream), + .ExpectedPubItem => |*x| return x.render(tokens, stream), + .ExpectedIdentifier => |*x| return x.render(tokens, stream), + .ExpectedStatement => |*x| return x.render(tokens, stream), + .ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream), + .ExpectedVarDecl => |*x| return x.render(tokens, stream), + .ExpectedReturnType => |*x| return x.render(tokens, stream), + .ExpectedAggregateKw => |*x| return x.render(tokens, stream), + .UnattachedDocComment => |*x| return x.render(tokens, stream), + .ExpectedEqOrSemi => |*x| return x.render(tokens, stream), + .ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream), + .ExpectedSemiOrElse => |*x| return x.render(tokens, stream), + .ExpectedLabelOrLBrace => |*x| return x.render(tokens, stream), + .ExpectedLBrace => |*x| return x.render(tokens, stream), + .ExpectedColonOrRParen => |*x| return x.render(tokens, stream), + .ExpectedLabelable => |*x| return x.render(tokens, stream), + .ExpectedInlinable => |*x| return x.render(tokens, stream), + .ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream), + .ExpectedCall => |*x| return x.render(tokens, stream), + .ExpectedCallOrFnProto => |*x| return x.render(tokens, stream), + .ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream), + .ExtraAlignQualifier => |*x| return x.render(tokens, stream), + .ExtraConstQualifier => |*x| return x.render(tokens, stream), + .ExtraVolatileQualifier => |*x| return x.render(tokens, stream), + .ExtraAllowZeroQualifier => |*x| return x.render(tokens, stream), + .ExpectedTypeExpr => |*x| return x.render(tokens, stream), + .ExpectedPrimaryTypeExpr => |*x| return x.render(tokens, stream), + .ExpectedParamType => |*x| return x.render(tokens, stream), + .ExpectedExpr => |*x| return x.render(tokens, stream), + .ExpectedPrimaryExpr => |*x| return x.render(tokens, stream), + .ExpectedToken => |*x| return x.render(tokens, stream), + .ExpectedCommaOrEnd => |*x| return x.render(tokens, stream), + .ExpectedParamList => |*x| return x.render(tokens, stream), + .ExpectedPayload => |*x| return x.render(tokens, stream), + .ExpectedBlockOrAssignment => |*x| return x.render(tokens, stream), + .ExpectedBlockOrExpression => |*x| return x.render(tokens, stream), + .ExpectedExprOrAssignment => |*x| return x.render(tokens, stream), + .ExpectedPrefixExpr => |*x| return x.render(tokens, stream), + .ExpectedLoopExpr => |*x| return x.render(tokens, stream), + .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream), + .ExpectedSuffixOp => |*x| return x.render(tokens, stream), } } pub fn loc(self: *const Error) TokenIndex { switch (self.*) { - // TODO https://github.com/ziglang/zig/issues/683 - @TagType(Error).InvalidToken => |x| return x.token, - @TagType(Error).ExpectedContainerMembers => |x| return x.token, - @TagType(Error).ExpectedStringLiteral => |x| return x.token, - @TagType(Error).ExpectedIntegerLiteral => |x| return x.token, - @TagType(Error).ExpectedPubItem => |x| return x.token, - @TagType(Error).ExpectedIdentifier => |x| return x.token, - @TagType(Error).ExpectedStatement => |x| return x.token, - @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token, - @TagType(Error).ExpectedVarDecl => |x| return x.token, - @TagType(Error).ExpectedReturnType => |x| return x.token, - @TagType(Error).ExpectedAggregateKw => |x| return x.token, - @TagType(Error).UnattachedDocComment => |x| return x.token, - @TagType(Error).ExpectedEqOrSemi => |x| return x.token, - @TagType(Error).ExpectedSemiOrLBrace => |x| return x.token, - @TagType(Error).ExpectedSemiOrElse => |x| return x.token, - @TagType(Error).ExpectedLabelOrLBrace => |x| return x.token, - @TagType(Error).ExpectedLBrace => |x| return x.token, - @TagType(Error).ExpectedColonOrRParen => |x| return x.token, - @TagType(Error).ExpectedLabelable => |x| return x.token, - @TagType(Error).ExpectedInlinable => |x| return x.token, - @TagType(Error).ExpectedAsmOutputReturnOrType => |x| return x.token, - @TagType(Error).ExpectedCall => |x| return x.node.firstToken(), - @TagType(Error).ExpectedCallOrFnProto => |x| return x.node.firstToken(), - @TagType(Error).ExpectedSliceOrRBracket => |x| return x.token, - @TagType(Error).ExtraAlignQualifier => |x| return x.token, - @TagType(Error).ExtraConstQualifier => |x| return x.token, - @TagType(Error).ExtraVolatileQualifier => |x| return x.token, - @TagType(Error).ExtraAllowZeroQualifier => |x| return x.token, - @TagType(Error).ExpectedTypeExpr => |x| return x.token, - @TagType(Error).ExpectedPrimaryTypeExpr => |x| return x.token, - @TagType(Error).ExpectedParamType => |x| return x.token, - @TagType(Error).ExpectedExpr => |x| return x.token, - @TagType(Error).ExpectedPrimaryExpr => |x| return x.token, - @TagType(Error).ExpectedToken => |x| return x.token, - @TagType(Error).ExpectedCommaOrEnd => |x| return x.token, - @TagType(Error).ExpectedParamList => |x| return x.token, - @TagType(Error).ExpectedPayload => |x| return x.token, - @TagType(Error).ExpectedBlockOrAssignment => |x| return x.token, - @TagType(Error).ExpectedBlockOrExpression => |x| return x.token, - @TagType(Error).ExpectedExprOrAssignment => |x| return x.token, - @TagType(Error).ExpectedPrefixExpr => |x| return x.token, - @TagType(Error).ExpectedLoopExpr => |x| return x.token, - @TagType(Error).ExpectedDerefOrUnwrap => |x| return x.token, - @TagType(Error).ExpectedSuffixOp => |x| return x.token, + .InvalidToken => |x| return x.token, + .ExpectedContainerMembers => |x| return x.token, + .ExpectedStringLiteral => |x| return x.token, + .ExpectedIntegerLiteral => |x| return x.token, + .ExpectedPubItem => |x| return x.token, + .ExpectedIdentifier => |x| return x.token, + .ExpectedStatement => |x| return x.token, + .ExpectedVarDeclOrFn => |x| return x.token, + .ExpectedVarDecl => |x| return x.token, + .ExpectedReturnType => |x| return x.token, + .ExpectedAggregateKw => |x| return x.token, + .UnattachedDocComment => |x| return x.token, + .ExpectedEqOrSemi => |x| return x.token, + .ExpectedSemiOrLBrace => |x| return x.token, + .ExpectedSemiOrElse => |x| return x.token, + .ExpectedLabelOrLBrace => |x| return x.token, + .ExpectedLBrace => |x| return x.token, + .ExpectedColonOrRParen => |x| return x.token, + .ExpectedLabelable => |x| return x.token, + .ExpectedInlinable => |x| return x.token, + .ExpectedAsmOutputReturnOrType => |x| return x.token, + .ExpectedCall => |x| return x.node.firstToken(), + .ExpectedCallOrFnProto => |x| return x.node.firstToken(), + .ExpectedSliceOrRBracket => |x| return x.token, + .ExtraAlignQualifier => |x| return x.token, + .ExtraConstQualifier => |x| return x.token, + .ExtraVolatileQualifier => |x| return x.token, + .ExtraAllowZeroQualifier => |x| return x.token, + .ExpectedTypeExpr => |x| return x.token, + .ExpectedPrimaryTypeExpr => |x| return x.token, + .ExpectedParamType => |x| return x.token, + .ExpectedExpr => |x| return x.token, + .ExpectedPrimaryExpr => |x| return x.token, + .ExpectedToken => |x| return x.token, + .ExpectedCommaOrEnd => |x| return x.token, + .ExpectedParamList => |x| return x.token, + .ExpectedPayload => |x| return x.token, + .ExpectedBlockOrAssignment => |x| return x.token, + .ExpectedBlockOrExpression => |x| return x.token, + .ExpectedExprOrAssignment => |x| return x.token, + .ExpectedPrefixExpr => |x| return x.token, + .ExpectedLoopExpr => |x| return x.token, + .ExpectedDerefOrUnwrap => |x| return x.token, + .ExpectedSuffixOp => |x| return x.token, } } @@ -320,8 +318,19 @@ pub const Error = union(enum) { expected_id: Token.Id, pub fn render(self: *const ExpectedToken, tokens: *Tree.TokenList, stream: var) !void { - const token_name = @tagName(tokens.at(self.token).id); - return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); + const found_token = tokens.at(self.token); + switch (found_token.id) { + .Invalid_ampersands => { + return stream.print("`&&` is invalid. Note that `and` is boolean AND."); + }, + .Invalid => { + return stream.print("expected {}, found invalid bytes", @tagName(self.expected_id)); + }, + else => { + const token_name = @tagName(found_token.id); + return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); + }, + } } }; @@ -1708,15 +1717,15 @@ pub const Node = struct { i -= 1; switch (self.op) { - @TagType(Op).Call => |*call_info| { + .Call => |*call_info| { if (i < call_info.params.len) return call_info.params.at(i).*; i -= call_info.params.len; }, - Op.ArrayAccess => |index_expr| { + .ArrayAccess => |index_expr| { if (i < 1) return index_expr; i -= 1; }, - @TagType(Op).Slice => |range| { + .Slice => |range| { if (i < 1) return range.start; i -= 1; @@ -1725,16 +1734,16 @@ pub const Node = struct { i -= 1; } }, - Op.ArrayInitializer => |*exprs| { + .ArrayInitializer => |*exprs| { if (i < exprs.len) return exprs.at(i).*; i -= exprs.len; }, - Op.StructInitializer => |*fields| { + .StructInitializer => |*fields| { if (i < fields.len) return fields.at(i).*; i -= fields.len; }, - Op.UnwrapOptional, - Op.Deref, + .UnwrapOptional, + .Deref, => {}, } @@ -1743,7 +1752,7 @@ pub const Node = struct { pub fn firstToken(self: *const SuffixOp) TokenIndex { switch (self.op) { - @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(), + .Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(), else => {}, } return self.lhs.firstToken(); diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 4f3fb76b54..59acf99890 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -774,7 +774,7 @@ fn parseBoolAndExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node arena, it, tree, - SimpleBinOpParseFn(.Keyword_and, Node.InfixOp.Op.BoolAnd), + SimpleBinOpParseFn(.Keyword_and, .BoolAnd), parseCompareExpr, .Infinitely, ); diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index f6f3363bf6..66da3d7328 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -2267,6 +2267,32 @@ test "zig fmt: file ends with struct field" { ); } +test "zig fmt: comments at several places in struct init" { + try testTransform( + \\var bar = Bar{ + \\ .x = 10, // test + \\ .y = "test" + \\ // test + \\}; + \\ + , + \\var bar = Bar{ + \\ .x = 10, // test + \\ .y = "test", // test + \\}; + \\ + ); + + try testCanonical( + \\var bar = Bar{ // test + \\ .x = 10, // test + \\ .y = "test", + \\ // test + \\}; + \\ + ); +} + const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; diff --git a/std/zig/render.zig b/std/zig/render.zig index b85c11c6ac..6739db4285 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -2020,7 +2020,13 @@ fn renderTokenOffset( const after_comment_token = tree.tokens.at(token_index + offset); const next_line_indent = switch (after_comment_token.id) { - Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => indent - indent_delta, + Token.Id.RParen, Token.Id.RBrace, Token.Id.RBracket => blk: { + if (indent > indent_delta) { + break :blk indent - indent_delta; + } else { + break :blk 0; + } + }, else => indent, }; try stream.writeByteNTimes(' ', next_line_indent); diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 7bd5c537d3..4539e1e5b2 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -77,6 +77,7 @@ pub const Token = struct { pub const Id = enum { Invalid, + Invalid_ampersands, Identifier, StringLiteral, MultilineStringLiteralLine, @@ -484,6 +485,11 @@ pub const Tokenizer = struct { }, State.Ampersand => switch (c) { + '&' => { + result.id = Token.Id.Invalid_ampersands; + self.index += 1; + break; + }, '=' => { result.id = Token.Id.AmpersandEqual; self.index += 1; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index fd365235d8..a4bc2a66f0 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -201,7 +201,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.OutOfMemory; \\} , - "tmp.zig:2:7: error: error is discarded", + "tmp.zig:2:12: error: error is discarded", ); cases.add( @@ -234,7 +234,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "usingnamespace with wrong type", - \\use void; + \\usingnamespace void; , "tmp.zig:1:1: error: expected struct, enum, or union; found 'void'", ); @@ -2740,7 +2740,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ 3 = 3; \\} , - "tmp.zig:2:7: error: cannot assign to constant", + "tmp.zig:2:9: error: cannot assign to constant", ); cases.add( @@ -2750,7 +2750,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a = 4; \\} , - "tmp.zig:3:7: error: cannot assign to constant", + "tmp.zig:3:9: error: cannot assign to constant", ); cases.add( @@ -2820,7 +2820,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\export fn entry() void { f(); } , - "tmp.zig:3:7: error: cannot assign to constant", + "tmp.zig:3:9: error: cannot assign to constant", ); cases.add( @@ -3883,7 +3883,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - "tmp.zig:6:24: error: unable to evaluate constant expression", + "tmp.zig:6:26: error: unable to evaluate constant expression", "tmp.zig:4:17: note: called from here", ); @@ -4133,7 +4133,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ cstr[0] = 'W'; \\} , - "tmp.zig:3:11: error: cannot assign to constant", + "tmp.zig:3:13: error: cannot assign to constant", ); cases.add( @@ -4143,7 +4143,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ cstr[0] = 'W'; \\} , - "tmp.zig:3:11: error: cannot assign to constant", + "tmp.zig:3:13: error: cannot assign to constant", ); cases.add( @@ -4291,7 +4291,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ f.field = 0; \\} , - "tmp.zig:6:13: error: cannot assign to constant", + "tmp.zig:6:15: error: cannot assign to constant", ); cases.add( diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index db8fdcf368..71af5586ed 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -93,6 +93,7 @@ comptime { _ = @import("behavior/undefined.zig"); _ = @import("behavior/underscore.zig"); _ = @import("behavior/union.zig"); + _ = @import("behavior/usingnamespace.zig"); _ = @import("behavior/var_args.zig"); _ = @import("behavior/vector.zig"); _ = @import("behavior/void.zig"); diff --git a/test/stage1/behavior/atomics.zig b/test/stage1/behavior/atomics.zig index daa463fd45..1a941cf21c 100644 --- a/test/stage1/behavior/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -1,5 +1,6 @@ const std = @import("std"); const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -69,3 +70,33 @@ test "cmpxchg with ptr" { expect(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); expect(x == &data2); } + +// TODO this test is disabled until this issue is resolved: +// https://github.com/ziglang/zig/issues/2883 +// otherwise cross compiling will result in: +// lld: error: undefined symbol: __sync_val_compare_and_swap_16 +//test "128-bit cmpxchg" { +// var x: u128 align(16) = 1234; // TODO: https://github.com/ziglang/zig/issues/2987 +// if (@cmpxchgWeak(u128, &x, 99, 5678, .SeqCst, .SeqCst)) |x1| { +// expect(x1 == 1234); +// } else { +// @panic("cmpxchg should have failed"); +// } +// +// while (@cmpxchgWeak(u128, &x, 1234, 5678, .SeqCst, .SeqCst)) |x1| { +// expect(x1 == 1234); +// } +// expect(x == 5678); +// +// expect(@cmpxchgStrong(u128, &x, 5678, 42, .SeqCst, .SeqCst) == null); +// expect(x == 42); +//} + +test "cmpxchg with ignored result" { + var x: i32 = 1234; + var ptr = &x; + + _ = @cmpxchgStrong(i32, &x, 1234, 5678, .Monotonic, .Monotonic); + + expectEqual(i32(5678), x); +} diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 97d3a269cc..58d662d768 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -1,5 +1,6 @@ const std = @import("std"); const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; const builtin = @import("builtin"); test "compile time recursion" { @@ -794,3 +795,12 @@ test "no undeclared identifier error in unanalyzed branches" { lol_this_doesnt_exist = nonsense; } } + +test "comptime assign int to optional int" { + comptime { + var x: ?i32 = null; + x = 2; + x.? *= 10; + expectEqual(20, x.?); + } +} diff --git a/test/stage1/behavior/fn.zig b/test/stage1/behavior/fn.zig index d6d670b09b..6b9c8b8fe7 100644 --- a/test/stage1/behavior/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -228,3 +228,22 @@ test "implicit cast fn call result to optional in field result" { S.entry(); comptime S.entry(); } + +test "discard the result of a function that returns a struct" { + const S = struct { + fn entry() void { + _ = func(); + } + + fn func() Foo { + return undefined; + } + + const Foo = struct { + a: u64, + b: u64, + }; + }; + S.entry(); + comptime S.entry(); +} diff --git a/test/stage1/behavior/usingnamespace.zig b/test/stage1/behavior/usingnamespace.zig new file mode 100644 index 0000000000..fb45a9392d --- /dev/null +++ b/test/stage1/behavior/usingnamespace.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +fn Foo(comptime T: type) type { + return struct { + usingnamespace T; + }; +} + +test "usingnamespace inside a generic struct" { + const std2 = Foo(std); + const testing2 = Foo(std.testing); + std2.testing.expect(true); + testing2.expect(true); +} diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index 70b47c4590..431e3fe272 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -74,3 +74,9 @@ test "implicit cast vector to array" { S.doTheTest(); comptime S.doTheTest(); } + +test "array to vector" { + var foo: f32 = 3.14; + var arr = [4]f32{ foo, 1.5, 0.0, 0.0 }; + var vec: @Vector(4, f32) = arr; +} diff --git a/test/translate_c.zig b/test/translate_c.zig index d2a5b72b2b..672075e3b6 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1780,6 +1780,40 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} ); + cases.addC("escape sequences", + \\const char *escapes() { + \\char a = '\'', + \\ b = '\\', + \\ c = '\a', + \\ d = '\b', + \\ e = '\f', + \\ f = '\n', + \\ g = '\r', + \\ h = '\t', + \\ i = '\v', + \\ j = '\0', + \\ k = '\"'; + \\ return "\'\\\a\b\f\n\r\t\v\0\""; + \\} + \\ + , + \\pub export fn escapes() [*c]const u8 { + \\ var a: u8 = u8('\''); + \\ var b: u8 = u8('\\'); + \\ var c: u8 = u8('\x07'); + \\ var d: u8 = u8('\x08'); + \\ var e: u8 = u8('\x0c'); + \\ var f: u8 = u8('\n'); + \\ var g: u8 = u8('\r'); + \\ var h: u8 = u8('\t'); + \\ var i: u8 = u8('\x0b'); + \\ var j: u8 = u8('\x00'); + \\ var k: u8 = u8('\"'); + \\ return c"\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; + \\} + \\ + ); + /////////////// Cases for only stage1 because stage2 behavior is better //////////////// cases.addC("Parameterless function prototypes", \\void foo() {} |
