| author | |
| committer | |
| log | b54514d9dd15225ef2578b33c4c384db4680b90b |
| tree | e2e3c9482567d2e187c18ebf3ae6f99a4c65b60e |
| parent | 75a7abb0c47d9f5e99b1f69250776528fa60f569 |
| signature |
* translate-c: Use [N:0] arrays when initializer is a string literal
Translate incomplete arrays as [N:0] when initialized by a string literal.
This preserves a bit more of the type information from the original C program.
Fixes #82153 files changed, 210 insertions(+), 85 deletions(-)
src/translate_c.zig+78-50| ... | @@ -636,7 +636,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co | ... | @@ -636,7 +636,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co |
| 636 | if (has_init) trans_init: { | 636 | if (has_init) trans_init: { |
| 637 | if (decl_init) |expr| { | 637 | if (decl_init) |expr| { |
| 638 | const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) | 638 | const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) |
| 639 | transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0) | 639 | transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) |
| 640 | else | 640 | else |
| 641 | transExprCoercing(c, scope, expr, .used); | 641 | transExprCoercing(c, scope, expr, .used); |
| 642 | init_node = node_or_error catch |err| switch (err) { | 642 | init_node = node_or_error catch |err| switch (err) { |
| ... | @@ -1412,7 +1412,7 @@ fn transDeclStmtOne( | ... | @@ -1412,7 +1412,7 @@ fn transDeclStmtOne( |
| 1412 | 1412 | ||
| 1413 | var init_node = if (decl_init) |expr| | 1413 | var init_node = if (decl_init) |expr| |
| 1414 | if (expr.getStmtClass() == .StringLiteralClass) | 1414 | if (expr.getStmtClass() == .StringLiteralClass) |
| 1415 | try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node)) | 1415 | try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) |
| 1416 | else | 1416 | else |
| 1417 | try transExprCoercing(c, scope, expr, .used) | 1417 | try transExprCoercing(c, scope, expr, .used) |
| 1418 | else | 1418 | else |
| ... | @@ -1758,6 +1758,20 @@ fn transReturnStmt( | ... | @@ -1758,6 +1758,20 @@ fn transReturnStmt( |
| 1758 | return Tag.@"return".create(c.arena, rhs); | 1758 | return Tag.@"return".create(c.arena, rhs); |
| 1759 | } | 1759 | } |
| 1760 | 1760 | ||
| 1761 | fn transNarrowStringLiteral( | ||
| 1762 | c: *Context, | ||
| 1763 | scope: *Scope, | ||
| 1764 | stmt: *const clang.StringLiteral, | ||
| 1765 | result_used: ResultUsed, | ||
| 1766 | ) TransError!Node { | ||
| 1767 | var len: usize = undefined; | ||
| 1768 | const bytes_ptr = stmt.getString_bytes_begin_size(&len); | ||
| 1769 | |||
| 1770 | const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); | ||
| 1771 | const node = try Tag.string_literal.create(c.arena, str); | ||
| 1772 | return maybeSuppressResult(c, scope, result_used, node); | ||
| 1773 | } | ||
| 1774 | |||
| 1761 | fn transStringLiteral( | 1775 | fn transStringLiteral( |
| 1762 | c: *Context, | 1776 | c: *Context, |
| 1763 | scope: *Scope, | 1777 | scope: *Scope, |
| ... | @@ -1766,19 +1780,14 @@ fn transStringLiteral( | ... | @@ -1766,19 +1780,14 @@ fn transStringLiteral( |
| 1766 | ) TransError!Node { | 1780 | ) TransError!Node { |
| 1767 | const kind = stmt.getKind(); | 1781 | const kind = stmt.getKind(); |
| 1768 | switch (kind) { | 1782 | switch (kind) { |
| 1769 | .Ascii, .UTF8 => { | 1783 | .Ascii, .UTF8 => return transNarrowStringLiteral(c, scope, stmt, result_used), |
| 1770 | var len: usize = undefined; | ||
| 1771 | const bytes_ptr = stmt.getString_bytes_begin_size(&len); | ||
| 1772 | |||
| 1773 | const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); | ||
| 1774 | const node = try Tag.string_literal.create(c.arena, str); | ||
| 1775 | return maybeSuppressResult(c, scope, result_used, node); | ||
| 1776 | }, | ||
| 1777 | .UTF16, .UTF32, .Wide => { | 1784 | .UTF16, .UTF32, .Wide => { |
| 1778 | const str_type = @tagName(stmt.getKind()); | 1785 | const str_type = @tagName(stmt.getKind()); |
| 1779 | const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); | 1786 | const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); |
| 1780 | const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1); | ||
| 1781 | 1787 | ||
| 1788 | const expr_base = @ptrCast(*const clang.Expr, stmt); | ||
| 1789 | const array_type = try transQualTypeInitialized(c, scope, expr_base.getType(), expr_base, expr_base.getBeginLoc()); | ||
| 1790 | const lit_array = try transStringLiteralInitializer(c, scope, stmt, array_type); | ||
| 1782 | const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); | 1791 | const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); |
| 1783 | try scope.appendNode(decl); | 1792 | try scope.appendNode(decl); |
| 1784 | const node = try Tag.identifier.create(c.arena, name); | 1793 | const node = try Tag.identifier.create(c.arena, name); |
| ... | @@ -1787,52 +1796,67 @@ fn transStringLiteral( | ... | @@ -1787,52 +1796,67 @@ fn transStringLiteral( |
| 1787 | } | 1796 | } |
| 1788 | } | 1797 | } |
| 1789 | 1798 | ||
| 1790 | /// Parse the size of an array back out from an ast Node. | 1799 | fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo { |
| 1791 | fn zigArraySize(c: *Context, node: Node) TransError!usize { | 1800 | return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data; |
| 1792 | if (node.castTag(.array_type)) |array| { | ||
| 1793 | return array.data.len; | ||
| 1794 | } | ||
| 1795 | return error.UnsupportedTranslation; | ||
| 1796 | } | 1801 | } |
| 1797 | 1802 | ||
| 1798 | /// Translate a string literal to an array of integers. Used when an | 1803 | /// Translate a string literal that is initializing an array. In general narrow string |
| 1799 | /// array is initialized from a string literal. `array_size` is the | 1804 | /// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation. |
| 1800 | /// size of the array being initialized. If the string literal is larger | 1805 | /// Wide string literals become an array of integers. zero-fillers pad out the array to |
| 1801 | /// than the array, truncate the string. If the array is larger than the | 1806 | /// the appropriate length, if necessary. |
| 1802 | /// string literal, pad the array with 0's | 1807 | fn transStringLiteralInitializer( |
| 1803 | fn transStringLiteralAsArray( | ||
| 1804 | c: *Context, | 1808 | c: *Context, |
| 1805 | scope: *Scope, | 1809 | scope: *Scope, |
| 1806 | stmt: *const clang.StringLiteral, | 1810 | stmt: *const clang.StringLiteral, |
| 1807 | array_size: usize, | 1811 | array_type: Node, |
| 1808 | ) TransError!Node { | 1812 | ) TransError!Node { |
| 1809 | if (array_size == 0) return error.UnsupportedType; | 1813 | assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type); |
| 1814 | |||
| 1815 | const is_narrow = stmt.getKind() == .Ascii or stmt.getKind() == .UTF8; | ||
| 1810 | 1816 | ||
| 1811 | const str_length = stmt.getLength(); | 1817 | const str_length = stmt.getLength(); |
| 1818 | const payload = getArrayPayload(array_type); | ||
| 1819 | const array_size = payload.len; | ||
| 1820 | const elem_type = payload.elem_type; | ||
| 1821 | |||
| 1822 | if (array_size == 0) return Tag.empty_array.create(c.arena, elem_type); | ||
| 1823 | |||
| 1824 | const num_inits = math.min(str_length, array_size); | ||
| 1825 | const init_node = if (num_inits > 0) blk: { | ||
| 1826 | if (is_narrow) { | ||
| 1827 | // "string literal".* or string literal"[0..num_inits].* | ||
| 1828 | var str = try transNarrowStringLiteral(c, scope, stmt, .used); | ||
| 1829 | if (str_length != array_size) str = try Tag.string_slice.create(c.arena, .{ .string = str, .end = num_inits }); | ||
| 1830 | break :blk try Tag.deref.create(c.arena, str); | ||
| 1831 | } else { | ||
| 1832 | const init_list = try c.arena.alloc(Node, num_inits); | ||
| 1833 | var i: c_uint = 0; | ||
| 1834 | while (i < num_inits) : (i += 1) { | ||
| 1835 | init_list[i] = try transCreateCharLitNode(c, false, stmt.getCodeUnit(i)); | ||
| 1836 | } | ||
| 1837 | const init_args = .{ .len = num_inits, .elem_type = elem_type }; | ||
| 1838 | const init_array_type = try if (array_type.tag() == .array_type) Tag.array_type.create(c.arena, init_args) else Tag.null_sentinel_array_type.create(c.arena, init_args); | ||
| 1839 | break :blk try Tag.array_init.create(c.arena, .{ | ||
| 1840 | .cond = init_array_type, | ||
| 1841 | .cases = init_list, | ||
| 1842 | }); | ||
| 1843 | } | ||
| 1844 | } else null; | ||
| 1812 | 1845 | ||
| 1813 | const expr_base = @ptrCast(*const clang.Expr, stmt); | 1846 | if (num_inits == array_size) return init_node.?; // init_node is only null if num_inits == 0; but if num_inits == array_size == 0 we've already returned |
| 1814 | const ty = expr_base.getType().getTypePtr(); | 1847 | assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned. |
| 1815 | const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); | ||
| 1816 | 1848 | ||
| 1817 | const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc()); | 1849 | const filler_node = try Tag.array_filler.create(c.arena, .{ |
| 1818 | const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type }); | 1850 | .type = elem_type, |
| 1819 | const init_list = try c.arena.alloc(Node, array_size); | 1851 | .filler = Tag.zero_literal.init(), |
| 1852 | .count = array_size - str_length, | ||
| 1853 | }); | ||
| 1820 | 1854 | ||
| 1821 | var i: c_uint = 0; | 1855 | if (init_node) |some| { |
| 1822 | const kind = stmt.getKind(); | 1856 | return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node }); |
| 1823 | const narrow = kind == .Ascii or kind == .UTF8; | 1857 | } else { |
| 1824 | while (i < str_length and i < array_size) : (i += 1) { | 1858 | return filler_node; |
| 1825 | const code_unit = stmt.getCodeUnit(i); | ||
| 1826 | init_list[i] = try transCreateCharLitNode(c, narrow, code_unit); | ||
| 1827 | } | ||
| 1828 | while (i < array_size) : (i += 1) { | ||
| 1829 | init_list[i] = try transCreateNodeNumber(c, 0, .int); | ||
| 1830 | } | 1859 | } |
| 1831 | |||
| 1832 | return Tag.array_init.create(c.arena, .{ | ||
| 1833 | .cond = arr_type, | ||
| 1834 | .cases = init_list, | ||
| 1835 | }); | ||
| 1836 | } | 1860 | } |
| 1837 | 1861 | ||
| 1838 | /// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where | 1862 | /// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where |
| ... | @@ -3342,9 +3366,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { | ... | @@ -3342,9 +3366,8 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { |
| 3342 | try c.global_scope.nodes.append(decl_node); | 3366 | try c.global_scope.nodes.append(decl_node); |
| 3343 | } | 3367 | } |
| 3344 | 3368 | ||
| 3345 | /// Translate a qual type for a variable with an initializer. The initializer | 3369 | /// Translate a qualtype for a variable with an initializer. This only matters |
| 3346 | /// only matters for incomplete arrays, since the size of the array is determined | 3370 | /// for incomplete arrays, since the initializer determines the size of the array. |
| 3347 | /// by the size of the initializer | ||
| 3348 | fn transQualTypeInitialized( | 3371 | fn transQualTypeInitialized( |
| 3349 | c: *Context, | 3372 | c: *Context, |
| 3350 | scope: *Scope, | 3373 | scope: *Scope, |
| ... | @@ -3360,9 +3383,14 @@ fn transQualTypeInitialized( | ... | @@ -3360,9 +3383,14 @@ fn transQualTypeInitialized( |
| 3360 | switch (decl_init.getStmtClass()) { | 3383 | switch (decl_init.getStmtClass()) { |
| 3361 | .StringLiteralClass => { | 3384 | .StringLiteralClass => { |
| 3362 | const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); | 3385 | const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); |
| 3363 | const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator | 3386 | const string_lit_size = string_lit.getLength(); |
| 3364 | const array_size = @intCast(usize, string_lit_size); | 3387 | const array_size = @intCast(usize, string_lit_size); |
| 3365 | return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); | 3388 | |
| 3389 | // incomplete array initialized with empty string, will be translated as [1]T{0} | ||
| 3390 | // see https://github.com/ziglang/zig/issues/8256 | ||
| 3391 | if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty }); | ||
| 3392 | |||
| 3393 | return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); | ||
| 3366 | }, | 3394 | }, |
| 3367 | .InitListExprClass => { | 3395 | .InitListExprClass => { |
| 3368 | const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); | 3396 | const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); |
src/translate_c/ast.zig+83-3| ... | @@ -40,6 +40,8 @@ pub const Node = extern union { | ... | @@ -40,6 +40,8 @@ pub const Node = extern union { |
| 40 | string_literal, | 40 | string_literal, |
| 41 | char_literal, | 41 | char_literal, |
| 42 | enum_literal, | 42 | enum_literal, |
| 43 | /// "string"[0..end] | ||
| 44 | string_slice, | ||
| 43 | identifier, | 45 | identifier, |
| 44 | @"if", | 46 | @"if", |
| 45 | /// if (!operand) break; | 47 | /// if (!operand) break; |
| ... | @@ -176,6 +178,7 @@ pub const Node = extern union { | ... | @@ -176,6 +178,7 @@ pub const Node = extern union { |
| 176 | c_pointer, | 178 | c_pointer, |
| 177 | single_pointer, | 179 | single_pointer, |
| 178 | array_type, | 180 | array_type, |
| 181 | null_sentinel_array_type, | ||
| 179 | 182 | ||
| 180 | /// @import("std").meta.sizeof(operand) | 183 | /// @import("std").meta.sizeof(operand) |
| 181 | std_meta_sizeof, | 184 | std_meta_sizeof, |
| ... | @@ -334,7 +337,7 @@ pub const Node = extern union { | ... | @@ -334,7 +337,7 @@ pub const Node = extern union { |
| 334 | .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral, | 337 | .std_meta_promoteIntLiteral => Payload.PromoteIntLiteral, |
| 335 | .block => Payload.Block, | 338 | .block => Payload.Block, |
| 336 | .c_pointer, .single_pointer => Payload.Pointer, | 339 | .c_pointer, .single_pointer => Payload.Pointer, |
| 337 | .array_type => Payload.Array, | 340 | .array_type, .null_sentinel_array_type => Payload.Array, |
| 338 | .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, | 341 | .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, |
| 339 | .log2_int_type => Payload.Log2IntType, | 342 | .log2_int_type => Payload.Log2IntType, |
| 340 | .var_simple, .pub_var_simple => Payload.SimpleVarDecl, | 343 | .var_simple, .pub_var_simple => Payload.SimpleVarDecl, |
| ... | @@ -342,6 +345,7 @@ pub const Node = extern union { | ... | @@ -342,6 +345,7 @@ pub const Node = extern union { |
| 342 | .array_filler => Payload.ArrayFiller, | 345 | .array_filler => Payload.ArrayFiller, |
| 343 | .pub_inline_fn => Payload.PubInlineFn, | 346 | .pub_inline_fn => Payload.PubInlineFn, |
| 344 | .field_access => Payload.FieldAccess, | 347 | .field_access => Payload.FieldAccess, |
| 348 | .string_slice => Payload.StringSlice, | ||
| 345 | }; | 349 | }; |
| 346 | } | 350 | } |
| 347 | 351 | ||
| ... | @@ -584,10 +588,12 @@ pub const Payload = struct { | ... | @@ -584,10 +588,12 @@ pub const Payload = struct { |
| 584 | 588 | ||
| 585 | pub const Array = struct { | 589 | pub const Array = struct { |
| 586 | base: Payload, | 590 | base: Payload, |
| 587 | data: struct { | 591 | data: ArrayTypeInfo, |
| 592 | |||
| 593 | pub const ArrayTypeInfo = struct { | ||
| 588 | elem_type: Node, | 594 | elem_type: Node, |
| 589 | len: usize, | 595 | len: usize, |
| 590 | }, | 596 | }; |
| 591 | }; | 597 | }; |
| 592 | 598 | ||
| 593 | pub const Pointer = struct { | 599 | pub const Pointer = struct { |
| ... | @@ -664,6 +670,14 @@ pub const Payload = struct { | ... | @@ -664,6 +670,14 @@ pub const Payload = struct { |
| 664 | radix: Node, | 670 | radix: Node, |
| 665 | }, | 671 | }, |
| 666 | }; | 672 | }; |
| 673 | |||
| 674 | pub const StringSlice = struct { | ||
| 675 | base: Payload, | ||
| 676 | data: struct { | ||
| 677 | string: Node, | ||
| 678 | end: usize, | ||
| 679 | }, | ||
| 680 | }; | ||
| 667 | }; | 681 | }; |
| 668 | 682 | ||
| 669 | /// Converts the nodes into a Zig ast. | 683 | /// Converts the nodes into a Zig ast. |
| ... | @@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { | ... | @@ -1015,6 +1029,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1015 | .data = undefined, | 1029 | .data = undefined, |
| 1016 | }); | 1030 | }); |
| 1017 | }, | 1031 | }, |
| 1032 | .string_slice => { | ||
| 1033 | const payload = node.castTag(.string_slice).?.data; | ||
| 1034 | |||
| 1035 | const string = try renderNode(c, payload.string); | ||
| 1036 | const l_bracket = try c.addToken(.l_bracket, "["); | ||
| 1037 | const start = try c.addNode(.{ | ||
| 1038 | .tag = .integer_literal, | ||
| 1039 | .main_token = try c.addToken(.integer_literal, "0"), | ||
| 1040 | .data = undefined, | ||
| 1041 | }); | ||
| 1042 | _ = try c.addToken(.ellipsis2, ".."); | ||
| 1043 | const end = try c.addNode(.{ | ||
| 1044 | .tag = .integer_literal, | ||
| 1045 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.end}), | ||
| 1046 | .data = undefined, | ||
| 1047 | }); | ||
| 1048 | _ = try c.addToken(.r_bracket, "]"); | ||
| 1049 | |||
| 1050 | return c.addNode(.{ | ||
| 1051 | .tag = .slice, | ||
| 1052 | .main_token = l_bracket, | ||
| 1053 | .data = .{ | ||
| 1054 | .lhs = string, | ||
| 1055 | .rhs = try c.addExtra(std.zig.ast.Node.Slice{ | ||
| 1056 | .start = start, | ||
| 1057 | .end = end, | ||
| 1058 | }), | ||
| 1059 | }, | ||
| 1060 | }); | ||
| 1061 | }, | ||
| 1018 | .fail_decl => { | 1062 | .fail_decl => { |
| 1019 | const payload = node.castTag(.fail_decl).?.data; | 1063 | const payload = node.castTag(.fail_decl).?.data; |
| 1020 | // pub const name = @compileError(msg); | 1064 | // pub const name = @compileError(msg); |
| ... | @@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { | ... | @@ -1581,6 +1625,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1581 | const payload = node.castTag(.array_type).?.data; | 1625 | const payload = node.castTag(.array_type).?.data; |
| 1582 | return renderArrayType(c, payload.len, payload.elem_type); | 1626 | return renderArrayType(c, payload.len, payload.elem_type); |
| 1583 | }, | 1627 | }, |
| 1628 | .null_sentinel_array_type => { | ||
| 1629 | const payload = node.castTag(.null_sentinel_array_type).?.data; | ||
| 1630 | return renderNullSentinelArrayType(c, payload.len, payload.elem_type); | ||
| 1631 | }, | ||
| 1584 | .array_filler => { | 1632 | .array_filler => { |
| 1585 | const payload = node.castTag(.array_filler).?.data; | 1633 | const payload = node.castTag(.array_filler).?.data; |
| 1586 | 1634 | ||
| ... | @@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { | ... | @@ -1946,6 +1994,36 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { |
| 1946 | }); | 1994 | }); |
| 1947 | } | 1995 | } |
| 1948 | 1996 | ||
| 1997 | fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { | ||
| 1998 | const l_bracket = try c.addToken(.l_bracket, "["); | ||
| 1999 | const len_expr = try c.addNode(.{ | ||
| 2000 | .tag = .integer_literal, | ||
| 2001 | .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}), | ||
| 2002 | .data = undefined, | ||
| 2003 | }); | ||
| 2004 | _ = try c.addToken(.colon, ":"); | ||
| 2005 | |||
| 2006 | const sentinel_expr = try c.addNode(.{ | ||
| 2007 | .tag = .integer_literal, | ||
| 2008 | .main_token = try c.addToken(.integer_literal, "0"), | ||
| 2009 | .data = undefined, | ||
| 2010 | }); | ||
| 2011 | |||
| 2012 | _ = try c.addToken(.r_bracket, "]"); | ||
| 2013 | const elem_type_expr = try renderNode(c, elem_type); | ||
| 2014 | return c.addNode(.{ | ||
| 2015 | .tag = .array_type_sentinel, | ||
| 2016 | .main_token = l_bracket, | ||
| 2017 | .data = .{ | ||
| 2018 | .lhs = len_expr, | ||
| 2019 | .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel { | ||
| 2020 | .sentinel = sentinel_expr, | ||
| 2021 | .elem_type = elem_type_expr, | ||
| 2022 | }), | ||
| 2023 | }, | ||
| 2024 | }); | ||
| 2025 | } | ||
| 2026 | |||
| 1949 | fn addSemicolonIfNeeded(c: *Context, node: Node) !void { | 2027 | fn addSemicolonIfNeeded(c: *Context, node: Node) !void { |
| 1950 | switch (node.tag()) { | 2028 | switch (node.tag()) { |
| 1951 | .warning => unreachable, | 2029 | .warning => unreachable, |
| ... | @@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { | ... | @@ -2014,6 +2092,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2014 | .integer_literal, | 2092 | .integer_literal, |
| 2015 | .float_literal, | 2093 | .float_literal, |
| 2016 | .string_literal, | 2094 | .string_literal, |
| 2095 | .string_slice, | ||
| 2017 | .char_literal, | 2096 | .char_literal, |
| 2018 | .enum_literal, | 2097 | .enum_literal, |
| 2019 | .identifier, | 2098 | .identifier, |
| ... | @@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { | ... | @@ -2035,6 +2114,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2035 | .func, | 2114 | .func, |
| 2036 | .call, | 2115 | .call, |
| 2037 | .array_type, | 2116 | .array_type, |
| 2117 | .null_sentinel_array_type, | ||
| 2038 | .bool_to_int, | 2118 | .bool_to_int, |
| 2039 | .div_exact, | 2119 | .div_exact, |
| 2040 | .byte_offset_of, | 2120 | .byte_offset_of, |
test/translate_c.zig+49-32| ... | @@ -745,14 +745,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -745,14 +745,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 745 | \\ static const char v2[] = "2.2.2"; | 745 | \\ static const char v2[] = "2.2.2"; |
| 746 | \\} | 746 | \\} |
| 747 | , &[_][]const u8{ | 747 | , &[_][]const u8{ |
| 748 | \\const v2: [6]u8 = [6]u8{ | 748 | \\const v2: [5:0]u8 = "2.2.2".*; |
| 749 | \\ '2', | ||
| 750 | \\ '.', | ||
| 751 | \\ '2', | ||
| 752 | \\ '.', | ||
| 753 | \\ '2', | ||
| 754 | \\ 0, | ||
| 755 | \\}; | ||
| 756 | \\pub export fn foo() void {} | 749 | \\pub export fn foo() void {} |
| 757 | }); | 750 | }); |
| 758 | 751 | ||
| ... | @@ -1600,30 +1593,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1600,30 +1593,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1600 | \\static char arr1[] = "hello"; | 1593 | \\static char arr1[] = "hello"; |
| 1601 | \\char arr2[] = "hello"; | 1594 | \\char arr2[] = "hello"; |
| 1602 | , &[_][]const u8{ | 1595 | , &[_][]const u8{ |
| 1603 | \\pub export var arr0: [6]u8 = [6]u8{ | 1596 | \\pub export var arr0: [5:0]u8 = "hello".*; |
| 1604 | \\ 'h', | 1597 | \\pub var arr1: [5:0]u8 = "hello".*; |
| 1605 | \\ 'e', | 1598 | \\pub export var arr2: [5:0]u8 = "hello".*; |
| 1606 | \\ 'l', | ||
| 1607 | \\ 'l', | ||
| 1608 | \\ 'o', | ||
| 1609 | \\ 0, | ||
| 1610 | \\}; | ||
| 1611 | \\pub var arr1: [6]u8 = [6]u8{ | ||
| 1612 | \\ 'h', | ||
| 1613 | \\ 'e', | ||
| 1614 | \\ 'l', | ||
| 1615 | \\ 'l', | ||
| 1616 | \\ 'o', | ||
| 1617 | \\ 0, | ||
| 1618 | \\}; | ||
| 1619 | \\pub export var arr2: [6]u8 = [6]u8{ | ||
| 1620 | \\ 'h', | ||
| 1621 | \\ 'e', | ||
| 1622 | \\ 'l', | ||
| 1623 | \\ 'l', | ||
| 1624 | \\ 'o', | ||
| 1625 | \\ 0, | ||
| 1626 | \\}; | ||
| 1627 | }); | 1599 | }); |
| 1628 | 1600 | ||
| 1629 | cases.add("array initializer expr", | 1601 | cases.add("array initializer expr", |
| ... | @@ -3425,4 +3397,49 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3425,4 +3397,49 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3425 | , &[_][]const u8{ | 3397 | , &[_][]const u8{ |
| 3426 | \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins"); | 3398 | \\pub const FOO = @compileError("TODO implement function '__builtin_alloca_with_align' in std.c.builtins"); |
| 3427 | }); | 3399 | }); |
| 3400 | |||
| 3401 | cases.add("null sentinel arrays when initialized from string literal. Issue #8256", | ||
| 3402 | \\#include <stdint.h> | ||
| 3403 | \\char zero[0] = "abc"; | ||
| 3404 | \\uint32_t zero_w[0] = U"💯💯💯"; | ||
| 3405 | \\char empty_incomplete[] = ""; | ||
| 3406 | \\uint32_t empty_incomplete_w[] = U""; | ||
| 3407 | \\char empty_constant[100] = ""; | ||
| 3408 | \\uint32_t empty_constant_w[100] = U""; | ||
| 3409 | \\char incomplete[] = "abc"; | ||
| 3410 | \\uint32_t incomplete_w[] = U"💯💯💯"; | ||
| 3411 | \\char truncated[1] = "abc"; | ||
| 3412 | \\uint32_t truncated_w[1] = U"💯💯💯"; | ||
| 3413 | \\char extend[5] = "a"; | ||
| 3414 | \\uint32_t extend_w[5] = U"💯"; | ||
| 3415 | \\char no_null[3] = "abc"; | ||
| 3416 | \\uint32_t no_null_w[3] = U"💯💯💯"; | ||
| 3417 | , &[_][]const u8{ | ||
| 3418 | \\pub export var zero: [0]u8 = [0]u8{}; | ||
| 3419 | \\pub export var zero_w: [0]u32 = [0]u32{}; | ||
| 3420 | \\pub export var empty_incomplete: [1]u8 = [1]u8{0} ** 1; | ||
| 3421 | \\pub export var empty_incomplete_w: [1]u32 = [1]u32{0} ** 1; | ||
| 3422 | \\pub export var empty_constant: [100]u8 = [1]u8{0} ** 100; | ||
| 3423 | \\pub export var empty_constant_w: [100]u32 = [1]u32{0} ** 100; | ||
| 3424 | \\pub export var incomplete: [3:0]u8 = "abc".*; | ||
| 3425 | \\pub export var incomplete_w: [3:0]u32 = [3:0]u32{ | ||
| 3426 | \\ '\u{1f4af}', | ||
| 3427 | \\ '\u{1f4af}', | ||
| 3428 | \\ '\u{1f4af}', | ||
| 3429 | \\}; | ||
| 3430 | \\pub export var truncated: [1]u8 = "abc"[0..1].*; | ||
| 3431 | \\pub export var truncated_w: [1]u32 = [1]u32{ | ||
| 3432 | \\ '\u{1f4af}', | ||
| 3433 | \\}; | ||
| 3434 | \\pub export var extend: [5]u8 = "a"[0..1].* ++ [1]u8{0} ** 4; | ||
| 3435 | \\pub export var extend_w: [5]u32 = [1]u32{ | ||
| 3436 | \\ '\u{1f4af}', | ||
| 3437 | \\} ++ [1]u32{0} ** 4; | ||
| 3438 | \\pub export var no_null: [3]u8 = "abc".*; | ||
| 3439 | \\pub export var no_null_w: [3]u32 = [3]u32{ | ||
| 3440 | \\ '\u{1f4af}', | ||
| 3441 | \\ '\u{1f4af}', | ||
| 3442 | \\ '\u{1f4af}', | ||
| 3443 | \\}; | ||
| 3444 | }); | ||
| 3428 | } | 3445 | } |