From cc46c1b9024beefdd82ce8abd07e8849a72db20c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 26 Jan 2021 19:47:15 +0100 Subject: Add tests, fix locals that are created in blocks like loops, and handle all breaks correctly --- test/stage2/wasm.zig | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'test') diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index f522db8809..06ede2d735 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -122,4 +122,96 @@ pub fn addCases(ctx: *TestContext) !void { \\} , "35\n"); } + + { + var case = ctx.exe("wasm conditions", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ if (i > @as(u32, 4)) { + \\ i += 10; + \\ } + \\ return i; + \\} + , "15\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ if (i < @as(u32, 4)) { + \\ i += 10; + \\ } else { + \\ i = 2; + \\ } + \\ return i; + \\} + , "2\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 5; + \\ if (i < @as(u32, 4)) { + \\ i += 10; + \\ } else if(i == @as(u32, 5)) { + \\ i = 20; + \\ } + \\ return i; + \\} + , "20\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 11; + \\ if (i < @as(u32, 4)) { + \\ i += 10; + \\ } else { + \\ if (i > @as(u32, 10)) { + \\ i += 20; + \\ } else { + \\ i = 20; + \\ } + \\ } + \\ return i; + \\} + , "31\n"); + } + + { + var case = ctx.exe("wasm while loops", wasi); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 0; + \\ while(i < @as(u32, 5)){ + \\ i += 1; + \\ } + \\ + \\ return i; + \\} + , "5\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 0; + \\ while(i < @as(u32, 10)){ + \\ var x: u32 = 1; + \\ i += x; + \\ } + \\ return i; + \\} + , "10\n"); + + case.addCompareOutput( + \\export fn _start() u32 { + \\ var i: u32 = 0; + \\ while(i < @as(u32, 10)){ + \\ var x: u32 = 1; + \\ i += x; + \\ if (i == @as(u32, 5)) break; + \\ } + \\ return i; + \\} + , "5\n"); + } } -- cgit v1.2.3 From 1ed8c54cd349497adb264b0502783a6422e4f2d1 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 26 Jan 2021 09:08:05 -0800 Subject: translate-c: add wide string literal support Adds support for wide, UTF-16, and UTF-32 string literals. If used to initialize an incomplete array, the same logic as narrow strings is used. Otherwise they are translated as global "anonymous" arrays of the relevant underlying char type. A dot is used in the name to ensure the generated names do not conflict with any other names in the translated program. For example: ```c void my_fn() { const uint32_t *foo = U"foo"; } ``` becomes: ```zig const @"zig.UTF32_string_2" = [4]c_uint{ '\u{66}', '\u{6f}', '\u{6f}', 0, }; pub export fn my_fn() void { var foo: [*c]const u32 = &@"zig.UTF32_string_2"; } ``` --- src/translate_c.zig | 56 ++++++++++++++++++++++++++++++++++------------- test/run_translated_c.zig | 24 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 15 deletions(-) (limited to 'test') diff --git a/src/translate_c.zig b/src/translate_c.zig index bca9ff3a20..8efac4922f 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -780,7 +780,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co eq_tok = try appendToken(c, .Equal, "="); if (decl_init) |expr| { const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) - transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), type_node) + transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(rp.c, type_node) catch 0) else transExprCoercing(rp, scope, expr, .used, .r_value); init_node = node_or_error catch |err| switch (err) { @@ -1662,7 +1662,7 @@ fn transDeclStmtOne( const eq_token = try appendToken(c, .Equal, "="); var init_node = if (decl_init) |expr| if (expr.getStmtClass() == .StringLiteralClass) - try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) + try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(rp.c, type_node)) else try transExprCoercing(rp, scope, expr, .used, .r_value) else @@ -2059,16 +2059,41 @@ fn transStringLiteral( }; return maybeSuppressResult(rp, scope, result_used, &node.base); }, - .UTF16, .UTF32, .Wide => return revertAndWarn( - rp, - error.UnsupportedTranslation, - @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), - "TODO: support string literal kind {s}", - .{kind}, - ), + .UTF16, .UTF32, .Wide => { + const node = try transWideStringLiteral(rp, scope, stmt); + return maybeSuppressResult(rp, scope, result_used, node); + }, } } +/// Translates a wide string literal as a global "anonymous" array of the relevant-sized +/// integer type + null terminator, and returns an identifier node for it +fn transWideStringLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.StringLiteral) TransError!*ast.Node { + const str_type = @tagName(stmt.getKind()); + const mangle = rp.c.getMangle(); + const name = try std.fmt.allocPrint(rp.c.arena, "zig.{s}_string_{d}", .{ str_type, mangle }); + + const const_tok = try appendToken(rp.c, .Keyword_const, "const"); + const name_tok = try appendIdentifier(rp.c, name); + const eq_tok = try appendToken(rp.c, .Equal, "="); + var semi_tok: ast.TokenIndex = undefined; + + const lit_array = try transStringLiteralAsArray(rp, scope, stmt, stmt.getLength() + 1); + + semi_tok = try appendToken(rp.c, .Semicolon, ";"); + const var_decl_node = try ast.Node.VarDecl.create(rp.c.arena, .{ + .name_token = name_tok, + .mut_token = const_tok, + .semicolon_token = semi_tok, + }, .{ + .visib_token = null, + .eq_token = eq_tok, + .init_node = lit_array, + }); + try addTopLevelDecl(rp.c, name, &var_decl_node.base); + return transCreateNodeIdentifier(rp.c, name); +} + /// Parse the size of an array back out from an ast Node. fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { if (node.castTag(.ArrayType)) |array| { @@ -2081,17 +2106,18 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { } /// Translate a string literal to an array of integers. Used when an -/// array is initialized from a string literal. `target_node` is the -/// array being initialized. If the string literal is larger than the -/// array, truncate the string. If the array is larger than the string -/// literal, pad the array with 0's +/// array is initialized from a string literal. `array_size` is the +/// size of the array being initialized. If the string literal is larger +/// than the array, truncate the string. If the array is larger than the +/// string literal, pad the array with 0's fn transStringLiteralAsArray( rp: RestorePoint, scope: *Scope, stmt: *const clang.StringLiteral, - target_node: *ast.Node, + array_size: usize, ) TransError!*ast.Node { - const array_size = try zigArraySize(rp.c, target_node); + if (array_size == 0) return error.UnsupportedType; + const str_length = stmt.getLength(); const expr_base = @ptrCast(*const clang.Expr, stmt); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index e3f9d6b132..a99271eb41 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -794,4 +794,28 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Wide, UTF-16, and UTF-32 string literals", + \\#include + \\#include + \\#include + \\int main(void) { + \\ const wchar_t *wide_str = L"wide"; + \\ const wchar_t wide_hello[] = L"hello"; + \\ if (wcslen(wide_str) != 4) abort(); + \\ if (wcslen(L"literal") != 7) abort(); + \\ if (wcscmp(wide_hello, L"hello") != 0) abort(); + \\ + \\ const uint16_t *u16_str = u"wide"; + \\ const uint16_t u16_hello[] = u"hello"; + \\ if (u16_str[3] != u'e' || u16_str[4] != 0) abort(); + \\ if (u16_hello[4] != u'o' || u16_hello[5] != 0) abort(); + \\ + \\ const uint32_t *u32_str = U"wide"; + \\ const uint32_t u32_hello[] = U"hello"; + \\ if (u32_str[3] != U'e' || u32_str[4] != 0) abort(); + \\ if (u32_hello[4] != U'o' || u32_hello[5] != 0) abort(); + \\ return 0; + \\} + , ""); } -- cgit v1.2.3 From f9b85c6e50e22153aff4c87e2ba943d68f24ee8c Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Mon, 18 Jan 2021 00:36:37 -0500 Subject: stage1: add error for slice.len incr beyond bounds comptime direct slice.len increment dodges bounds checking but we can emit an error for it, at least in the simple case. - promote original assert to compile-error - add test case closes #7810 --- src/stage1/ir.cpp | 7 ++++--- test/compile_errors.zig | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'test') diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index e39e44c3fd..9fd6f15873 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -22655,9 +22655,10 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != ConstArraySpecialBuf) { - ir_assert(new_index < - ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, - &elem_ptr_instruction->base.base); + if (new_index >= ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len) { + ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf("out of bounds slice")); + return ira->codegen->invalid_inst_gen; + } } out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f936eb49a7..ef87acf538 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -7981,6 +7981,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:7:37: note: referenced here", }); + // issue #7810 + cases.add("comptime slice-len increment beyond bounds", + \\export fn foo_slice_len_increment_beyond_bounds() void { + \\ comptime { + \\ var buf_storage: [8]u8 = undefined; + \\ var buf: []const u8 = buf_storage[0..]; + \\ buf.len += 1; + \\ buf[8] = 42; + \\ } + \\} + , &[_][]const u8{ + ":6:12: error: out of bounds slice", + }); + cases.add("comptime slice-sentinel is out of bounds (unterminated)", \\export fn foo_array() void { \\ comptime { -- cgit v1.2.3 From 0b5f3c2ef96df02341cdf54f6eefb3cdb88781d8 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Mon, 11 Jan 2021 11:19:24 -0700 Subject: Replace @TagType uses, mostly with std.meta.Tag --- doc/langref.html.in | 6 ++--- lib/std/builtin.zig | 2 +- lib/std/c/ast.zig | 2 +- lib/std/c/parse.zig | 6 ++--- lib/std/c/tokenizer.zig | 4 ++-- lib/std/hash/auto_hash.zig | 2 +- lib/std/json.zig | 4 ++-- lib/std/meta.zig | 10 ++++----- lib/std/meta/trailer_flags.zig | 2 +- lib/std/testing.zig | 6 ++--- lib/std/zig/parser_test.zig | 2 +- src/DepTokenizer.zig | 4 ++-- src/link/MachO/commands.zig | 2 +- src/stage1/analyze.cpp | 2 +- src/test.zig | 4 ++-- src/translate_c.zig | 2 +- src/type.zig | 2 +- src/value.zig | 2 +- test/compile_errors.zig | 20 ++++------------- test/runtime_safety.zig | 2 +- test/stage1/behavior/bugs/1322.zig | 4 ++-- test/stage1/behavior/enum.zig | 15 +++++++------ test/stage1/behavior/type_info.zig | 2 +- test/stage1/behavior/union.zig | 45 +++++++++++++++++++------------------- test/tests.zig | 2 +- tools/process_headers.zig | 2 +- 26 files changed, 73 insertions(+), 83 deletions(-) (limited to 'test') diff --git a/doc/langref.html.in b/doc/langref.html.in index 3af4d7d2b1..f75fc351d9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2909,15 +2909,15 @@ test "enum variant switch" { expect(mem.eql(u8, what_is_it, "this is a number")); } -// @TagType can be used to access the integer tag type of an enum. +// @typeInfo can be used to access the integer tag type of an enum. const Small = enum { one, two, three, four, }; -test "@TagType" { - expect(@TagType(Small) == u2); +test "std.meta.Tag" { + expect(@typeInfo(Small).Enum.tag_type == u2); } // @typeInfo tells us the field count and the fields names: diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index c883e03ba9..7163cc5357 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -175,7 +175,7 @@ pub const SourceLocation = struct { column: u32, }; -pub const TypeId = @TagType(TypeInfo); +pub const TypeId = std.meta.Tag(TypeInfo); /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig index 207fe8eac8..71455c0ea3 100644 --- a/lib/std/c/ast.zig +++ b/lib/std/c/ast.zig @@ -110,7 +110,7 @@ pub const Error = union(enum) { pub const ExpectedToken = struct { token: TokenIndex, - expected_id: @TagType(Token.Id), + expected_id: std.meta.Tag(Token.Id), pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { const found_token = tree.tokens.at(self.token); diff --git a/lib/std/c/parse.zig b/lib/std/c/parse.zig index 17c07611ab..3d17938d7a 100644 --- a/lib/std/c/parse.zig +++ b/lib/std/c/parse.zig @@ -26,7 +26,7 @@ pub const Options = struct { None, /// Some warnings are errors - Some: []@TagType(ast.Error), + Some: []std.meta.Tag(ast.Error), /// All warnings are errors All, @@ -1363,7 +1363,7 @@ const Parser = struct { return &node.base; } - fn eatToken(parser: *Parser, id: @TagType(Token.Id)) ?TokenIndex { + fn eatToken(parser: *Parser, id: std.meta.Tag(Token.Id)) ?TokenIndex { while (true) { switch ((parser.it.next() orelse return null).id) { .LineComment, .MultiLineComment, .Nl => continue, @@ -1377,7 +1377,7 @@ const Parser = struct { } } - fn expectToken(parser: *Parser, id: @TagType(Token.Id)) Error!TokenIndex { + fn expectToken(parser: *Parser, id: std.meta.Tag(Token.Id)) Error!TokenIndex { while (true) { switch ((parser.it.next() orelse return error.ParseError).id) { .LineComment, .MultiLineComment, .Nl => continue, diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig index ea5774fe4c..2e1969e269 100644 --- a/lib/std/c/tokenizer.zig +++ b/lib/std/c/tokenizer.zig @@ -131,7 +131,7 @@ pub const Token = struct { Keyword_error, Keyword_pragma, - pub fn symbol(id: @TagType(Id)) []const u8 { + pub fn symbol(id: std.meta.TagType(Id)) []const u8 { return switch (id) { .Invalid => "Invalid", .Eof => "Eof", @@ -347,7 +347,7 @@ pub const Token = struct { pub const Tokenizer = struct { buffer: []const u8, index: usize = 0, - prev_tok_id: @TagType(Token.Id) = .Invalid, + prev_tok_id: std.meta.TagType(Token.Id) = .Invalid, pp_directive: bool = false, pub fn next(self: *Tokenizer) Token { diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 8b5852c4af..4afc2b425b 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -239,7 +239,7 @@ fn testHashDeepRecursive(key: anytype) u64 { test "typeContainsSlice" { comptime { - testing.expect(!typeContainsSlice(@TagType(std.builtin.TypeInfo))); + testing.expect(!typeContainsSlice(meta.Tag(std.builtin.TypeInfo))); testing.expect(typeContainsSlice([]const u8)); testing.expect(!typeContainsSlice(u8)); diff --git a/lib/std/json.zig b/lib/std/json.zig index a97044a4d8..dc23155a5e 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -246,7 +246,7 @@ pub const StreamingParser = struct { // Only call this function to generate array/object final state. pub fn fromInt(x: anytype) State { debug.assert(x == 0 or x == 1); - const T = @TagType(State); + const T = std.meta.Tag(State); return @intToEnum(State, @intCast(T, x)); } }; @@ -1782,7 +1782,7 @@ test "parseFree descends into tagged union" { }; // use a string with unicode escape so we know result can't be a reference to global constant const r = try parse(T, &TokenStream.init("\"with\\u0105unicode\""), options); - testing.expectEqual(@TagType(T).string, @as(@TagType(T), r)); + testing.expectEqual(std.meta.Tag(T).string, @as(std.meta.Tag(T), r)); testing.expectEqualSlices(u8, "withÄ…unicode", r.string); testing.expectEqual(@as(usize, 0), fail_alloc.deallocations); parseFree(T, r, options); diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 9a1215e79b..30f69ae9a5 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -606,7 +606,7 @@ pub const TagType = Tag; pub fn Tag(comptime T: type) type { return switch (@typeInfo(T)) { .Enum => |info| info.tag_type, - .Union => |info| if (info.tag_type) |TheTag| TheTag else null, + .Union => |info| info.tag_type orelse @compileError(@typeName(T) ++ " has no tag type"), else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"), }; } @@ -626,9 +626,9 @@ test "std.meta.Tag" { } ///Returns the active tag of a tagged union -pub fn activeTag(u: anytype) @TagType(@TypeOf(u)) { +pub fn activeTag(u: anytype) Tag(@TypeOf(u)) { const T = @TypeOf(u); - return @as(@TagType(T), u); + return @as(Tag(T), u); } test "std.meta.activeTag" { @@ -653,11 +653,11 @@ const TagPayloadType = TagPayload; ///Given a tagged union type, and an enum, return the type of the union /// field corresponding to the enum tag. -pub fn TagPayload(comptime U: type, tag: @TagType(U)) type { +pub fn TagPayload(comptime U: type, tag: Tag(U)) type { testing.expect(trait.is(.Union)(U)); const info = @typeInfo(U).Union; - const tag_info = @typeInfo(@TagType(U)).Enum; + const tag_info = @typeInfo(Tag(U)).Enum; inline for (info.fields) |field_info| { if (comptime mem.eql(u8, field_info.name, @tagName(tag))) diff --git a/lib/std/meta/trailer_flags.zig b/lib/std/meta/trailer_flags.zig index a5882d9e1b..1697e9fe43 100644 --- a/lib/std/meta/trailer_flags.zig +++ b/lib/std/meta/trailer_flags.zig @@ -146,7 +146,7 @@ test "TrailerFlags" { b: bool, c: u64, }); - testing.expectEqual(u2, @TagType(Flags.FieldEnum)); + testing.expectEqual(u2, meta.Tag(Flags.FieldEnum)); var flags = Flags.init(.{ .b = true, diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 26938367e9..8df05ba7fe 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -119,10 +119,10 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { @compileError("Unable to compare untagged union values"); } - const TagType = @TagType(@TypeOf(expected)); + const Tag = std.meta.Tag(@TypeOf(expected)); - const expectedTag = @as(TagType, expected); - const actualTag = @as(TagType, actual); + const expectedTag = @as(Tag, expected); + const actualTag = @as(Tag, actual); expectEqual(expectedTag, actualTag); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index d7cc1208a2..2f0b7ff082 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3822,7 +3822,7 @@ fn testCanonical(source: []const u8) !void { return testTransform(source, source); } -const Error = @TagType(std.zig.ast.Error); +const Error = std.meta.Tag(std.zig.ast.Error); fn testError(source: []const u8, expected_errors: []const Error) !void { const tree = try std.zig.parse(std.testing.allocator, source); diff --git a/src/DepTokenizer.zig b/src/DepTokenizer.zig index b246a22bd0..0bd2999719 100644 --- a/src/DepTokenizer.zig +++ b/src/DepTokenizer.zig @@ -266,11 +266,11 @@ pub fn next(self: *Tokenizer) ?Token { unreachable; } -fn errorPosition(comptime id: @TagType(Token), index: usize, bytes: []const u8) Token { +fn errorPosition(comptime id: std.meta.Tag(Token), index: usize, bytes: []const u8) Token { return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes }); } -fn errorIllegalChar(comptime id: @TagType(Token), index: usize, char: u8) Token { +fn errorIllegalChar(comptime id: std.meta.Tag(Token), index: usize, char: u8) Token { return @unionInit(Token, @tagName(id), .{ .index = index, .char = char }); } diff --git a/src/link/MachO/commands.zig b/src/link/MachO/commands.zig index baea36b4e6..67b808d856 100644 --- a/src/link/MachO/commands.zig +++ b/src/link/MachO/commands.zig @@ -140,7 +140,7 @@ pub const LoadCommand = union(enum) { } fn eql(self: LoadCommand, other: LoadCommand) bool { - if (@as(@TagType(LoadCommand), self) != @as(@TagType(LoadCommand), other)) return false; + if (@as(meta.Tag(LoadCommand), self) != @as(meta.Tag(LoadCommand), other)) return false; return switch (self) { .DyldInfoOnly => |x| meta.eql(x, other.DyldInfoOnly), .Symtab => |x| meta.eql(x, other.Symtab), diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp index 6bc97d323a..c701abce8a 100644 --- a/src/stage1/analyze.cpp +++ b/src/stage1/analyze.cpp @@ -3267,7 +3267,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { tag_type = new_type_table_entry(ZigTypeIdEnum); buf_resize(&tag_type->name, 0); - buf_appendf(&tag_type->name, "@typeInfo(%s).Enum.tag_type", buf_ptr(&union_type->name)); + buf_appendf(&tag_type->name, "@typeInfo(%s).Union.tag_type.?", buf_ptr(&union_type->name)); tag_type->llvm_type = tag_int_type->llvm_type; tag_type->llvm_di_type = tag_int_type->llvm_di_type; tag_type->abi_size = tag_int_type->abi_size; diff --git a/src/test.zig b/src/test.zig index 150b6496c1..07eb001e14 100644 --- a/src/test.zig +++ b/src/test.zig @@ -750,7 +750,7 @@ pub const TestContext = struct { for (actual_errors.list) |actual_error| { for (case_error_list) |case_msg, i| { - const ex_tag: @TagType(@TypeOf(case_msg)) = case_msg; + const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; switch (actual_error) { .src => |actual_msg| { for (actual_msg.notes) |*note| { @@ -789,7 +789,7 @@ pub const TestContext = struct { } while (notes_to_check.popOrNull()) |note| { for (case_error_list) |case_msg, i| { - const ex_tag: @TagType(@TypeOf(case_msg)) = case_msg; + const ex_tag: std.meta.Tag(@TypeOf(case_msg)) = case_msg; switch (note.*) { .src => |actual_msg| { for (actual_msg.notes) |*sub_note| { diff --git a/src/translate_c.zig b/src/translate_c.zig index 8efac4922f..11dbacefa2 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3288,7 +3288,7 @@ const ClangFunctionType = union(enum) { NoProto: *const clang.FunctionType, fn getReturnType(self: @This()) clang.QualType { - switch (@as(@TagType(@This()), self)) { + switch (@as(std.meta.Tag(@This()), self)) { .Proto => return self.Proto.getReturnType(), .NoProto => return self.NoProto.getReturnType(), } diff --git a/src/type.zig b/src/type.zig index be61f57c1d..1636407a87 100644 --- a/src/type.zig +++ b/src/type.zig @@ -110,7 +110,7 @@ pub const Type = extern union { pub fn tag(self: Type) Tag { if (self.tag_if_small_enough < Tag.no_payload_count) { - return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough)); + return @intToEnum(Tag, @intCast(std.meta.Tag(Tag), self.tag_if_small_enough)); } else { return self.ptr_otherwise.tag; } diff --git a/src/value.zig b/src/value.zig index 5b0563ca98..50298da682 100644 --- a/src/value.zig +++ b/src/value.zig @@ -223,7 +223,7 @@ pub const Value = extern union { pub fn tag(self: Value) Tag { if (self.tag_if_small_enough < Tag.no_payload_count) { - return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough)); + return @intToEnum(Tag, @intCast(std.meta.Tag(Tag), self.tag_if_small_enough)); } else { return self.ptr_otherwise.tag; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index ef87acf538..3b4eb61195 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -323,7 +323,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ e: E, \\}; \\export fn entry() void { - \\ if (@TagType(E) != u8) @compileError("did not infer u8 tag type"); + \\ if (@typeInfo(E).Enum.tag_type != u8) @compileError("did not infer u8 tag type"); \\ const s: S = undefined; \\} , &[_][]const u8{ @@ -2728,7 +2728,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const InvalidToken = struct {}; \\const ExpectedVarDeclOrFn = struct {}; , &[_][]const u8{ - "tmp.zig:4:9: error: expected type '@TagType(Error)', found 'type'", + "tmp.zig:4:9: error: expected type '@typeInfo(Error).Union.tag_type.?', found 'type'", }); cases.addTest("binary OR operator on error sets", @@ -7462,24 +7462,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:4:5: note: declared here", }); - cases.add("@TagType when union has no attached enum", - \\const Foo = union { - \\ A: i32, - \\}; - \\export fn entry() void { - \\ const x = @TagType(Foo); - \\} - , &[_][]const u8{ - "tmp.zig:5:24: error: union 'Foo' has no tag", - "tmp.zig:1:13: note: consider 'union(enum)' here", - }); - cases.add("non-integer tag type to automatic union enum", \\const Foo = union(enum(f32)) { \\ A: i32, \\}; \\export fn entry() void { - \\ const x = @TagType(Foo); + \\ const x = @typeInfo(Foo).Union.tag_type.?; \\} , &[_][]const u8{ "tmp.zig:1:24: error: expected integer tag type, found 'f32'", @@ -7490,7 +7478,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ A: i32, \\}; \\export fn entry() void { - \\ const x = @TagType(Foo); + \\ const x = @typeInfo(Foo).Union.tag_type.?; \\} , &[_][]const u8{ "tmp.zig:1:19: error: expected enum tag type, found 'u32'", diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 2ab728b580..eb49b2dbc1 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub fn main() void { \\ var u: U = undefined; \\ @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); - \\ var t: @TagType(U) = u; + \\ var t: @typeInfo(U).Union.tag_type.? = u; \\ var n = @tagName(t); \\} ); diff --git a/test/stage1/behavior/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig index 3231a985e7..02ead6afff 100644 --- a/test/stage1/behavior/bugs/1322.zig +++ b/test/stage1/behavior/bugs/1322.zig @@ -13,7 +13,7 @@ const C = struct {}; test "tagged union with all void fields but a meaningful tag" { var a: A = A{ .b = B{ .c = C{} } }; - std.testing.expect(@as(@TagType(B), a.b) == @TagType(B).c); + std.testing.expect(@as(std.meta.Tag(B), a.b) == std.meta.Tag(B).c); a = A{ .b = B.None }; - std.testing.expect(@as(@TagType(B), a.b) == @TagType(B).None); + std.testing.expect(@as(std.meta.Tag(B), a.b) == std.meta.Tag(B).None); } diff --git a/test/stage1/behavior/enum.zig b/test/stage1/behavior/enum.zig index 1d424d6e39..ecb95be8f5 100644 --- a/test/stage1/behavior/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,5 +1,6 @@ const expect = @import("std").testing.expect; const mem = @import("std").mem; +const Tag = @import("std").meta.Tag; test "extern enum" { const S = struct { @@ -827,12 +828,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime expect(@TagType(Small) == u2); + comptime expect(Tag(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime expect(@TagType(Small2) == u2); + comptime expect(Tag(Small2) == u2); } } @@ -905,11 +906,11 @@ fn getC(data: *const BitFieldOfEnums) C { } test "casting enum to its tag type" { - testCastEnumToTagType(Small2.Two); - comptime testCastEnumToTagType(Small2.Two); + testCastEnumTag(Small2.Two); + comptime testCastEnumTag(Small2.Two); } -fn testCastEnumToTagType(value: Small2) void { +fn testCastEnumTag(value: Small2) void { expect(@enumToInt(value) == 1); } @@ -1163,14 +1164,14 @@ test "enum with comptime_int tag type" { Two = 2, Three = 1, }; - comptime expect(@TagType(Enum) == comptime_int); + comptime expect(Tag(Enum) == comptime_int); } test "enum with one member default to u0 tag type" { const E0 = enum { X, }; - comptime expect(@TagType(E0) == u0); + comptime expect(Tag(E0) == u0); } test "tagName on enum literals" { diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index aa5ac89c94..6dec7ca4d2 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -14,7 +14,7 @@ test "type info: tag type, void info" { } fn testBasic() void { - expect(@TagType(TypeInfo) == TypeId); + expect(@typeInfo(TypeInfo).Union.tag_type == TypeId); const void_info = @typeInfo(void); expect(void_info == TypeId.Void); expect(void_info.Void == {}); diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index 63f36e755a..e46b6bb6b9 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -1,6 +1,7 @@ const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +const Tag = std.meta.Tag; const Value = union(enum) { Int: u64, @@ -128,7 +129,7 @@ const MultipleChoice = union(enum(u32)) { test "simple union(enum(u32))" { var x = MultipleChoice.C; expect(x == MultipleChoice.C); - expect(@enumToInt(@as(@TagType(MultipleChoice), x)) == 60); + expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -144,13 +145,13 @@ const MultipleChoice2 = union(enum(u32)) { }; test "union(enum(u32)) with specified and unspecified tag values" { - comptime expect(@TagType(@TagType(MultipleChoice2)) == u32); + comptime expect(Tag(Tag(MultipleChoice2)) == u32); testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - expect(@enumToInt(@as(@TagType(MultipleChoice2), x)) == 60); + expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60); expect(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, @@ -204,11 +205,11 @@ test "union field access gives the enum values" { } test "cast union to tag type of union" { - testCastUnionToTagType(TheUnion{ .B = 1234 }); - comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); + testCastUnionToTag(TheUnion{ .B = 1234 }); + comptime testCastUnionToTag(TheUnion{ .B = 1234 }); } -fn testCastUnionToTagType(x: TheUnion) void { +fn testCastUnionToTag(x: TheUnion) void { expect(@as(TheTag, x) == TheTag.B); } @@ -298,7 +299,7 @@ const TaggedUnionWithAVoid = union(enum) { fn testTaggedUnionInit(x: anytype) bool { const y = TaggedUnionWithAVoid{ .A = x }; - return @as(@TagType(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; + return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A; } pub const UnionEnumNoPayloads = union(enum) { @@ -309,8 +310,8 @@ pub const UnionEnumNoPayloads = union(enum) { test "tagged union with no payloads" { const a = UnionEnumNoPayloads{ .B = {} }; switch (a) { - @TagType(UnionEnumNoPayloads).A => @panic("wrong"), - @TagType(UnionEnumNoPayloads).B => {}, + Tag(UnionEnumNoPayloads).A => @panic("wrong"), + Tag(UnionEnumNoPayloads).B => {}, } } @@ -325,9 +326,9 @@ test "union with only 1 field casted to its enum type" { }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - const Tag = @TagType(Expr); - comptime expect(@TagType(Tag) == u0); - var t = @as(Tag, e); + const ExprTag = Tag(Expr); + comptime expect(Tag(ExprTag) == u0); + var t = @as(ExprTag, e); expect(t == Expr.Literal); } @@ -337,17 +338,17 @@ test "union with only 1 field casted to its enum type which has enum value speci Bool: bool, }; - const Tag = enum(comptime_int) { + const ExprTag = enum(comptime_int) { Literal = 33, }; - const Expr = union(Tag) { + const Expr = union(ExprTag) { Literal: Literal, }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime expect(@TagType(Tag) == comptime_int); - var t = @as(Tag, e); + comptime expect(Tag(ExprTag) == comptime_int); + var t = @as(ExprTag, e); expect(t == Expr.Literal); expect(@enumToInt(t) == 33); comptime expect(@enumToInt(t) == 33); @@ -501,7 +502,7 @@ test "union with one member defaults to u0 tag type" { const U0 = union(enum) { X: u32, }; - comptime expect(@TagType(@TagType(U0)) == u0); + comptime expect(Tag(Tag(U0)) == u0); } test "union with comptime_int tag" { @@ -510,7 +511,7 @@ test "union with comptime_int tag" { Y: u16, Z: u8, }; - comptime expect(@TagType(@TagType(Union)) == comptime_int); + comptime expect(Tag(Tag(Union)) == comptime_int); } test "extern union doesn't trigger field check at comptime" { @@ -591,7 +592,7 @@ test "function call result coerces from tagged union to the tag" { Two: usize, }; - const ArchTag = @TagType(Arch); + const ArchTag = Tag(Arch); fn doTheTest() void { var x: ArchTag = getArch1(); @@ -696,8 +697,8 @@ test "cast from pointer to anonymous struct to pointer to union" { test "method call on an empty union" { const S = struct { - const MyUnion = union(Tag) { - pub const Tag = enum { X1, X2 }; + const MyUnion = union(MyUnionTag) { + pub const MyUnionTag = enum { X1, X2 }; X1: [0]u8, X2: [0]u8, @@ -797,7 +798,7 @@ test "union enum type gets a separate scope" { }; fn doTheTest() void { - expect(!@hasDecl(@TagType(U), "foo")); + expect(!@hasDecl(Tag(U), "foo")); } }; diff --git a/test/tests.zig b/test/tests.zig index ec6d9e1df8..a0a50d29a5 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -499,7 +499,7 @@ pub fn addPkgTests( if (skip_single_threaded and test_target.single_threaded) continue; - const ArchTag = @TagType(builtin.Arch); + const ArchTag = std.meta.Tag(builtin.Arch); if (test_target.disable_native and test_target.target.getOsTag() == std.Target.current.os.tag and test_target.target.getCpuArch() == std.Target.current.cpu.arch) diff --git a/tools/process_headers.zig b/tools/process_headers.zig index 999b62e715..c5279ba74f 100644 --- a/tools/process_headers.zig +++ b/tools/process_headers.zig @@ -47,7 +47,7 @@ const MultiAbi = union(enum) { fn eql(a: MultiAbi, b: MultiAbi) bool { if (@enumToInt(a) != @enumToInt(b)) return false; - if (@TagType(MultiAbi)(a) != .specific) + if (std.meta.Tag(MultiAbi)(a) != .specific) return true; return a.specific == b.specific; } -- cgit v1.2.3 From 0f5eda973e0c17b3f792cdb06674bf8d2863c8fb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 31 Jan 2021 20:58:11 -0700 Subject: stage2: delete astgen for switch expressions The astgen for switch expressions did not respect the ZIR rules of only referencing instructions that are in scope: %14 = block_comptime_flat({ %15 = block_comptime_flat({ %16 = const(TypedValue{ .ty = comptime_int, .val = 1}) }) %17 = block_comptime_flat({ %18 = const(TypedValue{ .ty = comptime_int, .val = 2}) }) }) %19 = block({ %20 = ref(%5) %21 = deref(%20) %22 = switchbr(%20, [%15, %17], { %15 => { %23 = const(TypedValue{ .ty = comptime_int, .val = 1}) %24 = store(%10, %23) %25 = const(TypedValue{ .ty = void, .val = {}}) %26 = break("label_19", %25) }, %17 => { %27 = const(TypedValue{ .ty = comptime_int, .val = 2}) %28 = store(%10, %27) %29 = const(TypedValue{ .ty = void, .val = {}}) %30 = break("label_19", %29) } }, { %31 = unreachable_safe() }, special_prong=else) }) In this snippet you can see that the comptime expr referenced %15 and %17 which are not in scope. There also was no test coverage for runtime switch expressions. Switch expressions will have to be re-introduced to follow these rules and with some test coverage. There is some usable code being deleted in this commit; it will be useful to reference when re-implementing switch later. A few more improvements to do while we're at it: * only use .ref result loc on switch target if any prongs obtain the payload with |*syntax| - this improvement should be done to if, while, and for as well. - this will remove the needless ref/deref instructions above * remove switchbr and add switch_block, which is both a block and a switch branch. - similarly we should remove loop and add loop_block. This commit introduces a "force_comptime" flag into the GenZIR scope. The main purpose of this will be to choose the "comptime" variants of certain key zir instructions, such as function calls and branches. We will be moving away from using the block_comptime_flat ZIR instruction, and eventually deleting it. This commit also contains miscellaneous fixes to this branch that bring it to the state of passing all the tests. --- src/Module.zig | 13 +- src/astgen.zig | 342 ++++++++------------------------------------------- src/zir.zig | 56 --------- src/zir_sema.zig | 236 +---------------------------------- test/stage2/test.zig | 37 ------ 5 files changed, 72 insertions(+), 612 deletions(-) (limited to 'test') diff --git a/src/Module.zig b/src/Module.zig index 0bafc72e6b..b495afb336 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -375,6 +375,10 @@ pub const Scope = struct { } } + pub fn isComptime(self: *Scope) bool { + return self.getGenZIR().force_comptime; + } + pub fn ownerDecl(self: *Scope) ?*Decl { return switch (self.tag) { .block => self.cast(Block).?.owner_decl, @@ -712,6 +716,7 @@ pub const Scope = struct { parent: *Scope, decl: *Decl, arena: *Allocator, + force_comptime: bool, /// The first N instructions in a function body ZIR are arg instructions. instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, label: ?Label = null, @@ -1008,6 +1013,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &fn_type_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer fn_type_scope.instructions.deinit(self.gpa); @@ -1171,6 +1177,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &decl_arena.allocator, .parent = &decl.container.base, + .force_comptime = false, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1369,6 +1376,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &gen_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = false, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1428,6 +1436,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .decl = decl, .arena = &type_scope_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer type_scope.instructions.deinit(self.gpa); @@ -1497,13 +1506,15 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { decl.analysis = .in_progress; - // A comptime decl does not store any value so we can just deinit this arena after analysis is done. + // A comptime decl does not store any value so we can just deinit + // this arena after analysis is done. var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); defer analysis_arena.deinit(); var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &analysis_arena.allocator, .parent = &decl.container.base, + .force_comptime = true, }; defer gen_scope.instructions.deinit(self.gpa); diff --git a/src/astgen.zig b/src/astgen.zig index 446ee98eb4..dfc5f06ddc 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -309,7 +309,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), - .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?), + .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}), .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?), .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}), @@ -334,11 +334,19 @@ fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.C return comptimeExpr(mod, scope, rl, node.expr); } -pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst { - const tree = parent_scope.tree(); - const src = tree.token_locs[node.firstToken()].start; +pub fn comptimeExpr( + mod: *Module, + parent_scope: *Scope, + rl: ResultLoc, + node: *ast.Node, +) InnerError!*zir.Inst { + // If we are already in a comptime scope, no need to make another one. + if (parent_scope.isComptime()) { + return expr(mod, parent_scope, rl, node); + } - // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one. + // Optimization for labeled blocks: don't need to have 2 layers of blocks, + // we can reuse the existing one. if (node.castTag(.LabeledBlock)) |block_node| { return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime); } @@ -348,6 +356,7 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as .parent = parent_scope, .decl = parent_scope.ownerDecl().?, .arena = parent_scope.arena(), + .force_comptime = true, .instructions = .{}, }; defer block_scope.instructions.deinit(mod.gpa); @@ -356,6 +365,9 @@ pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *as // instruction is the block's result value. _ = try expr(mod, &block_scope.base, rl, node); + const tree = parent_scope.tree(); + const src = tree.token_locs[node.firstToken()].start; + const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); @@ -410,7 +422,7 @@ fn breakExpr( try gen_zir.labeled_breaks.append(mod.gpa, br.castTag(.@"break").?); if (have_store_to_block) { - const inst_list = parent_scope.cast(Scope.GenZIR).?.instructions.items; + const inst_list = parent_scope.getGenZIR().instructions.items; const last_inst = inst_list[inst_list.len - 2]; const store_inst = last_inst.castTag(.store_to_block_ptr).?; assert(store_inst.positionals.lhs == gen_zir.rl_ptr.?); @@ -559,6 +571,7 @@ fn labeledBlockExpr( .parent = parent_scope, .decl = parent_scope.ownerDecl().?, .arena = gen_zir.arena, + .force_comptime = parent_scope.isComptime(), .instructions = .{}, // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{ @@ -746,6 +759,7 @@ fn varDecl( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer init_scope.instructions.deinit(mod.gpa); @@ -1107,6 +1121,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer gen_scope.instructions.deinit(mod.gpa); @@ -1343,6 +1358,7 @@ fn orelseCatchExpr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&block_scope, rl); @@ -1367,6 +1383,7 @@ fn orelseCatchExpr( .parent = &block_scope.base, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -1395,6 +1412,7 @@ fn orelseCatchExpr( .parent = &block_scope.base, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -1416,6 +1434,7 @@ fn orelseCatchExpr( then_result, unwrapped_payload, block, + block, ); } @@ -1432,7 +1451,8 @@ fn finishThenElseBlock( else_src: usize, then_result: *zir.Inst, else_result: ?*zir.Inst, - block: *zir.Inst.Block, + main_block: *zir.Inst.Block, + then_break_block: *zir.Inst.Block, ) InnerError!*zir.Inst { // We now have enough information to decide whether the result instruction should // be communicated via result location pointer or break instructions. @@ -1441,42 +1461,42 @@ fn finishThenElseBlock( .break_void => { if (!then_result.tag.isNoReturn()) { _ = try addZirInstTag(mod, &then_scope.base, then_src, .break_void, .{ - .block = block, + .block = then_break_block, }); } if (else_result) |inst| { if (!inst.tag.isNoReturn()) { _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{ - .block = block, + .block = main_block, }); } } else { _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{ - .block = block, + .block = main_block, }); } assert(!strat.elide_store_to_block_ptr_instructions); try copyBodyNoEliding(then_body, then_scope.*); try copyBodyNoEliding(else_body, else_scope.*); - return &block.base; + return &main_block.base; }, .break_operand => { if (!then_result.tag.isNoReturn()) { _ = try addZirInstTag(mod, &then_scope.base, then_src, .@"break", .{ - .block = block, + .block = then_break_block, .operand = then_result, }); } if (else_result) |inst| { if (!inst.tag.isNoReturn()) { _ = try addZirInstTag(mod, &else_scope.base, else_src, .@"break", .{ - .block = block, + .block = main_block, .operand = inst, }); } } else { _ = try addZirInstTag(mod, &else_scope.base, else_src, .break_void, .{ - .block = block, + .block = main_block, }); } if (strat.elide_store_to_block_ptr_instructions) { @@ -1487,8 +1507,8 @@ fn finishThenElseBlock( try copyBodyNoEliding(else_body, else_scope.*); } switch (rl) { - .ref => return &block.base, - else => return rvalue(mod, parent_scope, rl, &block.base), + .ref => return &main_block.base, + else => return rvalue(mod, parent_scope, rl, &main_block.base), } }, } @@ -1643,6 +1663,7 @@ fn boolBinOp( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer block_scope.instructions.deinit(mod.gpa); @@ -1662,6 +1683,7 @@ fn boolBinOp( .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer rhs_scope.instructions.deinit(mod.gpa); @@ -1676,6 +1698,7 @@ fn boolBinOp( .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer const_scope.instructions.deinit(mod.gpa); @@ -1789,6 +1812,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&block_scope, rl); @@ -1813,6 +1837,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -1830,6 +1855,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, + .force_comptime = block_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -1863,6 +1889,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn then_result, else_result, block, + block, ); } @@ -1912,6 +1939,7 @@ fn whileExpr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&loop_scope, rl); @@ -1921,6 +1949,7 @@ fn whileExpr( .parent = &loop_scope.base, .decl = loop_scope.decl, .arena = loop_scope.arena, + .force_comptime = loop_scope.force_comptime, .instructions = .{}, }; defer continue_scope.instructions.deinit(mod.gpa); @@ -1978,6 +2007,7 @@ fn whileExpr( .parent = &continue_scope.base, .decl = continue_scope.decl, .arena = continue_scope.arena, + .force_comptime = continue_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -1992,6 +2022,7 @@ fn whileExpr( .parent = &continue_scope.base, .decl = continue_scope.decl, .arena = continue_scope.arena, + .force_comptime = continue_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -2027,6 +2058,7 @@ fn whileExpr( then_result, else_result, while_block, + cond_block, ); } @@ -2068,6 +2100,7 @@ fn forExpr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; setBlockResultLoc(&loop_scope, rl); @@ -2077,6 +2110,7 @@ fn forExpr( .parent = &loop_scope.base, .decl = loop_scope.decl, .arena = loop_scope.arena, + .force_comptime = loop_scope.force_comptime, .instructions = .{}, }; defer cond_scope.instructions.deinit(mod.gpa); @@ -2134,6 +2168,7 @@ fn forExpr( .parent = &cond_scope.base, .decl = cond_scope.decl, .arena = cond_scope.arena, + .force_comptime = cond_scope.force_comptime, .instructions = .{}, }; defer then_scope.instructions.deinit(mod.gpa); @@ -2174,6 +2209,7 @@ fn forExpr( .parent = &cond_scope.base, .decl = cond_scope.decl, .arena = cond_scope.arena, + .force_comptime = cond_scope.force_comptime, .instructions = .{}, }; defer else_scope.instructions.deinit(mod.gpa); @@ -2206,281 +2242,10 @@ fn forExpr( then_result, else_result, for_block, + cond_block, ); } -fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp { - var cur = node; - while (true) { - switch (cur.tag) { - .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur), - .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr, - else => return null, - } - } -} - -fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { - if (true) { - @panic("TODO reimplement this"); - } - var block_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = scope.ownerDecl().?, - .arena = scope.arena(), - .instructions = .{}, - }; - defer block_scope.instructions.deinit(mod.gpa); - - const tree = scope.tree(); - const switch_src = tree.token_locs[switch_node.switch_token].start; - const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr); - const target = try addZIRUnOp(mod, &block_scope.base, target_ptr.src, .deref, target_ptr); - // Add the switch instruction here so that it comes before any range checks. - const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ - .target_ptr = target_ptr, - .cases = undefined, // populated below - .items = &[_]*zir.Inst{}, // populated below - .else_body = undefined, // populated below - }, .{})).castTag(.switchbr).?; - - var items = std.ArrayList(*zir.Inst).init(mod.gpa); - defer items.deinit(); - var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa); - defer cases.deinit(); - - // Add comptime block containing all prong items first, - const item_block = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{ - .instructions = undefined, // populated below - }); - // then add block containing the switch. - const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), - }); - - // Most result location types can be forwarded directly; however - // if we need to write to a pointer which has an inferred type, - // proper type inference requires peer type resolution on the switch case. - const case_rl: ResultLoc = switch (rl) { - .discard, .none, .ty, .ptr, .ref => rl, - .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, - }; - - var item_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = scope.ownerDecl().?, - .arena = scope.arena(), - .instructions = .{}, - }; - defer item_scope.instructions.deinit(mod.gpa); - - var case_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = block_scope.decl, - .arena = block_scope.arena, - .instructions = .{}, - }; - defer case_scope.instructions.deinit(mod.gpa); - - var else_scope: Scope.GenZIR = .{ - .parent = scope, - .decl = block_scope.decl, - .arena = block_scope.arena, - .instructions = .{}, - }; - defer else_scope.instructions.deinit(mod.gpa); - - // first we gather all the switch items and check else/'_' prongs - var else_src: ?usize = null; - var underscore_src: ?usize = null; - var first_range: ?*zir.Inst = null; - var special_case: ?*ast.Node.SwitchCase = null; - for (switch_node.cases()) |uncasted_case| { - const case = uncasted_case.castTag(.SwitchCase).?; - const case_src = tree.token_locs[case.firstToken()].start; - // reset without freeing to reduce allocations. - case_scope.instructions.items.len = 0; - assert(case.items_len != 0); - - // Check for else/_ prong, those are handled last. - if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { - if (else_src) |src| { - const msg = msg: { - const msg = try mod.errMsg( - scope, - case_src, - "multiple else prongs in switch expression", - .{}, - ); - errdefer msg.destroy(mod.gpa); - try mod.errNote(scope, src, msg, "previous else prong is here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(scope, msg); - } - else_src = case_src; - special_case = case; - continue; - } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and - mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) - { - if (underscore_src) |src| { - const msg = msg: { - const msg = try mod.errMsg( - scope, - case_src, - "multiple '_' prongs in switch expression", - .{}, - ); - errdefer msg.destroy(mod.gpa); - try mod.errNote(scope, src, msg, "previous '_' prong is here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(scope, msg); - } - underscore_src = case_src; - special_case = case; - continue; - } - - if (else_src) |some_else| { - if (underscore_src) |some_underscore| { - const msg = msg: { - const msg = try mod.errMsg( - scope, - switch_src, - "else and '_' prong in switch expression", - .{}, - ); - errdefer msg.destroy(mod.gpa); - try mod.errNote(scope, some_else, msg, "else prong is here", .{}); - try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{}); - break :msg msg; - }; - return mod.failWithOwnedErrorMsg(scope, msg); - } - } - - // If this is a simple one item prong then it is handled by the switchbr. - if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) { - const item = try expr(mod, &item_scope.base, .none, case.items()[0]); - try items.append(item); - try switchCaseExpr(mod, &case_scope.base, case_rl, block, case); - - try cases.append(.{ - .item = item, - .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) }, - }); - continue; - } - - // TODO if the case has few items and no ranges it might be better - // to just handle them as switch prongs. - - // Check if the target matches any of the items. - // 1, 2, 3..6 will result in - // target == 1 or target == 2 or (target >= 3 and target <= 6) - var any_ok: ?*zir.Inst = null; - for (case.items()) |item| { - if (getRangeNode(item)) |range| { - const start = try expr(mod, &item_scope.base, .none, range.lhs); - const end = try expr(mod, &item_scope.base, .none, range.rhs); - const range_src = tree.token_locs[range.op_token].start; - const range_inst = try addZIRBinOp(mod, &item_scope.base, range_src, .switch_range, start, end); - try items.append(range_inst); - if (first_range == null) first_range = range_inst; - - // target >= start and target <= end - const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start); - const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end); - const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok); - - if (any_ok) |some| { - any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok); - } else { - any_ok = range_ok; - } - continue; - } - - const item_inst = try expr(mod, &item_scope.base, .none, item); - try items.append(item_inst); - const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); - - if (any_ok) |some| { - any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok); - } else { - any_ok = cpm_ok; - } - } - - const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{ - .condition = any_ok.?, - .then_body = undefined, // populated below - .else_body = undefined, // populated below - }, .{}); - const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }); - - // reset cond_scope for then_body - case_scope.instructions.items.len = 0; - try switchCaseExpr(mod, &case_scope.base, case_rl, block, case); - condbr.positionals.then_body = .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }; - - // reset cond_scope for else_body - case_scope.instructions.items.len = 0; - _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{ - .block = cond_block, - }, .{}); - condbr.positionals.else_body = .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }; - } - - // Generate else block or a break last to finish the block. - if (special_case) |case| { - try switchCaseExpr(mod, &else_scope.base, case_rl, block, case); - } else { - // Not handling all possible cases is a compile error. - _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe); - } - - // All items have been generated, add the instructions to the comptime block. - item_block.positionals.body = .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items), - }; - - // Actually populate switch instruction values. - if (else_src != null) switch_inst.kw_args.special_prong = .@"else"; - if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore; - switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items); - switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items); - switch_inst.kw_args.range = first_range; - switch_inst.positionals.else_body = .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), - }; - return &block.base; -} - -fn switchCaseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, case: *ast.Node.SwitchCase) !void { - const tree = scope.tree(); - const case_src = tree.token_locs[case.firstToken()].start; - if (case.payload != null) { - return mod.fail(scope, case_src, "TODO switch case payload capture", .{}); - } - - const case_body = try expr(mod, scope, rl, case.expr); - if (!case_body.tag.isNoReturn()) { - _ = try addZIRInst(mod, scope, case_src, zir.Inst.Break, .{ - .block = block, - .operand = case_body, - }, .{}); - } -} - fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[cfe.ltoken].start; @@ -2859,6 +2624,7 @@ fn asRlPtr( .parent = scope, .decl = scope.ownerDecl().?, .arena = scope.arena(), + .force_comptime = scope.isComptime(), .instructions = .{}, }; defer as_scope.instructions.deinit(mod.gpa); diff --git a/src/zir.zig b/src/zir.zig index 83eee71f87..2559fcdc8e 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -336,12 +336,6 @@ pub const Inst = struct { enum_literal, /// Create an enum type. enum_type, - /// A switch expression. - switchbr, - /// A range in a switch case, `lhs...rhs`. - /// Only checks that `lhs >= rhs` if they are ints, everything else is - /// validated by the .switch instruction. - switch_range, /// Does nothing; returns a void value. void_value, @@ -441,7 +435,6 @@ pub const Inst = struct { .error_union_type, .merge_error_sets, .slice_start, - .switch_range, => BinOp, .block, @@ -478,7 +471,6 @@ pub const Inst = struct { .enum_literal => EnumLiteral, .error_set => ErrorSet, .slice => Slice, - .switchbr => SwitchBr, .typeof_peer => TypeOfPeer, .container_field_named => ContainerFieldNamed, .container_field_typed => ContainerFieldTyped, @@ -605,7 +597,6 @@ pub const Inst = struct { .slice, .slice_start, .import, - .switch_range, .typeof_peer, .resolve_inferred_alloc, .set_eval_branch_quota, @@ -625,7 +616,6 @@ pub const Inst = struct { .unreachable_unsafe, .unreachable_safe, .loop, - .switchbr, .container_field_named, .container_field_typed, .container_field, @@ -1091,32 +1081,6 @@ pub const Inst = struct { }, }; - pub const SwitchBr = struct { - pub const base_tag = Tag.switchbr; - base: Inst, - - positionals: struct { - target_ptr: *Inst, - /// List of all individual items and ranges - items: []*Inst, - cases: []Case, - else_body: Body, - }, - kw_args: struct { - /// Pointer to first range if such exists. - range: ?*Inst = null, - special_prong: enum { - none, - @"else", - underscore, - } = .none, - }, - - pub const Case = struct { - item: *Inst, - body: Body, - }; - }; pub const TypeOfPeer = struct { pub const base_tag = .typeof_peer; base: Inst, @@ -1467,26 +1431,6 @@ const Writer = struct { } try stream.writeByte(']'); }, - []Inst.SwitchBr.Case => { - if (param.len == 0) { - return stream.writeAll("{}"); - } - try stream.writeAll("{\n"); - for (param) |*case, i| { - if (i != 0) { - try stream.writeAll(",\n"); - } - try stream.writeByteNTimes(' ', self.indent); - self.indent += 2; - try self.writeParamToStream(stream, &case.item); - try stream.writeAll(" => "); - try self.writeParamToStream(stream, &case.body); - self.indent -= 2; - } - try stream.writeByte('\n'); - try stream.writeByteNTimes(' ', self.indent - 2); - try stream.writeByte('}'); - }, else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), } } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 773c782746..301b95ad97 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -151,8 +151,6 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .slice => return zirSlice(mod, scope, old_inst.castTag(.slice).?), .slice_start => return zirSliceStart(mod, scope, old_inst.castTag(.slice_start).?), .import => return zirImport(mod, scope, old_inst.castTag(.import).?), - .switchbr => return zirSwitchbr(mod, scope, old_inst.castTag(.switchbr).?), - .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), .bool_and => return zirBoolOp(mod, scope, old_inst.castTag(.bool_and).?), .bool_or => return zirBoolOp(mod, scope, old_inst.castTag(.bool_or).?), .void_value => return mod.constVoid(scope, old_inst.src), @@ -795,6 +793,12 @@ fn analyzeBlockBody( var coerce_block = parent_block.makeSubBlock(); defer coerce_block.instructions.deinit(mod.gpa); const coerced_operand = try mod.coerce(&coerce_block.base, resolved_ty, br.operand); + // If no instructions were produced, such as in the case of a coercion of a + // constant value to a new type, we can simply point the br operand to it. + if (coerce_block.instructions.items.len == 0) { + br.operand = coerced_operand; + continue; + } assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); // Here we depend on the br instruction having been over-allocated (if necessary) // inide analyzeBreak so that it can be converted into a br_block_flat instruction. @@ -1531,234 +1535,6 @@ fn zirSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); } -fn zirSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - const start = try resolveInst(mod, scope, inst.positionals.lhs); - const end = try resolveInst(mod, scope, inst.positionals.rhs); - - switch (start.ty.zigTypeTag()) { - .Int, .ComptimeInt => {}, - else => return mod.constVoid(scope, inst.base.src), - } - switch (end.ty.zigTypeTag()) { - .Int, .ComptimeInt => {}, - else => return mod.constVoid(scope, inst.base.src), - } - if (start.value()) |start_val| { - if (end.value()) |end_val| { - if (start_val.compare(.gte, end_val)) { - return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{}); - } - } - } - return mod.constVoid(scope, inst.base.src); -} - -fn zirSwitchbr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr); - const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src); - try validateSwitch(mod, scope, target, inst); - - if (try mod.resolveDefinedValue(scope, target)) |target_val| { - for (inst.positionals.cases) |case| { - const resolved = try resolveInst(mod, scope, case.item); - const casted = try mod.coerce(scope, target.ty, resolved); - const item = try mod.resolveConstValue(scope, casted); - - if (target_val.eql(item)) { - try analyzeBody(mod, scope.cast(Scope.Block).?, case.body); - return mod.constNoReturn(scope, inst.base.src); - } - } - try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); - return mod.constNoReturn(scope, inst.base.src); - } - - if (inst.positionals.cases.len == 0) { - // no cases just analyze else_branch - try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); - return mod.constNoReturn(scope, inst.base.src); - } - - const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); - const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); - - var case_block: Scope.Block = .{ - .parent = parent_block, - .inst_table = parent_block.inst_table, - .func = parent_block.func, - .owner_decl = parent_block.owner_decl, - .src_decl = parent_block.src_decl, - .instructions = .{}, - .arena = parent_block.arena, - .inlining = parent_block.inlining, - .is_comptime = parent_block.is_comptime, - .branch_quota = parent_block.branch_quota, - }; - defer case_block.instructions.deinit(mod.gpa); - - for (inst.positionals.cases) |case, i| { - // Reset without freeing. - case_block.instructions.items.len = 0; - - const resolved = try resolveInst(mod, scope, case.item); - const casted = try mod.coerce(scope, target.ty, resolved); - const item = try mod.resolveConstValue(scope, casted); - - try analyzeBody(mod, &case_block, case.body); - - cases[i] = .{ - .item = item, - .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, - }; - } - - case_block.instructions.items.len = 0; - try analyzeBody(mod, &case_block, inst.positionals.else_body); - - const else_body: ir.Body = .{ - .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), - }; - - return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body); -} - -fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { - // validate usage of '_' prongs - if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { - return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); - // TODO notes "'_' prong here" inst.positionals.cases[last].src - } - - // check that target type supports ranges - if (inst.kw_args.range) |range_inst| { - switch (target.ty.zigTypeTag()) { - .Int, .ComptimeInt => {}, - else => { - return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty}); - // TODO notes "range used here" range_inst.src - }, - } - } - - // validate for duplicate items/missing else prong - switch (target.ty.zigTypeTag()) { - .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}), - .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), - .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}), - .Int, .ComptimeInt => { - var range_set = @import("RangeSet.zig").init(mod.gpa); - defer range_set.deinit(); - - for (inst.positionals.items) |item| { - const maybe_src = if (item.castTag(.switch_range)) |range| blk: { - const start_resolved = try resolveInst(mod, scope, range.positionals.lhs); - const start_casted = try mod.coerce(scope, target.ty, start_resolved); - const end_resolved = try resolveInst(mod, scope, range.positionals.rhs); - const end_casted = try mod.coerce(scope, target.ty, end_resolved); - - break :blk try range_set.add( - try mod.resolveConstValue(scope, start_casted), - try mod.resolveConstValue(scope, end_casted), - item.src, - ); - } else blk: { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, target.ty, resolved); - const value = try mod.resolveConstValue(scope, casted); - break :blk try range_set.add(value, value, item.src); - }; - - if (maybe_src) |previous_src| { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - // TODO notes "previous value is here" previous_src - } - } - - if (target.ty.zigTypeTag() == .Int) { - var arena = std.heap.ArenaAllocator.init(mod.gpa); - defer arena.deinit(); - - const start = try target.ty.minInt(&arena, mod.getTarget()); - const end = try target.ty.maxInt(&arena, mod.getTarget()); - if (try range_set.spans(start, end)) { - if (inst.kw_args.special_prong == .@"else") { - return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); - } - return; - } - } - - if (inst.kw_args.special_prong != .@"else") { - return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); - } - }, - .Bool => { - var true_count: u8 = 0; - var false_count: u8 = 0; - for (inst.positionals.items) |item| { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); - if ((try mod.resolveConstValue(scope, casted)).toBool()) { - true_count += 1; - } else { - false_count += 1; - } - - if (true_count + false_count > 2) { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - } - } - if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") { - return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); - } - if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") { - return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); - } - }, - .EnumLiteral, .Void, .Fn, .Pointer, .Type => { - if (inst.kw_args.special_prong != .@"else") { - return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); - } - - var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); - defer seen_values.deinit(); - - for (inst.positionals.items) |item| { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, target.ty, resolved); - const val = try mod.resolveConstValue(scope, casted); - - if (try seen_values.fetchPut(val, item.src)) |prev| { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - // TODO notes "previous value here" prev.value - } - } - }, - - .ErrorUnion, - .NoReturn, - .Array, - .Struct, - .Undefined, - .Null, - .Optional, - .BoundFn, - .Opaque, - .Vector, - .Frame, - .AnyFrame, - .ComptimeFloat, - .Float, - => { - return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty}); - }, - } -} - fn zirImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 0d5a52980b..78d7eba262 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -962,43 +962,6 @@ pub fn addCases(ctx: *TestContext) !void { , "hello\nhello\nhello\nhello\nhello\n", ); - - // comptime switch - - // Basic for loop - case.addCompareOutput( - \\pub export fn _start() noreturn { - \\ assert(foo() == 1); - \\ exit(); - \\} - \\ - \\fn foo() u32 { - \\ const a: comptime_int = 1; - \\ var b: u32 = 0; - \\ switch (a) { - \\ 1 => b = 1, - \\ 2 => b = 2, - \\ else => unreachable, - \\ } - \\ return b; - \\} - \\ - \\pub fn assert(ok: bool) void { - \\ if (!ok) unreachable; // assertion failure - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "", - ); } { -- cgit v1.2.3 From bdfe3aeab8310a64cc9c2f5fac194a609aa0f13d Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 26 Jan 2021 21:09:40 +0200 Subject: stage2 cbe: condbr and breaks --- src/codegen/c.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++------ test/stage2/cbe.zig | 18 +++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) (limited to 'test') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 9bccde5ffd..bf6a5aac1f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -41,6 +41,7 @@ pub const Object = struct { value_map: CValueMap, next_arg_index: usize = 0, next_local_index: usize = 0, + next_block_index: usize = 0, indent_writer: std.io.AutoIndentingStream(std.ArrayList(u8).Writer), fn resolveInst(o: *Object, inst: *Inst) !CValue { @@ -255,8 +256,8 @@ pub const DeclGen = struct { .int_signed, .int_unsigned => { const info = t.intInfo(dg.module.getTarget()); const sign_prefix = switch (info.signedness) { - .signed => "i", - .unsigned => "", + .signed => "", + .unsigned => "u", }; inline for (.{ 8, 16, 32, 64, 128 }) |nbits| { if (info.bits <= nbits) { @@ -325,6 +326,7 @@ pub fn genDecl(o: *Object) !void { try o.indent_writer.insertNewline(); try o.dg.renderFunctionSignature(o.writer(), is_global); + try o.writer().writeByte(' '); try genBody(o, func.body); try o.indent_writer.insertNewline(); @@ -372,11 +374,11 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!void { const writer = o.writer(); if (body.instructions.len == 0) { - try writer.writeAll(" {}"); + try writer.writeAll("{}"); return; } - try writer.writeAll(" {\n"); + try writer.writeAll("{\n"); o.indent_writer.pushIndent(); for (body.instructions) |inst| { @@ -404,6 +406,9 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), .unreach => try genUnreach(o, inst.castTag(.unreach).?), .loop => try genLoop(o, inst.castTag(.loop).?), + .condbr => try genCondBr(o, inst.castTag(.condbr).?), + .br => try genBr(o, inst.castTag(.br).?), + .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -579,7 +584,31 @@ fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue { } fn genBlock(o: *Object, inst: *Inst.Block) !CValue { - return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement blocks", .{}); + const block_id: usize = o.next_block_index; + o.next_block_index += 1; + // abuse codegen.msv to store the block's id + inst.codegen.mcv.a = block_id; + try genBody(o, inst.body); + try o.indent_writer.insertNewline(); + // label must be followed by an expression, add an empty one. + try o.writer().print("zig_block_{d}:;\n", .{block_id}); + + // blocks in C cannot result in values + // TODO we need some other way to pass the result of the block + return CValue.none; +} + +fn genBr(o: *Object, inst: *Inst.Br) !CValue { + if (inst.operand.ty.tag() != .void) { + return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement block return values", .{}); + } + + return genBrVoid(o, inst.block); +} + +fn genBrVoid(o: *Object, block: *Inst.Block) !CValue { + try o.writer().print("goto zig_block_{d};\n", .{block.codegen.mcv.a}); + return CValue.none; } fn genBitcast(o: *Object, inst: *Inst.UnOp) !CValue { @@ -622,12 +651,27 @@ fn genUnreach(o: *Object, inst: *Inst.NoOp) !CValue { } fn genLoop(o: *Object, inst: *Inst.Loop) !CValue { - try o.writer().writeAll("while (true)"); + try o.writer().writeAll("while (true) "); try genBody(o, inst.body); try o.indent_writer.insertNewline(); return CValue.none; } +fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue { + const cond = try o.resolveInst(inst.condition); + const writer = o.writer(); + + try writer.writeAll("if ("); + try o.writeCValue(writer, cond); + try writer.writeAll(") "); + try genBody(o, inst.then_body); + try writer.writeAll(" else "); + try genBody(o, inst.else_body); + try o.indent_writer.insertNewline(); + + return CValue.none; +} + fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 6d4e2062bf..c953d6077e 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -133,6 +133,24 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ , ""); + + // Simple while loop + case.addCompareOutput( + \\export fn main() c_int { + \\ var a: c_int = 0; + \\ while (a < 5) : (a+=1) {} + \\ exit(a - 5); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + , ""); } { -- cgit v1.2.3 From 258f3ec5ecf8d2a165382d5837bed0dac2e0375b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 11:05:22 +0200 Subject: stage2 cbe: block results --- src/codegen/c.zig | 38 +++++++++++++++++++++++++++----------- test/stage2/cbe.zig | 21 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 11 deletions(-) (limited to 'test') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index bf6a5aac1f..e33f812f0b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -325,7 +325,7 @@ pub fn genDecl(o: *Object) !void { const func: *Module.Fn = func_payload.data; try o.indent_writer.insertNewline(); try o.dg.renderFunctionSignature(o.writer(), is_global); - + try o.writer().writeByte(' '); try genBody(o, func.body); @@ -586,28 +586,44 @@ fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue { fn genBlock(o: *Object, inst: *Inst.Block) !CValue { const block_id: usize = o.next_block_index; o.next_block_index += 1; - // abuse codegen.msv to store the block's id - inst.codegen.mcv.a = block_id; + const writer = o.writer(); + + // store the block id in relocs.capacity as it is not used for anything else in the C backend. + inst.codegen.relocs.capacity = block_id; + const result = if (inst.base.ty.tag() != .void and !inst.base.isUnused()) blk: { + // allocate a location for the result + const local = try o.allocLocal(inst.base.ty, .Mut); + try writer.writeAll(";\n"); + break :blk local; + } else + CValue{ .none = {} }; + + inst.codegen.mcv = @bitCast(@import("../codegen.zig").AnyMCValue, result); try genBody(o, inst.body); try o.indent_writer.insertNewline(); // label must be followed by an expression, add an empty one. - try o.writer().print("zig_block_{d}:;\n", .{block_id}); - - // blocks in C cannot result in values - // TODO we need some other way to pass the result of the block - return CValue.none; + try writer.print("zig_block_{d}:;\n", .{block_id}); + return result; } fn genBr(o: *Object, inst: *Inst.Br) !CValue { - if (inst.operand.ty.tag() != .void) { - return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement block return values", .{}); + const result = @bitCast(CValue, inst.block.codegen.mcv); + const writer = o.writer(); + + // If result is .none then the value of the block is unused. + if (inst.operand.ty.tag() != .void and result != .none) { + const operand = try o.resolveInst(inst.operand); + try o.writeCValue(writer, result); + try writer.writeAll(" = "); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); } return genBrVoid(o, inst.block); } fn genBrVoid(o: *Object, block: *Inst.Block) !CValue { - try o.writer().print("goto zig_block_{d};\n", .{block.codegen.mcv.a}); + try o.writer().print("goto zig_block_{d};\n", .{block.codegen.relocs.capacity}); return CValue.none; } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index c953d6077e..8a264f5ca6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -151,6 +151,27 @@ pub fn addCases(ctx: *TestContext) !void { \\ unreachable; \\} , ""); + + // If expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = @as(c_int, if (cond == 0) + \\ 2 + \\ else + \\ 3) + 9; + \\ exit(a - 11); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ ); + \\ unreachable; + \\} + , ""); } { -- cgit v1.2.3 From 106520329e5adc6cf5ef83595da6c9d5dd3c4b35 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 11:40:34 +0200 Subject: stage2 cbe: implement switchbr --- src/Module.zig | 4 +-- src/codegen/c.zig | 35 ++++++++++++++++++++++++ src/ir.zig | 4 +-- test/stage2/cbe.zig | 78 ++++++++++++++++++++++++++--------------------------- 4 files changed, 78 insertions(+), 43 deletions(-) (limited to 'test') diff --git a/src/Module.zig b/src/Module.zig index b495afb336..46c3d513f1 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2215,7 +2215,7 @@ pub fn addSwitchBr( self: *Module, block: *Scope.Block, src: usize, - target_ptr: *Inst, + target: *Inst, cases: []Inst.SwitchBr.Case, else_body: ir.Body, ) !*Inst { @@ -2226,7 +2226,7 @@ pub fn addSwitchBr( .ty = Type.initTag(.noreturn), .src = src, }, - .target_ptr = target_ptr, + .target = target, .cases = cases, .else_body = else_body, }; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e33f812f0b..7fcbe44205 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -129,6 +129,9 @@ pub const DeclGen = struct { t: Type, val: Value, ) error{ OutOfMemory, AnalysisFail }!void { + if (val.isUndef()) { + return dg.fail(dg.decl.src(), "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{}); + } switch (t.zigTypeTag()) { .Int => { if (t.isSignedInt()) @@ -196,6 +199,7 @@ pub const DeclGen = struct { }, } }, + .Bool => return writer.print("{}", .{val.toBool()}), else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{ @tagName(e), }), @@ -409,6 +413,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .condbr => try genCondBr(o, inst.castTag(.condbr).?), .br => try genBr(o, inst.castTag(.br).?), .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), + .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), + // booland and boolor are non-short-circuit operations + .booland => try genBinOp(o, inst.castTag(.booland).?, " & "), + .boolor => try genBinOp(o, inst.castTag(.boolor).?, " | "), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -688,6 +696,33 @@ fn genCondBr(o: *Object, inst: *Inst.CondBr) !CValue { return CValue.none; } +fn genSwitchBr(o: *Object, inst: *Inst.SwitchBr) !CValue { + const target = try o.resolveInst(inst.target); + const writer = o.writer(); + + try writer.writeAll("switch ("); + try o.writeCValue(writer, target); + try writer.writeAll(") {\n"); + o.indent_writer.pushIndent(); + + for (inst.cases) |case| { + try writer.writeAll("case "); + try o.dg.renderValue(writer, inst.target.ty, case.item); + try writer.writeAll(": "); + // the case body must be noreturn so we don't need to insert a break + try genBody(o, case.body); + try o.indent_writer.insertNewline(); + } + + try writer.writeAll("default: "); + try genBody(o, inst.else_body); + try o.indent_writer.insertNewline(); + + o.indent_writer.popIndent(); + try writer.writeAll("}\n"); + return CValue.none; +} + fn genAsm(o: *Object, as: *Inst.Assembly) !CValue { if (as.base.isUnused() and !as.is_volatile) return CValue.none; diff --git a/src/ir.zig b/src/ir.zig index 408efc3bba..0e83dbfd56 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -521,7 +521,7 @@ pub const Inst = struct { pub const base_tag = Tag.switchbr; base: Inst, - target_ptr: *Inst, + target: *Inst, cases: []Case, /// Set of instructions whose lifetimes end at the start of one of the cases. /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. @@ -544,7 +544,7 @@ pub const Inst = struct { var i = index; if (i < 1) - return self.target_ptr; + return self.target; i -= 1; return null; diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 8a264f5ca6..aacb2b7077 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -133,45 +133,6 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\ , ""); - - // Simple while loop - case.addCompareOutput( - \\export fn main() c_int { - \\ var a: c_int = 0; - \\ while (a < 5) : (a+=1) {} - \\ exit(a - 5); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - , ""); - - // If expression - case.addCompareOutput( - \\export fn main() c_int { - \\ var cond: c_int = 0; - \\ var a: c_int = @as(c_int, if (cond == 0) - \\ 2 - \\ else - \\ 3) + 9; - \\ exit(a - 11); - \\} - \\ - \\fn exit(code: usize) noreturn { - \\ asm volatile ("syscall" - \\ : - \\ : [number] "{rax}" (231), - \\ [arg1] "{rdi}" (code) - \\ ); - \\ unreachable; - \\} - , ""); } { @@ -224,6 +185,45 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exeFromCompiledC("control flow", .{}); + + // Simple while loop + case.addCompareOutput( + \\export fn main() c_int { + \\ var a: c_int = 0; + \\ while (a < 5) : (a+=1) {} + \\ return a - 5; + \\} + , ""); + + // If expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = @as(c_int, if (cond == 0) + \\ 2 + \\ else + \\ 3) + 9; + \\ return a - 11; + \\} + , ""); + + // Switch expression + case.addCompareOutput( + \\export fn main() c_int { + \\ var cond: c_int = 0; + \\ var a: c_int = switch (cond) { + \\ 1 => 1, + \\ 2 => 2, + \\ 99...300, 12 => 3, + \\ 0 => 4, + \\ else => 5, + \\ }; + \\ return a - 4; + \\} + , ""); + } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { \\ unreachable; -- cgit v1.2.3 From 3ec5c9a3bcae09c01cbe4f0505e6ab03834bbb98 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 27 Jan 2021 12:22:38 +0200 Subject: stage2 cbe: implement not and some bitwise ops --- src/codegen/c.zig | 24 ++++++++++++++++++++++-- test/stage2/cbe.zig | 7 +++++++ 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 7fcbe44205..39fa80ea3d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -293,6 +293,7 @@ pub const DeclGen = struct { try dg.renderType(w, t.elemType()); try w.writeAll(" *"); }, + .Null, .Undefined => unreachable, // must be const or comptime else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{ @tagName(e), }), @@ -387,6 +388,7 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi for (body.instructions) |inst| { const result_value = switch (inst.tag) { + .constant => unreachable, // excluded from function bodies .add => try genBinOp(o, inst.castTag(.add).?, " + "), .alloc => try genAlloc(o, inst.castTag(.alloc).?), .arg => genArg(o), @@ -415,8 +417,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .brvoid => try genBrVoid(o, inst.castTag(.brvoid).?.block), .switchbr => try genSwitchBr(o, inst.castTag(.switchbr).?), // booland and boolor are non-short-circuit operations - .booland => try genBinOp(o, inst.castTag(.booland).?, " & "), - .boolor => try genBinOp(o, inst.castTag(.boolor).?, " | "), + .booland, .bitand => try genBinOp(o, inst.castTag(.booland).?, " & "), + .boolor, .bitor => try genBinOp(o, inst.castTag(.boolor).?, " | "), + .xor => try genBinOp(o, inst.castTag(.xor).?, " ^ "), + .not => try genUnOp(o, inst.castTag(.not).?, "!"), else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}), }; switch (result_value) { @@ -541,6 +545,22 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { return local; } +fn genUnOp(o: *Object, inst: *Inst.UnOp, operator: []const u8) !CValue { + if (inst.base.isUnused()) + return CValue.none; + + const operand = try o.resolveInst(inst.operand); + + const writer = o.writer(); + const local = try o.allocLocal(inst.base.ty, .Const); + + try writer.print(" = {s}", .{operator}); + try o.writeCValue(writer, operand); + try writer.writeAll(";\n"); + + return local; +} + fn genCall(o: *Object, inst: *Inst.Call) !CValue { if (inst.func.castTag(.constant)) |func_inst| { const fn_decl = if (func_inst.val.castTag(.extern_fn)) |extern_fn| diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index aacb2b7077..0eb2cf68b4 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -196,6 +196,13 @@ pub fn addCases(ctx: *TestContext) !void { \\ return a - 5; \\} , ""); + case.addCompareOutput( + \\export fn main() c_int { + \\ var a = true; + \\ while (!a) {} + \\ return 0; + \\} + , ""); // If expression case.addCompareOutput( -- cgit v1.2.3 From 221f1d898c39e9ea25f1d7fc9642bfbb3c97e894 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 5 Feb 2021 15:37:18 -0800 Subject: translate-c: Improve function pointer handling Omit address-of operator if operand is a function. Improve handling of function-call translation when using function pointers Fixes #4124 --- src/clang.zig | 5 ++++- src/translate_c.zig | 56 ++++++++++++++++++++++++++++++++--------------- src/zig_clang.cpp | 5 +++++ src/zig_clang.h | 2 ++ test/run_translated_c.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++ test/translate_c.zig | 4 ++-- 6 files changed, 107 insertions(+), 21 deletions(-) (limited to 'test') diff --git a/src/clang.zig b/src/clang.zig index fc7a25fe12..954cfee6b2 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -848,7 +848,10 @@ pub const UnaryOperator = opaque { extern fn ZigClangUnaryOperator_getBeginLoc(*const UnaryOperator) SourceLocation; }; -pub const ValueDecl = opaque {}; +pub const ValueDecl = opaque { + pub const getType = ZigClangValueDecl_getType; + extern fn ZigClangValueDecl_getType(*const ValueDecl) QualType; +}; pub const VarDecl = opaque { pub const getLocation = ZigClangVarDecl_getLocation; diff --git a/src/translate_c.zig b/src/translate_c.zig index 11dbacefa2..e3652ddbfb 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3208,6 +3208,38 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub return maybeSuppressResult(rp, scope, result_used, &node.base); } +/// Check if an expression is ultimately a reference to a function declaration +/// (which means it should not be unwrapped with `.?` in translated code) +fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { + switch (expr.getStmtClass()) { + .ParenExprClass => { + const op_expr = @ptrCast(*const clang.ParenExpr, expr).getSubExpr(); + return cIsFunctionDeclRef(op_expr); + }, + .DeclRefExprClass => { + const decl_ref = @ptrCast(*const clang.DeclRefExpr, expr); + const value_decl = decl_ref.getDecl(); + const qt = value_decl.getType(); + return qualTypeChildIsFnProto(qt); + }, + .ImplicitCastExprClass => { + const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, expr); + const cast_kind = implicit_cast.getCastKind(); + if (cast_kind == .BuiltinFnToFnPtr) return true; + if (cast_kind == .FunctionToPointerDecay) { + return cIsFunctionDeclRef(implicit_cast.getSubExpr()); + } + return false; + }, + .UnaryOperatorClass => { + const un_op = @ptrCast(*const clang.UnaryOperator, expr); + const opcode = un_op.getOpcode(); + return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr()); + }, + else => return false, + } +} + fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!*ast.Node { const callee = stmt.getCallee(); var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value); @@ -3215,24 +3247,9 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r var is_ptr = false; const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr); - const fn_expr = if (is_ptr and fn_ty != null) blk: { - if (callee.getStmtClass() == .ImplicitCastExprClass) { - const implicit_cast = @ptrCast(*const clang.ImplicitCastExpr, callee); - const cast_kind = implicit_cast.getCastKind(); - if (cast_kind == .BuiltinFnToFnPtr) break :blk raw_fn_expr; - if (cast_kind == .FunctionToPointerDecay) { - const subexpr = implicit_cast.getSubExpr(); - if (subexpr.getStmtClass() == .DeclRefExprClass) { - const decl_ref = @ptrCast(*const clang.DeclRefExpr, subexpr); - const named_decl = decl_ref.getFoundDecl(); - if (@ptrCast(*const clang.Decl, named_decl).getKind() == .Function) { - break :blk raw_fn_expr; - } - } - } - } - break :blk try transCreateNodeUnwrapNull(rp.c, raw_fn_expr); - } else + const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee)) + try transCreateNodeUnwrapNull(rp.c, raw_fn_expr) + else raw_fn_expr; const num_args = stmt.getNumArgs(); @@ -3379,6 +3396,9 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.UnaryO else return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), .AddrOf => { + if (cIsFunctionDeclRef(op_expr)) { + return transExpr(rp, scope, op_expr, used, .r_value); + } const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value); return &op_node.base; diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 9bd68859e8..8dc6a0823b 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2773,6 +2773,11 @@ struct ZigClangSourceLocation ZigClangUnaryOperator_getBeginLoc(const struct Zig return bitcast(casted->getBeginLoc()); } +struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getType()); +} + const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *self) { auto casted = reinterpret_cast(self); return reinterpret_cast(casted->getCond()); diff --git a/src/zig_clang.h b/src/zig_clang.h index 42587b1719..6fe1da0bc1 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1200,6 +1200,8 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryOperator_getType(const struct ZIG_EXTERN_C const struct ZigClangExpr *ZigClangUnaryOperator_getSubExpr(const struct ZigClangUnaryOperator *); ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryOperator_getBeginLoc(const struct ZigClangUnaryOperator *); +ZIG_EXTERN_C struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl *); + ZIG_EXTERN_C const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *); ZIG_EXTERN_C const struct ZigClangStmt *ZigClangWhileStmt_getBody(const struct ZigClangWhileStmt *); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index a99271eb41..a8a3a0e21b 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -818,4 +818,60 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Address of function is no-op", + \\#include + \\#include + \\typedef int (*myfunc)(int); + \\int a(int arg) { return arg + 1;} + \\int b(int arg) { return arg + 2;} + \\int caller(myfunc fn, int arg) { + \\ return fn(arg); + \\} + \\int main() { + \\ myfunc arr[3] = {&a, &b, a}; + \\ myfunc foo = a; + \\ myfunc bar = &(a); + \\ if (foo != bar) abort(); + \\ if (arr[0] == arr[1]) abort(); + \\ if (arr[0] != arr[2]) abort(); + \\ if (caller(b, 40) != 42) abort(); + \\ if (caller(&b, 40) != 42) abort(); + \\ return 0; + \\} + , ""); + + cases.add("Obscure ways of calling functions; issue #4124", + \\#include + \\static int add(int a, int b) { + \\ return a + b; + \\} + \\typedef int (*adder)(int, int); + \\typedef void (*funcptr)(void); + \\int main() { + \\ if ((add)(1, 2) != 3) abort(); + \\ if ((&add)(1, 2) != 3) abort(); + \\ if (add(3, 1) != 4) abort(); + \\ if ((*add)(2, 3) != 5) abort(); + \\ if ((**add)(7, -1) != 6) abort(); + \\ if ((***add)(-2, 9) != 7) abort(); + \\ + \\ int (*ptr)(int a, int b); + \\ ptr = add; + \\ + \\ if (ptr(1, 2) != 3) abort(); + \\ if ((*ptr)(3, 1) != 4) abort(); + \\ if ((**ptr)(2, 3) != 5) abort(); + \\ if ((***ptr)(7, -1) != 6) abort(); + \\ if ((****ptr)(-2, 9) != 7) abort(); + \\ + \\ funcptr addr1 = (funcptr)(add); + \\ funcptr addr2 = (funcptr)(&add); + \\ + \\ if (addr1 != addr2) abort(); + \\ if (((int(*)(int, int))addr1)(1, 2) != 3) abort(); + \\ if (((adder)addr2)(1, 2) != 3) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 10ac76a2c5..75d00d12f4 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -2802,8 +2802,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ fn_f64(3); \\ fn_bool(@as(c_int, 123) != 0); \\ fn_bool(@as(c_int, 0) != 0); - \\ fn_bool(@ptrToInt(&fn_int) != 0); - \\ fn_int(@intCast(c_int, @ptrToInt(&fn_int))); + \\ fn_bool(@ptrToInt(fn_int) != 0); + \\ fn_int(@intCast(c_int, @ptrToInt(fn_int))); \\ fn_ptr(@intToPtr(?*c_void, @as(c_int, 42))); \\} }); -- cgit v1.2.3 From 1480c428065c01c6feff22ce84021c2e0e30aa9b Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 3 Jan 2021 02:20:37 -0700 Subject: require specifier for arrayish types --- lib/std/build.zig | 2 +- lib/std/fmt.zig | 85 ++++++++++++++++++++++++++++---------------- lib/std/testing.zig | 14 ++++---- src/Module.zig | 2 +- src/codegen.zig | 2 +- src/zir_sema.zig | 2 +- test/cli.zig | 8 ++--- test/standalone/cat/main.zig | 2 +- 8 files changed, 71 insertions(+), 46 deletions(-) (limited to 'test') diff --git a/lib/std/build.zig b/lib/std/build.zig index 0db9d4c24e..77ca854f15 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -739,7 +739,7 @@ pub const Builder = struct { return args.default_target; }, else => |e| { - warn("Unable to parse target '{}': {s}\n\n", .{ triple, @errorName(e) }); + warn("Unable to parse target '{s}': {s}\n\n", .{ triple, @errorName(e) }); self.markInvalidUserInput(); return args.default_target; }, diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 27e68ee9c1..c1b24cc6da 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -69,6 +69,7 @@ pub const FormatOptions = struct { /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. /// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max. /// - `*`: output the address of the value instead of the value itself. +/// - `any`: output a value of any type using its default format /// /// If a formatted user type contains a function of the type /// ``` @@ -387,17 +388,32 @@ pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @T return; } }, - .Array => |info| { - try writer.writeAll(@typeName(info.child) ++ "@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); - return; - }, else => {}, } @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); } +// This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948 +const ANY = "any"; + +fn defaultSpec(comptime T: type) [:0]const u8 { + switch (@typeInfo(T)) { + .Array => |_| return ANY, + .Pointer => |ptr_info| switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { + .Array => |_| return "*", + else => {}, + }, + .Many, .C => return "*", + .Slice => return ANY, + }, + .Optional => |info| return defaultSpec(info.child), + else => {}, + } + return ""; +} + pub fn formatType( value: anytype, comptime fmt: []const u8, @@ -405,18 +421,19 @@ pub fn formatType( writer: anytype, max_depth: usize, ) @TypeOf(writer).Error!void { - if (comptime std.mem.eql(u8, fmt, "*")) { + const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) defaultSpec(@TypeOf(value)) else fmt; + if (comptime std.mem.eql(u8, actual_fmt, "*")) { return formatAddress(value, options, writer); } const T = @TypeOf(value); if (comptime std.meta.trait.hasFn("format")(T)) { - return try value.format(fmt, options, writer); + return try value.format(actual_fmt, options, writer); } switch (@typeInfo(T)) { .ComptimeInt, .Int, .ComptimeFloat, .Float => { - return formatValue(value, fmt, options, writer); + return formatValue(value, actual_fmt, options, writer); }, .Void => { return formatBuf("void", options, writer); @@ -426,16 +443,16 @@ pub fn formatType( }, .Optional => { if (value) |payload| { - return formatType(payload, fmt, options, writer, max_depth); + return formatType(payload, actual_fmt, options, writer, max_depth); } else { return formatBuf("null", options, writer); } }, .ErrorUnion => { if (value) |payload| { - return formatType(payload, fmt, options, writer, max_depth); + return formatType(payload, actual_fmt, options, writer, max_depth); } else |err| { - return formatType(err, fmt, options, writer, max_depth); + return formatType(err, actual_fmt, options, writer, max_depth); } }, .ErrorSet => { @@ -461,7 +478,7 @@ pub fn formatType( } try writer.writeAll("("); - try formatType(@enumToInt(value), fmt, options, writer, max_depth); + try formatType(@enumToInt(value), actual_fmt, options, writer, max_depth); try writer.writeAll(")"); }, .Union => |info| { @@ -475,7 +492,7 @@ pub fn formatType( try writer.writeAll(" = "); inline for (info.fields) |u_field| { if (value == @field(UnionTagType, u_field.name)) { - try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1); + try formatType(@field(value, u_field.name), ANY, options, writer, max_depth - 1); } } try writer.writeAll(" }"); @@ -497,48 +514,54 @@ pub fn formatType( } try writer.writeAll(f.name); try writer.writeAll(" = "); - try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1); + try formatType(@field(value, f.name), ANY, options, writer, max_depth - 1); } try writer.writeAll(" }"); }, .Pointer => |ptr_info| switch (ptr_info.size) { .One => switch (@typeInfo(ptr_info.child)) { .Array => |info| { + if (actual_fmt.len == 0) + @compileError("cannot format array ref without a specifier (i.e. {s} or {*})"); if (info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(value, fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(value, actual_fmt, options, writer); } } - return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); + @compileError("Unknown format string: '" ++ actual_fmt ++ "'"); }, .Enum, .Union, .Struct => { - return formatType(value.*, fmt, options, writer, max_depth); + return formatType(value.*, actual_fmt, options, writer, max_depth); }, else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }), }, .Many, .C => { + if (actual_fmt.len == 0) + @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); if (ptr_info.sentinel) |sentinel| { - return formatType(mem.span(value), fmt, options, writer, max_depth); + return formatType(mem.span(value), actual_fmt, options, writer, max_depth); } if (ptr_info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(mem.span(value), fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(mem.span(value), actual_fmt, options, writer); } } - return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); + @compileError("Unknown format string: '" ++ actual_fmt ++ "'"); }, .Slice => { + if (actual_fmt.len == 0) + @compileError("cannot format slice without a specifier (i.e. {s} or {any})"); if (max_depth == 0) { return writer.writeAll("{ ... }"); } if (ptr_info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(value, fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(value, actual_fmt, options, writer); } } try writer.writeAll("{ "); for (value) |elem, i| { - try formatType(elem, fmt, options, writer, max_depth - 1); + try formatType(elem, actual_fmt, options, writer, max_depth - 1); if (i != value.len - 1) { try writer.writeAll(", "); } @@ -547,17 +570,19 @@ pub fn formatType( }, }, .Array => |info| { + if (actual_fmt.len == 0) + @compileError("cannot format array without a specifier (i.e. {s} or {any})"); if (max_depth == 0) { return writer.writeAll("{ ... }"); } if (info.child == u8) { - if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { - return formatText(&value, fmt, options, writer); + if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + return formatText(&value, actual_fmt, options, writer); } } try writer.writeAll("{ "); for (value) |elem, i| { - try formatType(elem, fmt, options, writer, max_depth - 1); + try formatType(elem, actual_fmt, options, writer, max_depth - 1); if (i < value.len - 1) { try writer.writeAll(", "); } @@ -568,7 +593,7 @@ pub fn formatType( try writer.writeAll("{ "); var i: usize = 0; while (i < info.len) : (i += 1) { - try formatValue(value[i], fmt, options, writer); + try formatValue(value[i], actual_fmt, options, writer); if (i < info.len - 1) { try writer.writeAll(", "); } @@ -1668,7 +1693,7 @@ test "slice" { { var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; var runtime_zero: usize = 0; - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 8df05ba7fe..1d89155a58 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -29,7 +29,7 @@ pub var zig_exe_path: []const u8 = undefined; /// and then aborts when actual_error_union is not expected_error. pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void { if (actual_error_union) |actual_payload| { - std.debug.panic("expected error.{s}, found {}", .{ @errorName(expected_error), actual_payload }); + std.debug.panic("expected error.{s}, found {any}", .{ @errorName(expected_error), actual_payload }); } else |actual_error| { if (expected_error != actual_error) { std.debug.panic("expected error.{s}, found error.{s}", .{ @@ -88,7 +88,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { }, .Slice => { if (actual.ptr != expected.ptr) { - std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr }); + std.debug.panic("expected slice ptr {*}, found {*}", .{ expected.ptr, actual.ptr }); } if (actual.len != expected.len) { std.debug.panic("expected slice len {}, found {}", .{ expected.len, actual.len }); @@ -145,11 +145,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { if (actual) |actual_payload| { expectEqual(expected_payload, actual_payload); } else { - std.debug.panic("expected {}, found null", .{expected_payload}); + std.debug.panic("expected {any}, found null", .{expected_payload}); } } else { if (actual) |actual_payload| { - std.debug.panic("expected null, found {}", .{actual_payload}); + std.debug.panic("expected null, found {any}", .{actual_payload}); } } }, @@ -159,11 +159,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { if (actual) |actual_payload| { expectEqual(expected_payload, actual_payload); } else |actual_err| { - std.debug.panic("expected {}, found {}", .{ expected_payload, actual_err }); + std.debug.panic("expected {any}, found {}", .{ expected_payload, actual_err }); } } else |expected_err| { if (actual) |actual_payload| { - std.debug.panic("expected {}, found {}", .{ expected_err, actual_payload }); + std.debug.panic("expected {}, found {any}", .{ expected_err, actual_payload }); } else |actual_err| { expectEqual(expected_err, actual_err); } @@ -279,7 +279,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const var i: usize = 0; while (i < expected.len) : (i += 1) { if (!std.meta.eql(expected[i], actual[i])) { - std.debug.panic("index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] }); + std.debug.panic("index {} incorrect. expected {any}, found {any}", .{ i, expected[i], actual[i] }); } } } diff --git a/src/Module.zig b/src/Module.zig index 8de03b54ab..a90998a386 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2400,7 +2400,7 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn else => unreachable, }; const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]); - return std.fmt.allocPrint(self.gpa, "{}:{}:{}", .{ base_name, loc.line, loc.column }); + return std.fmt.allocPrint(self.gpa, "{s}:{}:{}", .{ base_name, loc.line, loc.column }); } fn getNextAnonNameIndex(self: *Module) usize { diff --git a/src/codegen.zig b/src/codegen.zig index 63dbe3268e..9771386403 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -2223,7 +2223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32()); break :blk .ne; }, - else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), + else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), }; const reloc = Reloc{ diff --git a/src/zir_sema.zig b/src/zir_sema.zig index f373d7174d..a8120108a4 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -1832,7 +1832,7 @@ fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*In const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; if (!is_int) { - return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); + return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); } if (casted_lhs.value()) |lhs_val| { diff --git a/test/cli.zig b/test/cli.zig index 33dbc2d62b..8dbef06887 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -51,9 +51,9 @@ fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 { } fn printCmd(cwd: []const u8, argv: []const []const u8) void { - std.debug.warn("cd {} && ", .{cwd}); + std.debug.warn("cd {s} && ", .{cwd}); for (argv) |arg| { - std.debug.warn("{} ", .{arg}); + std.debug.warn("{s} ", .{arg}); } std.debug.warn("\n", .{}); } @@ -75,14 +75,14 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess if ((code != 0) == expect_0) { std.debug.warn("The following command exited with error code {}:\n", .{code}); printCmd(cwd, argv); - std.debug.warn("stderr:\n{}\n", .{result.stderr}); + std.debug.warn("stderr:\n{s}\n", .{result.stderr}); return error.CommandFailed; } }, else => { std.debug.warn("The following command terminated unexpectedly:\n", .{}); printCmd(cwd, argv); - std.debug.warn("stderr:\n{}\n", .{result.stderr}); + std.debug.warn("stderr:\n{s}\n", .{result.stderr}); return error.CommandFailed; }, } diff --git a/test/standalone/cat/main.zig b/test/standalone/cat/main.zig index 89e5fde3cd..80ec97877a 100644 --- a/test/standalone/cat/main.zig +++ b/test/standalone/cat/main.zig @@ -41,6 +41,6 @@ pub fn main() !void { } fn usage(exe: []const u8) !void { - warn("Usage: {} [FILE]...\n", .{exe}); + warn("Usage: {s} [FILE]...\n", .{exe}); return error.Invalid; } -- cgit v1.2.3 From a2ec77041bc4a58d922cd3f8db923b7272b1f8f7 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Mon, 8 Feb 2021 11:43:57 -0800 Subject: translate-c: call @boolToInt on return value when necessary In C, if a function has return type `int` and the return expression is a boolean expression, there is no implicit cast. Therefore the translated Zig code needs to call @boolToInt() on the result. Written with feedback from @Vexu Fixes #6215 --- src/translate_c.zig | 44 ++++++++++++++++++++++++++++++++++++++++---- test/run_translated_c.zig | 35 +++++++++++++++++++++++++++++++++++ test/translate_c.zig | 8 ++++---- 3 files changed, 79 insertions(+), 8 deletions(-) (limited to 'test') diff --git a/src/translate_c.zig b/src/translate_c.zig index e3652ddbfb..82a7695c13 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -78,6 +78,10 @@ const Scope = struct { mangle_count: u32 = 0, lbrace: ast.TokenIndex, + /// When the block corresponds to a function, keep track of the return type + /// so that the return expression can be cast, if necessary + return_type: ?clang.QualType = null, + fn init(c: *Context, parent: *Scope, labeled: bool) !Block { var blk = Block{ .base = .{ @@ -209,6 +213,21 @@ const Scope = struct { } } + fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType { + var scope = inner; + while (true) { + switch (scope.id) { + .Root => return null, + .Block => { + const block = @fieldParentPtr(Block, "base", scope); + if (block.return_type) |qt| return qt; + scope = scope.parent.?; + }, + else => scope = scope.parent.?, + } + } + } + fn getAlias(scope: *Scope, name: []const u8) []const u8 { return switch (scope.id) { .Root => return name, @@ -580,6 +599,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { else => break fn_type, } } else unreachable; + const fn_ty = @ptrCast(*const clang.FunctionType, fn_type); + const return_qt = fn_ty.getReturnType(); const proto_node = switch (fn_type.getTypeClass()) { .FunctionProto => blk: { @@ -617,7 +638,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { // actual function definition with body const body_stmt = fn_decl.getBody(); var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false); + block_scope.return_type = return_qt; defer block_scope.deinit(); + var scope = &block_scope.base; var param_id: c_uint = 0; @@ -667,10 +690,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }; // add return statement if the function didn't have one blk: { - const fn_ty = @ptrCast(*const clang.FunctionType, fn_type); - if (fn_ty.getNoReturnAttr()) break :blk; - const return_qt = fn_ty.getReturnType(); if (isCVoid(return_qt)) break :blk; if (block_scope.statements.items.len > 0) { @@ -2018,16 +2038,32 @@ fn transIntegerLiteral( return maybeSuppressResult(rp, scope, result_used, &as_node.base); } +/// In C if a function has return type `int` and the return value is a boolean +/// expression, there is no implicit cast. So the translated Zig will need to +/// call @boolToInt +fn zigShouldCastBooleanReturnToInt(node: ?*ast.Node, qt: ?clang.QualType) bool { + if (node == null or qt == null) return false; + return isBoolRes(node.?) and cIsNativeInt(qt.?); +} + fn transReturnStmt( rp: RestorePoint, scope: *Scope, expr: *const clang.ReturnStmt, ) TransError!*ast.Node { const return_kw = try appendToken(rp.c, .Keyword_return, "return"); - const rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| + var rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| try transExprCoercing(rp, scope, val_expr, .used, .r_value) else null; + const return_qt = scope.findBlockReturnType(rp.c); + if (zigShouldCastBooleanReturnToInt(rhs, return_qt)) { + const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1); + bool_to_int_node.params()[0] = rhs.?; + bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + rhs = &bool_to_int_node.base; + } const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ .ltoken = return_kw, .tag = .Return, diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index a8a3a0e21b..e28bdc96f0 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -874,4 +874,39 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Return boolean expression as int; issue #6215", + \\#include + \\#include + \\bool actual_bool(void) { return 4 - 1 < 4;} + \\char char_bool_ret(void) { return 0 || 1; } + \\short short_bool_ret(void) { return 0 < 1; } + \\int int_bool_ret(void) { return 1 && 1; } + \\long long_bool_ret(void) { return !(0 > 1); } + \\static int GLOBAL = 1; + \\int nested_scopes(int a, int b) { + \\ if (a == 1) { + \\ int target = 1; + \\ return b == target; + \\ } else { + \\ int target = 2; + \\ if (b == target) { + \\ return GLOBAL == 1; + \\ } + \\ return target == 2; + \\ } + \\} + \\int main(void) { + \\ if (!actual_bool()) abort(); + \\ if (!char_bool_ret()) abort(); + \\ if (!short_bool_ret()) abort(); + \\ if (!int_bool_ret()) abort(); + \\ if (!long_bool_ret()) abort(); + \\ if (!nested_scopes(1, 1)) abort(); + \\ if (nested_scopes(1, 2)) abort(); + \\ if (!nested_scopes(0, 2)) abort(); + \\ if (!nested_scopes(0, 3)) abort(); + \\ return 1 != 1; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 75d00d12f4..03ca87d5f6 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1305,10 +1305,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a: c_int = undefined; \\ var b: f32 = undefined; \\ var c: ?*c_void = undefined; - \\ return !(a == @as(c_int, 0)); - \\ return !(a != 0); - \\ return !(b != 0); - \\ return !(c != null); + \\ return @boolToInt(!(a == @as(c_int, 0))); + \\ return @boolToInt(!(a != 0)); + \\ return @boolToInt(!(b != 0)); + \\ return @boolToInt(!(c != null)); \\} }); -- cgit v1.2.3 From 5dfe0e7e8fabd3cfc3fdf5c7099791da98899903 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Mon, 11 Jan 2021 08:03:09 -0700 Subject: Convert inline fn to callconv(.Inline) everywhere --- doc/langref.html.in | 4 +- lib/std/c/builtins.zig | 98 +++++++++++++++++------------------ lib/std/compress/deflate.zig | 2 +- lib/std/crypto/25519/curve25519.zig | 4 +- lib/std/crypto/25519/edwards25519.zig | 6 +-- lib/std/crypto/25519/field.zig | 28 +++++----- lib/std/crypto/25519/ristretto255.zig | 8 +-- lib/std/crypto/25519/scalar.zig | 2 +- lib/std/crypto/aegis.zig | 4 +- lib/std/crypto/aes/aesni.zig | 32 ++++++------ lib/std/crypto/aes/armcrypto.zig | 32 ++++++------ lib/std/crypto/aes/soft.zig | 20 +++---- lib/std/crypto/blake3.zig | 6 +-- lib/std/crypto/chacha20.zig | 12 ++--- lib/std/crypto/ghash.zig | 4 +- lib/std/crypto/gimli.zig | 4 +- lib/std/crypto/salsa20.zig | 6 +-- lib/std/elf.zig | 16 +++--- lib/std/fmt/parse_float.zig | 8 +-- lib/std/hash/cityhash.zig | 2 +- lib/std/os/bits/freebsd.zig | 8 +-- lib/std/os/bits/netbsd.zig | 8 +-- lib/std/os/linux.zig | 2 +- lib/std/os/linux/tls.zig | 2 +- lib/std/os/windows.zig | 2 +- lib/std/start.zig | 4 +- lib/std/zig/parser_test.zig | 6 +-- lib/std/zig/system/x86.zig | 4 +- src/link/MachO.zig | 2 +- src/tracy.zig | 2 +- test/cli.zig | 2 +- test/compile_errors.zig | 12 ++--- test/stage1/behavior/fn.zig | 2 +- test/stage2/cbe.zig | 2 +- test/stage2/test.zig | 10 ++-- test/translate_c.zig | 32 ++++++------ 36 files changed, 199 insertions(+), 199 deletions(-) (limited to 'test') diff --git a/doc/langref.html.in b/doc/langref.html.in index 7759e26f3b..f43abfe1e6 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4240,9 +4240,9 @@ fn _start() callconv(.Naked) noreturn { abort(); } -// The inline specifier forces a function to be inlined at all call sites. +// The inline calling convention forces a function to be inlined at all call sites. // If the function cannot be inlined, it is a compile-time error. -inline fn shiftLeftOne(a: u32) u32 { +fn shiftLeftOne(a: u32) callconv(.Inline) u32 { return a << 1; } diff --git a/lib/std/c/builtins.zig b/lib/std/c/builtins.zig index 43f5ed0588..2b386c82f4 100644 --- a/lib/std/c/builtins.zig +++ b/lib/std/c/builtins.zig @@ -6,70 +6,70 @@ const std = @import("std"); -pub inline fn __builtin_bswap16(val: u16) callconv(.C) u16 { return @byteSwap(u16, val); } -pub inline fn __builtin_bswap32(val: u32) callconv(.C) u32 { return @byteSwap(u32, val); } -pub inline fn __builtin_bswap64(val: u64) callconv(.C) u64 { return @byteSwap(u64, val); } +pub fn __builtin_bswap16(val: u16) callconv(.Inline) u16 { return @byteSwap(u16, val); } +pub fn __builtin_bswap32(val: u32) callconv(.Inline) u32 { return @byteSwap(u32, val); } +pub fn __builtin_bswap64(val: u64) callconv(.Inline) u64 { return @byteSwap(u64, val); } -pub inline fn __builtin_signbit(val: f64) callconv(.C) c_int { return @boolToInt(std.math.signbit(val)); } -pub inline fn __builtin_signbitf(val: f32) callconv(.C) c_int { return @boolToInt(std.math.signbit(val)); } +pub fn __builtin_signbit(val: f64) callconv(.Inline) c_int { return @boolToInt(std.math.signbit(val)); } +pub fn __builtin_signbitf(val: f32) callconv(.Inline) c_int { return @boolToInt(std.math.signbit(val)); } -pub inline fn __builtin_popcount(val: c_uint) callconv(.C) c_int { +pub fn __builtin_popcount(val: c_uint) callconv(.Inline) c_int { // popcount of a c_uint will never exceed the capacity of a c_int @setRuntimeSafety(false); return @bitCast(c_int, @as(c_uint, @popCount(c_uint, val))); } -pub inline fn __builtin_ctz(val: c_uint) callconv(.C) c_int { +pub fn __builtin_ctz(val: c_uint) callconv(.Inline) c_int { // Returns the number of trailing 0-bits in val, starting at the least significant bit position. // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint @setRuntimeSafety(false); return @bitCast(c_int, @as(c_uint, @ctz(c_uint, val))); } -pub inline fn __builtin_clz(val: c_uint) callconv(.C) c_int { +pub fn __builtin_clz(val: c_uint) callconv(.Inline) c_int { // Returns the number of leading 0-bits in x, starting at the most significant bit position. // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint @setRuntimeSafety(false); return @bitCast(c_int, @as(c_uint, @clz(c_uint, val))); } -pub inline fn __builtin_sqrt(val: f64) callconv(.C) f64 { return @sqrt(val); } -pub inline fn __builtin_sqrtf(val: f32) callconv(.C) f32 { return @sqrt(val); } - -pub inline fn __builtin_sin(val: f64) callconv(.C) f64 { return @sin(val); } -pub inline fn __builtin_sinf(val: f32) callconv(.C) f32 { return @sin(val); } -pub inline fn __builtin_cos(val: f64) callconv(.C) f64 { return @cos(val); } -pub inline fn __builtin_cosf(val: f32) callconv(.C) f32 { return @cos(val); } - -pub inline fn __builtin_exp(val: f64) callconv(.C) f64 { return @exp(val); } -pub inline fn __builtin_expf(val: f32) callconv(.C) f32 { return @exp(val); } -pub inline fn __builtin_exp2(val: f64) callconv(.C) f64 { return @exp2(val); } -pub inline fn __builtin_exp2f(val: f32) callconv(.C) f32 { return @exp2(val); } -pub inline fn __builtin_log(val: f64) callconv(.C) f64 { return @log(val); } -pub inline fn __builtin_logf(val: f32) callconv(.C) f32 { return @log(val); } -pub inline fn __builtin_log2(val: f64) callconv(.C) f64 { return @log2(val); } -pub inline fn __builtin_log2f(val: f32) callconv(.C) f32 { return @log2(val); } -pub inline fn __builtin_log10(val: f64) callconv(.C) f64 { return @log10(val); } -pub inline fn __builtin_log10f(val: f32) callconv(.C) f32 { return @log10(val); } +pub fn __builtin_sqrt(val: f64) callconv(.Inline) f64 { return @sqrt(val); } +pub fn __builtin_sqrtf(val: f32) callconv(.Inline) f32 { return @sqrt(val); } + +pub fn __builtin_sin(val: f64) callconv(.Inline) f64 { return @sin(val); } +pub fn __builtin_sinf(val: f32) callconv(.Inline) f32 { return @sin(val); } +pub fn __builtin_cos(val: f64) callconv(.Inline) f64 { return @cos(val); } +pub fn __builtin_cosf(val: f32) callconv(.Inline) f32 { return @cos(val); } + +pub fn __builtin_exp(val: f64) callconv(.Inline) f64 { return @exp(val); } +pub fn __builtin_expf(val: f32) callconv(.Inline) f32 { return @exp(val); } +pub fn __builtin_exp2(val: f64) callconv(.Inline) f64 { return @exp2(val); } +pub fn __builtin_exp2f(val: f32) callconv(.Inline) f32 { return @exp2(val); } +pub fn __builtin_log(val: f64) callconv(.Inline) f64 { return @log(val); } +pub fn __builtin_logf(val: f32) callconv(.Inline) f32 { return @log(val); } +pub fn __builtin_log2(val: f64) callconv(.Inline) f64 { return @log2(val); } +pub fn __builtin_log2f(val: f32) callconv(.Inline) f32 { return @log2(val); } +pub fn __builtin_log10(val: f64) callconv(.Inline) f64 { return @log10(val); } +pub fn __builtin_log10f(val: f32) callconv(.Inline) f32 { return @log10(val); } // Standard C Library bug: The absolute value of the most negative integer remains negative. -pub inline fn __builtin_abs(val: c_int) callconv(.C) c_int { return std.math.absInt(val) catch std.math.minInt(c_int); } -pub inline fn __builtin_fabs(val: f64) callconv(.C) f64 { return @fabs(val); } -pub inline fn __builtin_fabsf(val: f32) callconv(.C) f32 { return @fabs(val); } - -pub inline fn __builtin_floor(val: f64) callconv(.C) f64 { return @floor(val); } -pub inline fn __builtin_floorf(val: f32) callconv(.C) f32 { return @floor(val); } -pub inline fn __builtin_ceil(val: f64) callconv(.C) f64 { return @ceil(val); } -pub inline fn __builtin_ceilf(val: f32) callconv(.C) f32 { return @ceil(val); } -pub inline fn __builtin_trunc(val: f64) callconv(.C) f64 { return @trunc(val); } -pub inline fn __builtin_truncf(val: f32) callconv(.C) f32 { return @trunc(val); } -pub inline fn __builtin_round(val: f64) callconv(.C) f64 { return @round(val); } -pub inline fn __builtin_roundf(val: f32) callconv(.C) f32 { return @round(val); } - -pub inline fn __builtin_strlen(s: [*c]const u8) callconv(.C) usize { return std.mem.lenZ(s); } -pub inline fn __builtin_strcmp(s1: [*c]const u8, s2: [*c]const u8) callconv(.C) c_int { +pub fn __builtin_abs(val: c_int) callconv(.Inline) c_int { return std.math.absInt(val) catch std.math.minInt(c_int); } +pub fn __builtin_fabs(val: f64) callconv(.Inline) f64 { return @fabs(val); } +pub fn __builtin_fabsf(val: f32) callconv(.Inline) f32 { return @fabs(val); } + +pub fn __builtin_floor(val: f64) callconv(.Inline) f64 { return @floor(val); } +pub fn __builtin_floorf(val: f32) callconv(.Inline) f32 { return @floor(val); } +pub fn __builtin_ceil(val: f64) callconv(.Inline) f64 { return @ceil(val); } +pub fn __builtin_ceilf(val: f32) callconv(.Inline) f32 { return @ceil(val); } +pub fn __builtin_trunc(val: f64) callconv(.Inline) f64 { return @trunc(val); } +pub fn __builtin_truncf(val: f32) callconv(.Inline) f32 { return @trunc(val); } +pub fn __builtin_round(val: f64) callconv(.Inline) f64 { return @round(val); } +pub fn __builtin_roundf(val: f32) callconv(.Inline) f32 { return @round(val); } + +pub fn __builtin_strlen(s: [*c]const u8) callconv(.Inline) usize { return std.mem.lenZ(s); } +pub fn __builtin_strcmp(s1: [*c]const u8, s2: [*c]const u8) callconv(.Inline) c_int { return @as(c_int, std.cstr.cmp(s1, s2)); } -pub inline fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.C) usize { +pub fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.Inline) usize { // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html // If it is not possible to determine which objects ptr points to at compile time, // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0 @@ -79,37 +79,37 @@ pub inline fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.C) unreachable; } -pub inline fn __builtin___memset_chk( +pub fn __builtin___memset_chk( dst: ?*c_void, val: c_int, len: usize, remaining: usize, -) callconv(.C) ?*c_void { +) callconv(.Inline) ?*c_void { if (len > remaining) @panic("std.c.builtins.memset_chk called with len > remaining"); return __builtin_memset(dst, val, len); } -pub inline fn __builtin_memset(dst: ?*c_void, val: c_int, len: usize) callconv(.C) ?*c_void { +pub fn __builtin_memset(dst: ?*c_void, val: c_int, len: usize) callconv(.Inline) ?*c_void { const dst_cast = @ptrCast([*c]u8, dst); @memset(dst_cast, @bitCast(u8, @truncate(i8, val)), len); return dst; } -pub inline fn __builtin___memcpy_chk( +pub fn __builtin___memcpy_chk( noalias dst: ?*c_void, noalias src: ?*const c_void, len: usize, remaining: usize, -) callconv(.C) ?*c_void { +) callconv(.Inline) ?*c_void { if (len > remaining) @panic("std.c.builtins.memcpy_chk called with len > remaining"); return __builtin_memcpy(dst, src, len); } -pub inline fn __builtin_memcpy( +pub fn __builtin_memcpy( noalias dst: ?*c_void, noalias src: ?*const c_void, len: usize, -) callconv(.C) ?*c_void { +) callconv(.Inline) ?*c_void { const dst_cast = @ptrCast([*c]u8, dst); const src_cast = @ptrCast([*c]const u8, src); diff --git a/lib/std/compress/deflate.zig b/lib/std/compress/deflate.zig index 3f920c08b6..e680dc9e6f 100644 --- a/lib/std/compress/deflate.zig +++ b/lib/std/compress/deflate.zig @@ -209,7 +209,7 @@ pub fn InflateStream(comptime ReaderType: type) type { // Insert a single byte into the window. // Assumes there's enough space. - inline fn appendUnsafe(self: *WSelf, value: u8) void { + fn appendUnsafe(self: *WSelf, value: u8) callconv(.Inline) void { self.buf[self.wi] = value; self.wi = (self.wi + 1) & (self.buf.len - 1); self.el += 1; diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index 14ad0444f5..765ffa1629 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -15,12 +15,12 @@ pub const Curve25519 = struct { x: Fe, /// Decode a Curve25519 point from its compressed (X) coordinates. - pub inline fn fromBytes(s: [32]u8) Curve25519 { + pub fn fromBytes(s: [32]u8) callconv(.Inline) Curve25519 { return .{ .x = Fe.fromBytes(s) }; } /// Encode a Curve25519 point. - pub inline fn toBytes(p: Curve25519) [32]u8 { + pub fn toBytes(p: Curve25519) callconv(.Inline) [32]u8 { return p.x.toBytes(); } diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index 8c0a783083..d4238f87bb 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -92,7 +92,7 @@ pub const Edwards25519 = struct { } /// Flip the sign of the X coordinate. - pub inline fn neg(p: Edwards25519) Edwards25519 { + pub fn neg(p: Edwards25519) callconv(.Inline) Edwards25519 { return .{ .x = p.x.neg(), .y = p.y, .z = p.z, .t = p.t.neg() }; } @@ -137,14 +137,14 @@ pub const Edwards25519 = struct { return p.add(q.neg()); } - inline fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) void { + fn cMov(p: *Edwards25519, a: Edwards25519, c: u64) callconv(.Inline) void { p.x.cMov(a.x, c); p.y.cMov(a.y, c); p.z.cMov(a.z, c); p.t.cMov(a.t, c); } - inline fn pcSelect(comptime n: usize, pc: [n]Edwards25519, b: u8) Edwards25519 { + fn pcSelect(comptime n: usize, pc: [n]Edwards25519, b: u8) callconv(.Inline) Edwards25519 { var t = Edwards25519.identityElement; comptime var i: u8 = 1; inline while (i < pc.len) : (i += 1) { diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index 12c7f06d39..320cb1bb51 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -52,7 +52,7 @@ pub const Fe = struct { pub const edwards25519sqrtam2 = Fe{ .limbs = .{ 1693982333959686, 608509411481997, 2235573344831311, 947681270984193, 266558006233600 } }; /// Return true if the field element is zero - pub inline fn isZero(fe: Fe) bool { + pub fn isZero(fe: Fe) callconv(.Inline) bool { var reduced = fe; reduced.reduce(); const limbs = reduced.limbs; @@ -60,7 +60,7 @@ pub const Fe = struct { } /// Return true if both field elements are equivalent - pub inline fn equivalent(a: Fe, b: Fe) bool { + pub fn equivalent(a: Fe, b: Fe) callconv(.Inline) bool { return a.sub(b).isZero(); } @@ -164,7 +164,7 @@ pub const Fe = struct { } /// Add a field element - pub inline fn add(a: Fe, b: Fe) Fe { + pub fn add(a: Fe, b: Fe) callconv(.Inline) Fe { var fe: Fe = undefined; comptime var i = 0; inline while (i < 5) : (i += 1) { @@ -174,7 +174,7 @@ pub const Fe = struct { } /// Substract a field elememnt - pub inline fn sub(a: Fe, b: Fe) Fe { + pub fn sub(a: Fe, b: Fe) callconv(.Inline) Fe { var fe = b; comptime var i = 0; inline while (i < 4) : (i += 1) { @@ -193,17 +193,17 @@ pub const Fe = struct { } /// Negate a field element - pub inline fn neg(a: Fe) Fe { + pub fn neg(a: Fe) callconv(.Inline) Fe { return zero.sub(a); } /// Return true if a field element is negative - pub inline fn isNegative(a: Fe) bool { + pub fn isNegative(a: Fe) callconv(.Inline) bool { return (a.toBytes()[0] & 1) != 0; } /// Conditonally replace a field element with `a` if `c` is positive - pub inline fn cMov(fe: *Fe, a: Fe, c: u64) void { + pub fn cMov(fe: *Fe, a: Fe, c: u64) callconv(.Inline) void { const mask: u64 = 0 -% c; var x = fe.*; comptime var i = 0; @@ -244,7 +244,7 @@ pub const Fe = struct { } } - inline fn _carry128(r: *[5]u128) Fe { + fn _carry128(r: *[5]u128) callconv(.Inline) Fe { var rs: [5]u64 = undefined; comptime var i = 0; inline while (i < 4) : (i += 1) { @@ -265,7 +265,7 @@ pub const Fe = struct { } /// Multiply two field elements - pub inline fn mul(a: Fe, b: Fe) Fe { + pub fn mul(a: Fe, b: Fe) callconv(.Inline) Fe { var ax: [5]u128 = undefined; var bx: [5]u128 = undefined; var a19: [5]u128 = undefined; @@ -288,7 +288,7 @@ pub const Fe = struct { return _carry128(&r); } - inline fn _sq(a: Fe, double: comptime bool) Fe { + fn _sq(a: Fe, double: comptime bool) callconv(.Inline) Fe { var ax: [5]u128 = undefined; var r: [5]u128 = undefined; comptime var i = 0; @@ -317,17 +317,17 @@ pub const Fe = struct { } /// Square a field element - pub inline fn sq(a: Fe) Fe { + pub fn sq(a: Fe) callconv(.Inline) Fe { return _sq(a, false); } /// Square and double a field element - pub inline fn sq2(a: Fe) Fe { + pub fn sq2(a: Fe) callconv(.Inline) Fe { return _sq(a, true); } /// Multiply a field element with a small (32-bit) integer - pub inline fn mul32(a: Fe, comptime n: u32) Fe { + pub fn mul32(a: Fe, comptime n: u32) callconv(.Inline) Fe { const sn = @intCast(u128, n); var fe: Fe = undefined; var x: u128 = 0; @@ -342,7 +342,7 @@ pub const Fe = struct { } /// Square a field element `n` times - inline fn sqn(a: Fe, comptime n: comptime_int) Fe { + fn sqn(a: Fe, comptime n: comptime_int) callconv(.Inline) Fe { var i: usize = 0; var fe = a; while (i < n) : (i += 1) { diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig index 35dd6ea76c..68fb938103 100644 --- a/lib/std/crypto/25519/ristretto255.zig +++ b/lib/std/crypto/25519/ristretto255.zig @@ -42,7 +42,7 @@ pub const Ristretto255 = struct { } /// Reject the neutral element. - pub inline fn rejectIdentity(p: Ristretto255) !void { + pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) !void { return p.p.rejectIdentity(); } @@ -141,19 +141,19 @@ pub const Ristretto255 = struct { } /// Double a Ristretto255 element. - pub inline fn dbl(p: Ristretto255) Ristretto255 { + pub fn dbl(p: Ristretto255) callconv(.Inline) Ristretto255 { return .{ .p = p.p.dbl() }; } /// Add two Ristretto255 elements. - pub inline fn add(p: Ristretto255, q: Ristretto255) Ristretto255 { + pub fn add(p: Ristretto255, q: Ristretto255) callconv(.Inline) Ristretto255 { return .{ .p = p.p.add(q.p) }; } /// Multiply a Ristretto255 element with a scalar. /// Return error.WeakPublicKey if the resulting element is /// the identity element. - pub inline fn mul(p: Ristretto255, s: [encoded_length]u8) !Ristretto255 { + pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) !Ristretto255 { return Ristretto255{ .p = try p.p.mul(s) }; } diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig index d00f147274..ceff153bff 100644 --- a/lib/std/crypto/25519/scalar.zig +++ b/lib/std/crypto/25519/scalar.zig @@ -46,7 +46,7 @@ pub fn reduce64(s: [64]u8) [32]u8 { /// Perform the X25519 "clamping" operation. /// The scalar is then guaranteed to be a multiple of the cofactor. -pub inline fn clamp(s: *[32]u8) void { +pub fn clamp(s: *[32]u8) callconv(.Inline) void { s[0] &= 248; s[31] = (s[31] & 127) | 64; } diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig index 234b439708..089dc06be4 100644 --- a/lib/std/crypto/aegis.zig +++ b/lib/std/crypto/aegis.zig @@ -35,7 +35,7 @@ const State128L = struct { return state; } - inline fn update(state: *State128L, d1: AesBlock, d2: AesBlock) void { + fn update(state: *State128L, d1: AesBlock, d2: AesBlock) callconv(.Inline) void { const blocks = &state.blocks; const tmp = blocks[7]; comptime var i: usize = 7; @@ -207,7 +207,7 @@ const State256 = struct { return state; } - inline fn update(state: *State256, d: AesBlock) void { + fn update(state: *State256, d: AesBlock) callconv(.Inline) void { const blocks = &state.blocks; const tmp = blocks[5].encrypt(blocks[0]); comptime var i: usize = 5; diff --git a/lib/std/crypto/aes/aesni.zig b/lib/std/crypto/aes/aesni.zig index 231bf5dbe8..13b3f8e527 100644 --- a/lib/std/crypto/aes/aesni.zig +++ b/lib/std/crypto/aes/aesni.zig @@ -19,24 +19,24 @@ pub const Block = struct { repr: BlockVec, /// Convert a byte sequence into an internal representation. - pub inline fn fromBytes(bytes: *const [16]u8) Block { + pub fn fromBytes(bytes: *const [16]u8) callconv(.Inline) Block { const repr = mem.bytesToValue(BlockVec, bytes); return Block{ .repr = repr }; } /// Convert the internal representation of a block into a byte sequence. - pub inline fn toBytes(block: Block) [16]u8 { + pub fn toBytes(block: Block) callconv(.Inline) [16]u8 { return mem.toBytes(block.repr); } /// XOR the block with a byte sequence. - pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 { + pub fn xorBytes(block: Block, bytes: *const [16]u8) callconv(.Inline) [16]u8 { const x = block.repr ^ fromBytes(bytes).repr; return mem.toBytes(x); } /// Encrypt a block with a round key. - pub inline fn encrypt(block: Block, round_key: Block) Block { + pub fn encrypt(block: Block, round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ vaesenc %[rk], %[in], %[out] @@ -48,7 +48,7 @@ pub const Block = struct { } /// Encrypt a block with the last round key. - pub inline fn encryptLast(block: Block, round_key: Block) Block { + pub fn encryptLast(block: Block, round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ vaesenclast %[rk], %[in], %[out] @@ -60,7 +60,7 @@ pub const Block = struct { } /// Decrypt a block with a round key. - pub inline fn decrypt(block: Block, inv_round_key: Block) Block { + pub fn decrypt(block: Block, inv_round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ vaesdec %[rk], %[in], %[out] @@ -72,7 +72,7 @@ pub const Block = struct { } /// Decrypt a block with the last round key. - pub inline fn decryptLast(block: Block, inv_round_key: Block) Block { + pub fn decryptLast(block: Block, inv_round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ vaesdeclast %[rk], %[in], %[out] @@ -84,17 +84,17 @@ pub const Block = struct { } /// Apply the bitwise XOR operation to the content of two blocks. - pub inline fn xorBlocks(block1: Block, block2: Block) Block { + pub fn xorBlocks(block1: Block, block2: Block) callconv(.Inline) Block { return Block{ .repr = block1.repr ^ block2.repr }; } /// Apply the bitwise AND operation to the content of two blocks. - pub inline fn andBlocks(block1: Block, block2: Block) Block { + pub fn andBlocks(block1: Block, block2: Block) callconv(.Inline) Block { return Block{ .repr = block1.repr & block2.repr }; } /// Apply the bitwise OR operation to the content of two blocks. - pub inline fn orBlocks(block1: Block, block2: Block) Block { + pub fn orBlocks(block1: Block, block2: Block) callconv(.Inline) Block { return Block{ .repr = block1.repr | block2.repr }; } @@ -114,7 +114,7 @@ pub const Block = struct { }; /// Encrypt multiple blocks in parallel, each their own round key. - pub inline fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block { + pub fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -124,7 +124,7 @@ pub const Block = struct { } /// Decrypt multiple blocks in parallel, each their own round key. - pub inline fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block { + pub fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -134,7 +134,7 @@ pub const Block = struct { } /// Encrypt multiple blocks in parallel with the same round key. - pub inline fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -144,7 +144,7 @@ pub const Block = struct { } /// Decrypt multiple blocks in parallel with the same round key. - pub inline fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -154,7 +154,7 @@ pub const Block = struct { } /// Encrypt multiple blocks in parallel with the same last round key. - pub inline fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -164,7 +164,7 @@ pub const Block = struct { } /// Decrypt multiple blocks in parallel with the same last round key. - pub inline fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { diff --git a/lib/std/crypto/aes/armcrypto.zig b/lib/std/crypto/aes/armcrypto.zig index cb8387a2c6..d331783284 100644 --- a/lib/std/crypto/aes/armcrypto.zig +++ b/lib/std/crypto/aes/armcrypto.zig @@ -19,18 +19,18 @@ pub const Block = struct { repr: BlockVec, /// Convert a byte sequence into an internal representation. - pub inline fn fromBytes(bytes: *const [16]u8) Block { + pub fn fromBytes(bytes: *const [16]u8) callconv(.Inline) Block { const repr = mem.bytesToValue(BlockVec, bytes); return Block{ .repr = repr }; } /// Convert the internal representation of a block into a byte sequence. - pub inline fn toBytes(block: Block) [16]u8 { + pub fn toBytes(block: Block) callconv(.Inline) [16]u8 { return mem.toBytes(block.repr); } /// XOR the block with a byte sequence. - pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 { + pub fn xorBytes(block: Block, bytes: *const [16]u8) callconv(.Inline) [16]u8 { const x = block.repr ^ fromBytes(bytes).repr; return mem.toBytes(x); } @@ -38,7 +38,7 @@ pub const Block = struct { const zero = Vector(2, u64){ 0, 0 }; /// Encrypt a block with a round key. - pub inline fn encrypt(block: Block, round_key: Block) Block { + pub fn encrypt(block: Block, round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ mov %[out].16b, %[in].16b @@ -54,7 +54,7 @@ pub const Block = struct { } /// Encrypt a block with the last round key. - pub inline fn encryptLast(block: Block, round_key: Block) Block { + pub fn encryptLast(block: Block, round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ mov %[out].16b, %[in].16b @@ -69,7 +69,7 @@ pub const Block = struct { } /// Decrypt a block with a round key. - pub inline fn decrypt(block: Block, inv_round_key: Block) Block { + pub fn decrypt(block: Block, inv_round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ mov %[out].16b, %[in].16b @@ -85,7 +85,7 @@ pub const Block = struct { } /// Decrypt a block with the last round key. - pub inline fn decryptLast(block: Block, inv_round_key: Block) Block { + pub fn decryptLast(block: Block, inv_round_key: Block) callconv(.Inline) Block { return Block{ .repr = asm ( \\ mov %[out].16b, %[in].16b @@ -100,17 +100,17 @@ pub const Block = struct { } /// Apply the bitwise XOR operation to the content of two blocks. - pub inline fn xorBlocks(block1: Block, block2: Block) Block { + pub fn xorBlocks(block1: Block, block2: Block) callconv(.Inline) Block { return Block{ .repr = block1.repr ^ block2.repr }; } /// Apply the bitwise AND operation to the content of two blocks. - pub inline fn andBlocks(block1: Block, block2: Block) Block { + pub fn andBlocks(block1: Block, block2: Block) callconv(.Inline) Block { return Block{ .repr = block1.repr & block2.repr }; } /// Apply the bitwise OR operation to the content of two blocks. - pub inline fn orBlocks(block1: Block, block2: Block) Block { + pub fn orBlocks(block1: Block, block2: Block) callconv(.Inline) Block { return Block{ .repr = block1.repr | block2.repr }; } @@ -120,7 +120,7 @@ pub const Block = struct { pub const optimal_parallel_blocks = 8; /// Encrypt multiple blocks in parallel, each their own round key. - pub inline fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block { + pub fn encryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -130,7 +130,7 @@ pub const Block = struct { } /// Decrypt multiple blocks in parallel, each their own round key. - pub inline fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) [count]Block { + pub fn decryptParallel(comptime count: usize, blocks: [count]Block, round_keys: [count]Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -140,7 +140,7 @@ pub const Block = struct { } /// Encrypt multiple blocks in parallel with the same round key. - pub inline fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn encryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -150,7 +150,7 @@ pub const Block = struct { } /// Decrypt multiple blocks in parallel with the same round key. - pub inline fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn decryptWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -160,7 +160,7 @@ pub const Block = struct { } /// Encrypt multiple blocks in parallel with the same last round key. - pub inline fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn encryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { @@ -170,7 +170,7 @@ pub const Block = struct { } /// Decrypt multiple blocks in parallel with the same last round key. - pub inline fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) [count]Block { + pub fn decryptLastWide(comptime count: usize, blocks: [count]Block, round_key: Block) callconv(.Inline) [count]Block { comptime var i = 0; var out: [count]Block = undefined; inline while (i < count) : (i += 1) { diff --git a/lib/std/crypto/aes/soft.zig b/lib/std/crypto/aes/soft.zig index 5eda9557ee..6f305b4050 100644 --- a/lib/std/crypto/aes/soft.zig +++ b/lib/std/crypto/aes/soft.zig @@ -18,7 +18,7 @@ pub const Block = struct { repr: BlockVec align(16), /// Convert a byte sequence into an internal representation. - pub inline fn fromBytes(bytes: *const [16]u8) Block { + pub fn fromBytes(bytes: *const [16]u8) callconv(.Inline) Block { const s0 = mem.readIntBig(u32, bytes[0..4]); const s1 = mem.readIntBig(u32, bytes[4..8]); const s2 = mem.readIntBig(u32, bytes[8..12]); @@ -27,7 +27,7 @@ pub const Block = struct { } /// Convert the internal representation of a block into a byte sequence. - pub inline fn toBytes(block: Block) [16]u8 { + pub fn toBytes(block: Block) callconv(.Inline) [16]u8 { var bytes: [16]u8 = undefined; mem.writeIntBig(u32, bytes[0..4], block.repr[0]); mem.writeIntBig(u32, bytes[4..8], block.repr[1]); @@ -37,7 +37,7 @@ pub const Block = struct { } /// XOR the block with a byte sequence. - pub inline fn xorBytes(block: Block, bytes: *const [16]u8) [16]u8 { + pub fn xorBytes(block: Block, bytes: *const [16]u8) callconv(.Inline) [16]u8 { const block_bytes = block.toBytes(); var x: [16]u8 = undefined; comptime var i: usize = 0; @@ -48,7 +48,7 @@ pub const Block = struct { } /// Encrypt a block with a round key. - pub inline fn encrypt(block: Block, round_key: Block) Block { + pub fn encrypt(block: Block, round_key: Block) callconv(.Inline) Block { const src = &block.repr; const s0 = block.repr[0]; @@ -65,7 +65,7 @@ pub const Block = struct { } /// Encrypt a block with the last round key. - pub inline fn encryptLast(block: Block, round_key: Block) Block { + pub fn encryptLast(block: Block, round_key: Block) callconv(.Inline) Block { const src = &block.repr; const t0 = block.repr[0]; @@ -87,7 +87,7 @@ pub const Block = struct { } /// Decrypt a block with a round key. - pub inline fn decrypt(block: Block, round_key: Block) Block { + pub fn decrypt(block: Block, round_key: Block) callconv(.Inline) Block { const src = &block.repr; const s0 = block.repr[0]; @@ -104,7 +104,7 @@ pub const Block = struct { } /// Decrypt a block with the last round key. - pub inline fn decryptLast(block: Block, round_key: Block) Block { + pub fn decryptLast(block: Block, round_key: Block) callconv(.Inline) Block { const src = &block.repr; const t0 = block.repr[0]; @@ -126,7 +126,7 @@ pub const Block = struct { } /// Apply the bitwise XOR operation to the content of two blocks. - pub inline fn xorBlocks(block1: Block, block2: Block) Block { + pub fn xorBlocks(block1: Block, block2: Block) callconv(.Inline) Block { var x: BlockVec = undefined; comptime var i = 0; inline while (i < 4) : (i += 1) { @@ -136,7 +136,7 @@ pub const Block = struct { } /// Apply the bitwise AND operation to the content of two blocks. - pub inline fn andBlocks(block1: Block, block2: Block) Block { + pub fn andBlocks(block1: Block, block2: Block) callconv(.Inline) Block { var x: BlockVec = undefined; comptime var i = 0; inline while (i < 4) : (i += 1) { @@ -146,7 +146,7 @@ pub const Block = struct { } /// Apply the bitwise OR operation to the content of two blocks. - pub inline fn orBlocks(block1: Block, block2: Block) Block { + pub fn orBlocks(block1: Block, block2: Block) callconv(.Inline) Block { var x: BlockVec = undefined; comptime var i = 0; inline while (i < 4) : (i += 1) { diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig index e3d3192bf8..7a65487135 100644 --- a/lib/std/crypto/blake3.zig +++ b/lib/std/crypto/blake3.zig @@ -66,7 +66,7 @@ const CompressVectorized = struct { const Lane = Vector(4, u32); const Rows = [4]Lane; - inline fn g(comptime even: bool, rows: *Rows, m: Lane) void { + fn g(comptime even: bool, rows: *Rows, m: Lane) callconv(.Inline) void { rows[0] +%= rows[1] +% m; rows[3] ^= rows[0]; rows[3] = math.rotr(Lane, rows[3], if (even) 8 else 16); @@ -75,13 +75,13 @@ const CompressVectorized = struct { rows[1] = math.rotr(Lane, rows[1], if (even) 7 else 12); } - inline fn diagonalize(rows: *Rows) void { + fn diagonalize(rows: *Rows) callconv(.Inline) void { rows[0] = @shuffle(u32, rows[0], undefined, [_]i32{ 3, 0, 1, 2 }); rows[3] = @shuffle(u32, rows[3], undefined, [_]i32{ 2, 3, 0, 1 }); rows[2] = @shuffle(u32, rows[2], undefined, [_]i32{ 1, 2, 3, 0 }); } - inline fn undiagonalize(rows: *Rows) void { + fn undiagonalize(rows: *Rows) callconv(.Inline) void { rows[0] = @shuffle(u32, rows[0], undefined, [_]i32{ 1, 2, 3, 0 }); rows[3] = @shuffle(u32, rows[3], undefined, [_]i32{ 2, 3, 0, 1 }); rows[2] = @shuffle(u32, rows[2], undefined, [_]i32{ 3, 0, 1, 2 }); diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index 8923bac26f..0f79707279 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -35,7 +35,7 @@ const ChaCha20VecImpl = struct { }; } - inline fn chacha20Core(x: *BlockVec, input: BlockVec) void { + fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void { x.* = input; var r: usize = 0; @@ -80,7 +80,7 @@ const ChaCha20VecImpl = struct { } } - inline fn hashToBytes(out: *[64]u8, x: BlockVec) void { + fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void { var i: usize = 0; while (i < 4) : (i += 1) { mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i][0]); @@ -90,7 +90,7 @@ const ChaCha20VecImpl = struct { } } - inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void { + fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void { x[0] +%= ctx[0]; x[1] +%= ctx[1]; x[2] +%= ctx[2]; @@ -190,7 +190,7 @@ const ChaCha20NonVecImpl = struct { }; } - inline fn chacha20Core(x: *BlockVec, input: BlockVec) void { + fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void { x.* = input; const rounds = comptime [_]QuarterRound{ @@ -219,7 +219,7 @@ const ChaCha20NonVecImpl = struct { } } - inline fn hashToBytes(out: *[64]u8, x: BlockVec) void { + fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void { var i: usize = 0; while (i < 4) : (i += 1) { mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]); @@ -229,7 +229,7 @@ const ChaCha20NonVecImpl = struct { } } - inline fn contextFeedback(x: *BlockVec, ctx: BlockVec) void { + fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void { var i: usize = 0; while (i < 16) : (i += 1) { x[i] +%= ctx[i]; diff --git a/lib/std/crypto/ghash.zig b/lib/std/crypto/ghash.zig index 3d56469d8e..ffc9ef41ae 100644 --- a/lib/std/crypto/ghash.zig +++ b/lib/std/crypto/ghash.zig @@ -95,7 +95,7 @@ pub const Ghash = struct { } } - inline fn clmul_pclmul(x: u64, y: u64) u64 { + fn clmul_pclmul(x: u64, y: u64) callconv(.Inline) u64 { const Vector = std.meta.Vector; const product = asm ( \\ vpclmulqdq $0x00, %[x], %[y], %[out] @@ -106,7 +106,7 @@ pub const Ghash = struct { return product[0]; } - inline fn clmul_pmull(x: u64, y: u64) u64 { + fn clmul_pmull(x: u64, y: u64) callconv(.Inline) u64 { const Vector = std.meta.Vector; const product = asm ( \\ pmull %[out].1q, %[x].1d, %[y].1d diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index a5a7fb8c59..4809b9b6f7 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -48,7 +48,7 @@ pub const State = struct { return mem.asBytes(&self.data); } - inline fn endianSwap(self: *Self) void { + fn endianSwap(self: *Self) callconv(.Inline) void { for (self.data) |*w| { w.* = mem.littleToNative(u32, w.*); } @@ -116,7 +116,7 @@ pub const State = struct { const Lane = Vector(4, u32); - inline fn shift(x: Lane, comptime n: comptime_int) Lane { + fn shift(x: Lane, comptime n: comptime_int) callconv(.Inline) Lane { return x << @splat(4, @as(u5, n)); } diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 14505865cf..e22668f998 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -37,7 +37,7 @@ const Salsa20VecImpl = struct { }; } - inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void { + fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) callconv(.Inline) void { const n1n2n3n0 = Lane{ input[3][1], input[3][2], input[3][3], input[3][0] }; const n1n2 = Half{ n1n2n3n0[0], n1n2n3n0[1] }; const n3n0 = Half{ n1n2n3n0[2], n1n2n3n0[3] }; @@ -211,7 +211,7 @@ const Salsa20NonVecImpl = struct { d: u6, }; - inline fn Rp(a: usize, b: usize, c: usize, d: u6) QuarterRound { + fn Rp(a: usize, b: usize, c: usize, d: u6) callconv(.Inline) QuarterRound { return QuarterRound{ .a = a, .b = b, @@ -220,7 +220,7 @@ const Salsa20NonVecImpl = struct { }; } - inline fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) void { + fn salsa20Core(x: *BlockVec, input: BlockVec, comptime feedback: bool) callconv(.Inline) void { const arx_steps = comptime [_]QuarterRound{ Rp(4, 0, 12, 7), Rp(8, 4, 0, 9), Rp(12, 8, 4, 13), Rp(0, 12, 8, 18), Rp(9, 5, 1, 7), Rp(13, 9, 5, 9), Rp(1, 13, 9, 13), Rp(5, 1, 13, 18), diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 6dfa373414..cfb6b448c0 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -720,10 +720,10 @@ pub const Elf32_Rel = extern struct { r_offset: Elf32_Addr, r_info: Elf32_Word, - pub inline fn r_sym(self: @This()) u24 { + pub fn r_sym(self: @This()) callconv(.Inline) u24 { return @truncate(u24, self.r_info >> 8); } - pub inline fn r_type(self: @This()) u8 { + pub fn r_type(self: @This()) callconv(.Inline) u8 { return @truncate(u8, self.r_info & 0xff); } }; @@ -731,10 +731,10 @@ pub const Elf64_Rel = extern struct { r_offset: Elf64_Addr, r_info: Elf64_Xword, - pub inline fn r_sym(self: @This()) u32 { + pub fn r_sym(self: @This()) callconv(.Inline) u32 { return @truncate(u32, self.r_info >> 32); } - pub inline fn r_type(self: @This()) u32 { + pub fn r_type(self: @This()) callconv(.Inline) u32 { return @truncate(u32, self.r_info & 0xffffffff); } }; @@ -743,10 +743,10 @@ pub const Elf32_Rela = extern struct { r_info: Elf32_Word, r_addend: Elf32_Sword, - pub inline fn r_sym(self: @This()) u24 { + pub fn r_sym(self: @This()) callconv(.Inline) u24 { return @truncate(u24, self.r_info >> 8); } - pub inline fn r_type(self: @This()) u8 { + pub fn r_type(self: @This()) callconv(.Inline) u8 { return @truncate(u8, self.r_info & 0xff); } }; @@ -755,10 +755,10 @@ pub const Elf64_Rela = extern struct { r_info: Elf64_Xword, r_addend: Elf64_Sxword, - pub inline fn r_sym(self: @This()) u32 { + pub fn r_sym(self: @This()) callconv(.Inline) u32 { return @truncate(u32, self.r_info >> 32); } - pub inline fn r_type(self: @This()) u32 { + pub fn r_type(self: @This()) callconv(.Inline) u32 { return @truncate(u32, self.r_info & 0xffffffff); } }; diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index caa88520ac..324b06898e 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -52,21 +52,21 @@ const Z96 = struct { d2: u32, // d = s >> 1 - inline fn shiftRight1(d: *Z96, s: Z96) void { + fn shiftRight1(d: *Z96, s: Z96) callconv(.Inline) void { d.d0 = (s.d0 >> 1) | ((s.d1 & 1) << 31); d.d1 = (s.d1 >> 1) | ((s.d2 & 1) << 31); d.d2 = s.d2 >> 1; } // d = s << 1 - inline fn shiftLeft1(d: *Z96, s: Z96) void { + fn shiftLeft1(d: *Z96, s: Z96) callconv(.Inline) void { d.d2 = (s.d2 << 1) | ((s.d1 & (1 << 31)) >> 31); d.d1 = (s.d1 << 1) | ((s.d0 & (1 << 31)) >> 31); d.d0 = s.d0 << 1; } // d += s - inline fn add(d: *Z96, s: Z96) void { + fn add(d: *Z96, s: Z96) callconv(.Inline) void { var w = @as(u64, d.d0) + @as(u64, s.d0); d.d0 = @truncate(u32, w); @@ -80,7 +80,7 @@ const Z96 = struct { } // d -= s - inline fn sub(d: *Z96, s: Z96) void { + fn sub(d: *Z96, s: Z96) callconv(.Inline) void { var w = @as(u64, d.d0) -% @as(u64, s.d0); d.d0 = @truncate(u32, w); diff --git a/lib/std/hash/cityhash.zig b/lib/std/hash/cityhash.zig index c2e7db4d49..d7f2f1a9eb 100644 --- a/lib/std/hash/cityhash.zig +++ b/lib/std/hash/cityhash.zig @@ -6,7 +6,7 @@ const std = @import("std"); const builtin = @import("builtin"); -inline fn offsetPtr(ptr: [*]const u8, offset: usize) [*]const u8 { +fn offsetPtr(ptr: [*]const u8, offset: usize) callconv(.Inline) [*]const u8 { // ptr + offset doesn't work at comptime so we need this instead. return @ptrCast([*]const u8, &ptr[offset]); } diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index e2c4eb055b..8529c5e3db 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -815,16 +815,16 @@ pub const sigval = extern union { pub const _SIG_WORDS = 4; pub const _SIG_MAXSIG = 128; -pub inline fn _SIG_IDX(sig: usize) usize { +pub fn _SIG_IDX(sig: usize) callconv(.Inline) usize { return sig - 1; } -pub inline fn _SIG_WORD(sig: usize) usize { +pub fn _SIG_WORD(sig: usize) callconv(.Inline) usize { return_SIG_IDX(sig) >> 5; } -pub inline fn _SIG_BIT(sig: usize) usize { +pub fn _SIG_BIT(sig: usize) callconv(.Inline) usize { return 1 << (_SIG_IDX(sig) & 31); } -pub inline fn _SIG_VALID(sig: usize) usize { +pub fn _SIG_VALID(sig: usize) callconv(.Inline) usize { return sig <= _SIG_MAXSIG and sig > 0; } diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 1780255aac..f8b950ea86 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -796,16 +796,16 @@ pub const _ksiginfo = extern struct { pub const _SIG_WORDS = 4; pub const _SIG_MAXSIG = 128; -pub inline fn _SIG_IDX(sig: usize) usize { +pub fn _SIG_IDX(sig: usize) callconv(.Inline) usize { return sig - 1; } -pub inline fn _SIG_WORD(sig: usize) usize { +pub fn _SIG_WORD(sig: usize) callconv(.Inline) usize { return_SIG_IDX(sig) >> 5; } -pub inline fn _SIG_BIT(sig: usize) usize { +pub fn _SIG_BIT(sig: usize) callconv(.Inline) usize { return 1 << (_SIG_IDX(sig) & 31); } -pub inline fn _SIG_VALID(sig: usize) usize { +pub fn _SIG_VALID(sig: usize) callconv(.Inline) usize { return sig <= _SIG_MAXSIG and sig > 0; } diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index ffc1029708..b7534db191 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -126,7 +126,7 @@ pub fn fork() usize { /// It is advised to avoid this function and use clone instead, because /// the compiler is not aware of how vfork affects control flow and you may /// see different results in optimized builds. -pub inline fn vfork() usize { +pub fn vfork() callconv(.Inline) usize { return @call(.{ .modifier = .always_inline }, syscall0, .{.vfork}); } diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index a9c0f6cb56..614f5b4395 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -300,7 +300,7 @@ fn initTLS() void { }; } -inline fn alignPtrCast(comptime T: type, ptr: [*]u8) *T { +fn alignPtrCast(comptime T: type, ptr: [*]u8) callconv(.Inline) *T { return @ptrCast(*T, @alignCast(@alignOf(*T), ptr)); } diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index e1e325d9f6..6f67b65252 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1669,7 +1669,7 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace { return path_space; } -inline fn MAKELANGID(p: c_ushort, s: c_ushort) LANGID { +fn MAKELANGID(p: c_ushort, s: c_ushort) callconv(.Inline) LANGID { return (s << 10) | p; } diff --git a/lib/std/start.zig b/lib/std/start.zig index 718d48130e..0fb96c768f 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -262,7 +262,7 @@ const bad_main_ret = "expected return type of main to be 'void', '!void', 'noret // This is marked inline because for some reason LLVM in release mode fails to inline it, // and we want fewer call frames in stack traces. -inline fn initEventLoopAndCallMain() u8 { +fn initEventLoopAndCallMain() callconv(.Inline) u8 { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { @@ -291,7 +291,7 @@ inline fn initEventLoopAndCallMain() u8 { // and we want fewer call frames in stack traces. // TODO This function is duplicated from initEventLoopAndCallMain instead of using generics // because it is working around stage1 compiler bugs. -inline fn initEventLoopAndCallWinMain() std.os.windows.INT { +fn initEventLoopAndCallWinMain() callconv(.Inline) std.os.windows.INT { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2f0b7ff082..822e9006c4 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -2355,17 +2355,17 @@ test "zig fmt: functions" { \\extern fn puts(s: *const u8) c_int; \\extern "c" fn puts(s: *const u8) c_int; \\export fn puts(s: *const u8) c_int; - \\inline fn puts(s: *const u8) c_int; + \\fn puts(s: *const u8) callconv(.Inline) c_int; \\noinline fn puts(s: *const u8) c_int; \\pub extern fn puts(s: *const u8) c_int; \\pub extern "c" fn puts(s: *const u8) c_int; \\pub export fn puts(s: *const u8) c_int; - \\pub inline fn puts(s: *const u8) c_int; + \\pub fn puts(s: *const u8) callconv(.Inline) c_int; \\pub noinline fn puts(s: *const u8) c_int; \\pub extern fn puts(s: *const u8) align(2 + 2) c_int; \\pub extern "c" fn puts(s: *const u8) align(2 + 2) c_int; \\pub export fn puts(s: *const u8) align(2 + 2) c_int; - \\pub inline fn puts(s: *const u8) align(2 + 2) c_int; + \\pub fn puts(s: *const u8) align(2 + 2) callconv(.Inline) c_int; \\pub noinline fn puts(s: *const u8) align(2 + 2) c_int; \\ ); diff --git a/lib/std/zig/system/x86.zig b/lib/std/zig/system/x86.zig index 24aebb5a61..bda9a17c95 100644 --- a/lib/std/zig/system/x86.zig +++ b/lib/std/zig/system/x86.zig @@ -19,11 +19,11 @@ fn setFeature(cpu: *Target.Cpu, feature: Target.x86.Feature, enabled: bool) void if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx); } -inline fn bit(input: u32, offset: u5) bool { +fn bit(input: u32, offset: u5) callconv(.Inline) bool { return (input >> offset) & 1 != 0; } -inline fn hasMask(input: u32, mask: u32) bool { +fn hasMask(input: u32, mask: u32) callconv(.Inline) bool { return (input & mask) == mask; } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index eee5841903..fd1c53cb67 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2366,7 +2366,7 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { return min_pos - start; } -inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { +fn checkForCollision(start: u64, end: u64, off: u64, size: u64) callconv(.Inline) ?u64 { const increased_size = padToIdeal(size); const test_end = off + increased_size; if (end > off and start < test_end) { diff --git a/src/tracy.zig b/src/tracy.zig index 6f56a87ce6..3f6cf56588 100644 --- a/src/tracy.zig +++ b/src/tracy.zig @@ -31,7 +31,7 @@ pub const Ctx = if (enable) ___tracy_c_zone_context else struct { pub fn end(self: Ctx) void {} }; -pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx { +pub fn trace(comptime src: std.builtin.SourceLocation) callconv(.Inline) Ctx { if (!enable) return .{}; const loc: ___tracy_source_location_data = .{ diff --git a/test/cli.zig b/test/cli.zig index 8dbef06887..8787dab277 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -113,7 +113,7 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { \\ return num * num; \\} \\extern fn zig_panic() noreturn; - \\pub inline fn panic(msg: []const u8, error_return_trace: ?*@import("builtin").StackTrace) noreturn { + \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("builtin").StackTrace) callconv(.Inline) noreturn { \\ zig_panic(); \\} ); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3b4eb61195..2fb4c36ed4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1648,7 +1648,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @call(.{ .modifier = .compile_time }, baz, .{}); \\} \\fn foo() void {} - \\inline fn bar() void {} + \\fn bar() callconv(.Inline) void {} \\fn baz1() void {} \\fn baz2() void {} , &[_][]const u8{ @@ -3944,7 +3944,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ var a = b; \\} - \\inline fn b() void { } + \\fn b() callconv(.Inline) void { } , &[_][]const u8{ "tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var", "tmp.zig:4:1: note: declared here", @@ -6782,11 +6782,11 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { // \\export fn foo() void { // \\ bar(); // \\} - // \\inline fn bar() void { + // \\fn bar() callconv(.Inline) void { // \\ baz(); // \\ quux(); // \\} - // \\inline fn baz() void { + // \\fn baz() callconv(.Inline) void { // \\ bar(); // \\ quux(); // \\} @@ -6799,7 +6799,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { // \\export fn foo() void { // \\ quux(@ptrToInt(bar)); // \\} - // \\inline fn bar() void { } + // \\fn bar() callconv(.Inline) void { } // \\extern fn quux(usize) void; //, &[_][]const u8{ // "tmp.zig:4:1: error: unable to inline function", @@ -7207,7 +7207,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ foo(); \\} - \\inline fn foo() void { + \\fn foo() callconv(.Inline) void { \\ @setAlignStack(16); \\} , &[_][]const u8{ diff --git a/test/stage1/behavior/fn.zig b/test/stage1/behavior/fn.zig index dd69d00c60..a1e726c565 100644 --- a/test/stage1/behavior/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -113,7 +113,7 @@ test "assign inline fn to const variable" { a(); } -inline fn inlineFn() void {} +fn inlineFn() callconv(.Inline) void {} test "pass by non-copying value" { expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 0eb2cf68b4..35ae1dbf12 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -179,7 +179,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ return y - 1; \\} \\ - \\inline fn rec(n: usize) usize { + \\fn rec(n: usize) callconv(.Inline) usize { \\ if (n <= 1) return n; \\ return rec(n - 1); \\} diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 78d7eba262..486edeb864 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -255,7 +255,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ exit(y - 6); \\} \\ - \\inline fn add(a: usize, b: usize, c: usize) usize { + \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { \\ return a + b + c; \\} \\ @@ -1228,7 +1228,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ exit(y - 6); \\} \\ - \\inline fn add(a: usize, b: usize, c: usize) usize { + \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { \\ if (a == 10) @compileError("bad"); \\ return a + b + c; \\} @@ -1251,7 +1251,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ exit(y - 6); \\} \\ - \\inline fn add(a: usize, b: usize, c: usize) usize { + \\fn add(a: usize, b: usize, c: usize) callconv(.Inline) usize { \\ if (a == 10) @compileError("bad"); \\ return a + b + c; \\} @@ -1277,7 +1277,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ exit(y - 21); \\} \\ - \\inline fn fibonacci(n: usize) usize { + \\fn fibonacci(n: usize) callconv(.Inline) usize { \\ if (n <= 2) return n; \\ return fibonacci(n - 2) + fibonacci(n - 1); \\} @@ -1300,7 +1300,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ exit(y - 21); \\} \\ - \\inline fn fibonacci(n: usize) usize { + \\fn fibonacci(n: usize) callconv(.Inline) usize { \\ if (n <= 2) return n; \\ return fibonacci(n - 2) + fibonacci(n - 1); \\} diff --git a/test/translate_c.zig b/test/translate_c.zig index 03ca87d5f6..b8e00a690b 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -43,7 +43,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); , - \\pub inline fn _AL_READ3BYTES(p: anytype) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) { + \\pub fn _AL_READ3BYTES(p: anytype) callconv(.Inline) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) { \\ return ((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16); \\} }); @@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; \\pub const Color = struct_Color; , - \\pub inline fn CLITERAL(type_1: anytype) @TypeOf(type_1) { + \\pub fn CLITERAL(type_1: anytype) callconv(.Inline) @TypeOf(type_1) { \\ return type_1; \\} , @@ -148,7 +148,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("correct semicolon after infixop", \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0) , &[_][]const u8{ - \\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) { + \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) { \\ return ((_fp.*._flags) & _IO_ERR_SEEN) != 0; \\} }); @@ -157,7 +157,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define FOO(x) ((x >= 0) + (x >= 0)) \\#define BAR 1 && 2 > 4 , &[_][]const u8{ - \\pub inline fn FOO(x: anytype) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) { + \\pub fn FOO(x: anytype) callconv(.Inline) @TypeOf(@boolToInt(x >= 0) + @boolToInt(x >= 0)) { \\ return @boolToInt(x >= 0) + @boolToInt(x >= 0); \\} , @@ -208,7 +208,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ break :blk bar; \\}; , - \\pub inline fn bar(x: anytype) @TypeOf(baz(1, 2)) { + \\pub fn bar(x: anytype) callconv(.Inline) @TypeOf(baz(1, 2)) { \\ return blk: { \\ _ = &x; \\ _ = 3; @@ -1590,13 +1590,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub extern var fn_ptr: ?fn () callconv(.C) void; , - \\pub inline fn foo() void { + \\pub fn foo() callconv(.Inline) void { \\ return fn_ptr.?(); \\} , \\pub extern var fn_ptr2: ?fn (c_int, f32) callconv(.C) u8; , - \\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 { + \\pub fn bar(arg_1: c_int, arg_2: f32) callconv(.Inline) u8 { \\ return fn_ptr2.?(arg_1, arg_2); \\} }); @@ -1629,7 +1629,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const glClearPFN = PFNGLCLEARPROC; , - \\pub inline fn glClearUnion(arg_2: GLbitfield) void { + \\pub fn glClearUnion(arg_2: GLbitfield) callconv(.Inline) void { \\ return glProcs.gl.Clear.?(arg_2); \\} , @@ -1650,15 +1650,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub extern var c: c_int; , - \\pub inline fn BASIC(c_1: anytype) @TypeOf(c_1 * 2) { + \\pub fn BASIC(c_1: anytype) callconv(.Inline) @TypeOf(c_1 * 2) { \\ return c_1 * 2; \\} , - \\pub inline fn FOO(L: anytype, b: anytype) @TypeOf(L + b) { + \\pub fn FOO(L: anytype, b: anytype) callconv(.Inline) @TypeOf(L + b) { \\ return L + b; \\} , - \\pub inline fn BAR() @TypeOf(c * c) { + \\pub fn BAR() callconv(.Inline) @TypeOf(c * c) { \\ return c * c; \\} }); @@ -2298,7 +2298,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro call", \\#define CALL(arg) bar(arg) , &[_][]const u8{ - \\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) { + \\pub fn CALL(arg: anytype) callconv(.Inline) @TypeOf(bar(arg)) { \\ return bar(arg); \\} }); @@ -2860,7 +2860,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define BAR (void*) a \\#define BAZ (uint32_t)(2) , &[_][]const u8{ - \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) { + \\pub fn FOO(bar: anytype) callconv(.Inline) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) { \\ return baz((@import("std").meta.cast(?*c_void, baz))); \\} , @@ -2902,11 +2902,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define MIN(a, b) ((b) < (a) ? (b) : (a)) \\#define MAX(a, b) ((b) > (a) ? (b) : (a)) , &[_][]const u8{ - \\pub inline fn MIN(a: anytype, b: anytype) @TypeOf(if (b < a) b else a) { + \\pub fn MIN(a: anytype, b: anytype) callconv(.Inline) @TypeOf(if (b < a) b else a) { \\ return if (b < a) b else a; \\} , - \\pub inline fn MAX(a: anytype, b: anytype) @TypeOf(if (b > a) b else a) { + \\pub fn MAX(a: anytype, b: anytype) callconv(.Inline) @TypeOf(if (b > a) b else a) { \\ return if (b > a) b else a; \\} }); @@ -3094,7 +3094,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen) \\ , &[_][]const u8{ - \\pub inline fn DefaultScreen(dpy: anytype) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) { + \\pub fn DefaultScreen(dpy: anytype) callconv(.Inline) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) { \\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen; \\} }); -- cgit v1.2.3 From bb4f4c043e7dde4e8b9fcbf0af9329d3fd08ff7b Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Tue, 9 Feb 2021 23:44:33 -0700 Subject: test/cli.zig: Remove inline from panic function in testGodboltApi Might add another line to stack traces there. --- test/cli.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/cli.zig b/test/cli.zig index 8787dab277..c0702fa54c 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -113,7 +113,7 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { \\ return num * num; \\} \\extern fn zig_panic() noreturn; - \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("builtin").StackTrace) callconv(.Inline) noreturn { + \\pub fn panic(msg: []const u8, error_return_trace: ?*@import("builtin").StackTrace) noreturn { \\ zig_panic(); \\} ); -- cgit v1.2.3 From d98f09e4f67fb2848be6052466db035450326605 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 11 Feb 2021 09:43:30 -0800 Subject: translate-c: comma operator should introduce a new scope This prevents inadvertent side-effects when an expression is not evaluated due to boolean short-circuiting Fixes #7989 --- src/translate_c.zig | 33 ++++++++++++++------------------- test/run_translated_c.zig | 13 +++++++++++++ test/translate_c.zig | 32 ++++++++++++++++++++++---------- 3 files changed, 49 insertions(+), 29 deletions(-) (limited to 'test') diff --git a/src/translate_c.zig b/src/translate_c.zig index 82a7695c13..4dc9453c85 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1438,30 +1438,25 @@ fn transBinaryOperator( switch (op) { .Assign => return try transCreateNodeAssign(rp, scope, result_used, stmt.getLHS(), stmt.getRHS()), .Comma => { - const block_scope = try scope.findBlockScope(rp.c); - const expr = block_scope.base.parent == scope; - const lparen = if (expr) try appendToken(rp.c, .LParen, "(") else undefined; + var block_scope = try Scope.Block.init(rp.c, scope, true); + const lparen = try appendToken(rp.c, .LParen, "("); const lhs = try transExpr(rp, &block_scope.base, stmt.getLHS(), .unused, .r_value); try block_scope.statements.append(lhs); const rhs = try transExpr(rp, &block_scope.base, stmt.getRHS(), .used, .r_value); - if (expr) { - _ = try appendToken(rp.c, .Semicolon, ";"); - const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs); - try block_scope.statements.append(&break_node.base); - const block_node = try block_scope.complete(rp.c); - const rparen = try appendToken(rp.c, .RParen, ")"); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = lparen, - .expr = block_node, - .rparen = rparen, - }; - return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); - } else { - return maybeSuppressResult(rp, scope, result_used, rhs); - } + _ = try appendToken(rp.c, .Semicolon, ";"); + const break_node = try transCreateNodeBreak(rp.c, block_scope.label, rhs); + try block_scope.statements.append(&break_node.base); + const block_node = try block_scope.complete(rp.c); + const rparen = try appendToken(rp.c, .RParen, ")"); + const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); + grouped_expr.* = .{ + .lparen = lparen, + .expr = block_node, + .rparen = rparen, + }; + return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); }, .Div => { if (cIsSignedInteger(qt)) { diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index e28bdc96f0..b8af201e36 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -909,4 +909,17 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 1 != 1; \\} , ""); + + cases.add("Comma operator should create new scope; issue #7989", + \\#include + \\#include + \\int main(void) { + \\ if (1 || (abort(), 1)) {} + \\ if (0 && (1, printf("do not print\n"))) {} + \\ int x = 0; + \\ x = (x = 3, 4, x + 1); + \\ if (x != 4) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 03ca87d5f6..2097e17323 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1723,11 +1723,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export fn foo() c_int { - \\ _ = @as(c_int, 2); - \\ _ = @as(c_int, 4); - \\ _ = @as(c_int, 2); - \\ _ = @as(c_int, 4); - \\ return @as(c_int, 6); + \\ _ = (blk: { + \\ _ = @as(c_int, 2); + \\ break :blk @as(c_int, 4); + \\ }); + \\ return (blk: { + \\ _ = (blk_1: { + \\ _ = @as(c_int, 2); + \\ break :blk_1 @as(c_int, 4); + \\ }); + \\ break :blk @as(c_int, 6); + \\ }); \\} }); @@ -1774,8 +1780,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ while (true) { \\ var a_1: c_int = 4; \\ a_1 = 9; - \\ _ = @as(c_int, 6); - \\ return a_1; + \\ return (blk: { + \\ _ = @as(c_int, 6); + \\ break :blk a_1; + \\ }); \\ } \\ while (true) { \\ var a_1: c_int = 2; @@ -1805,9 +1813,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var b: c_int = 4; \\ while ((i + @as(c_int, 2)) != 0) : (i = 2) { \\ var a: c_int = 2; - \\ a = 6; - \\ _ = @as(c_int, 5); - \\ _ = @as(c_int, 7); + \\ _ = (blk: { + \\ _ = (blk_1: { + \\ a = 6; + \\ break :blk_1 @as(c_int, 5); + \\ }); + \\ break :blk @as(c_int, 7); + \\ }); \\ } \\ } \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2))); -- cgit v1.2.3