authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-01-26 09:08:05-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-26 21:13:06-08:00
log1ed8c54cd349497adb264b0502783a6422e4f2d1
tree813be35e4f0e0745451c97474970322acdc79e40
parent79730e6f5cdf13b2514781881e69c557fd52eaea

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"; } ```

2 files changed, 65 insertions(+), 15 deletions(-)

src/translate_c.zig+41-15
...@@ -780,7 +780,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co...@@ -780,7 +780,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
780 eq_tok = try appendToken(c, .Equal, "=");780 eq_tok = try appendToken(c, .Equal, "=");
781 if (decl_init) |expr| {781 if (decl_init) |expr| {
782 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)782 const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)
783 transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), type_node)783 transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(rp.c, type_node) catch 0)
784 else784 else
785 transExprCoercing(rp, scope, expr, .used, .r_value);785 transExprCoercing(rp, scope, expr, .used, .r_value);
786 init_node = node_or_error catch |err| switch (err) {786 init_node = node_or_error catch |err| switch (err) {
...@@ -1662,7 +1662,7 @@ fn transDeclStmtOne(...@@ -1662,7 +1662,7 @@ fn transDeclStmtOne(
1662 const eq_token = try appendToken(c, .Equal, "=");1662 const eq_token = try appendToken(c, .Equal, "=");
1663 var init_node = if (decl_init) |expr|1663 var init_node = if (decl_init) |expr|
1664 if (expr.getStmtClass() == .StringLiteralClass)1664 if (expr.getStmtClass() == .StringLiteralClass)
1665 try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)1665 try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(rp.c, type_node))
1666 else1666 else
1667 try transExprCoercing(rp, scope, expr, .used, .r_value)1667 try transExprCoercing(rp, scope, expr, .used, .r_value)
1668 else1668 else
...@@ -2059,16 +2059,41 @@ fn transStringLiteral(...@@ -2059,16 +2059,41 @@ fn transStringLiteral(
2059 };2059 };
2060 return maybeSuppressResult(rp, scope, result_used, &node.base);2060 return maybeSuppressResult(rp, scope, result_used, &node.base);
2061 },2061 },
2062 .UTF16, .UTF32, .Wide => return revertAndWarn(2062 .UTF16, .UTF32, .Wide => {
2063 rp,2063 const node = try transWideStringLiteral(rp, scope, stmt);
2064 error.UnsupportedTranslation,2064 return maybeSuppressResult(rp, scope, result_used, node);
2065 @ptrCast(*const clang.Stmt, stmt).getBeginLoc(),2065 },
2066 "TODO: support string literal kind {s}",
2067 .{kind},
2068 ),
2069 }2066 }
2070}2067}
20712068
2069/// Translates a wide string literal as a global "anonymous" array of the relevant-sized
2070/// integer type + null terminator, and returns an identifier node for it
2071fn transWideStringLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.StringLiteral) TransError!*ast.Node {
2072 const str_type = @tagName(stmt.getKind());
2073 const mangle = rp.c.getMangle();
2074 const name = try std.fmt.allocPrint(rp.c.arena, "zig.{s}_string_{d}", .{ str_type, mangle });
2075
2076 const const_tok = try appendToken(rp.c, .Keyword_const, "const");
2077 const name_tok = try appendIdentifier(rp.c, name);
2078 const eq_tok = try appendToken(rp.c, .Equal, "=");
2079 var semi_tok: ast.TokenIndex = undefined;
2080
2081 const lit_array = try transStringLiteralAsArray(rp, scope, stmt, stmt.getLength() + 1);
2082
2083 semi_tok = try appendToken(rp.c, .Semicolon, ";");
2084 const var_decl_node = try ast.Node.VarDecl.create(rp.c.arena, .{
2085 .name_token = name_tok,
2086 .mut_token = const_tok,
2087 .semicolon_token = semi_tok,
2088 }, .{
2089 .visib_token = null,
2090 .eq_token = eq_tok,
2091 .init_node = lit_array,
2092 });
2093 try addTopLevelDecl(rp.c, name, &var_decl_node.base);
2094 return transCreateNodeIdentifier(rp.c, name);
2095}
2096
2072/// Parse the size of an array back out from an ast Node.2097/// Parse the size of an array back out from an ast Node.
2073fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {2098fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {
2074 if (node.castTag(.ArrayType)) |array| {2099 if (node.castTag(.ArrayType)) |array| {
...@@ -2081,17 +2106,18 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {...@@ -2081,17 +2106,18 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize {
2081}2106}
20822107
2083/// Translate a string literal to an array of integers. Used when an2108/// Translate a string literal to an array of integers. Used when an
2084/// array is initialized from a string literal. `target_node` is the2109/// array is initialized from a string literal. `array_size` is the
2085/// array being initialized. If the string literal is larger than the2110/// size of the array being initialized. If the string literal is larger
2086/// array, truncate the string. If the array is larger than the string2111/// than the array, truncate the string. If the array is larger than the
2087/// literal, pad the array with 0's2112/// string literal, pad the array with 0's
2088fn transStringLiteralAsArray(2113fn transStringLiteralAsArray(
2089 rp: RestorePoint,2114 rp: RestorePoint,
2090 scope: *Scope,2115 scope: *Scope,
2091 stmt: *const clang.StringLiteral,2116 stmt: *const clang.StringLiteral,
2092 target_node: *ast.Node,2117 array_size: usize,
2093) TransError!*ast.Node {2118) TransError!*ast.Node {
2094 const array_size = try zigArraySize(rp.c, target_node);2119 if (array_size == 0) return error.UnsupportedType;
2120
2095 const str_length = stmt.getLength();2121 const str_length = stmt.getLength();
20962122
2097 const expr_base = @ptrCast(*const clang.Expr, stmt);2123 const expr_base = @ptrCast(*const clang.Expr, stmt);
test/run_translated_c.zig+24
...@@ -794,4 +794,28 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -794,4 +794,28 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
794 \\ return 0;794 \\ return 0;
795 \\}795 \\}
796 , "");796 , "");
797
798 cases.add("Wide, UTF-16, and UTF-32 string literals",
799 \\#include <stdlib.h>
800 \\#include <stdint.h>
801 \\#include <wchar.h>
802 \\int main(void) {
803 \\ const wchar_t *wide_str = L"wide";
804 \\ const wchar_t wide_hello[] = L"hello";
805 \\ if (wcslen(wide_str) != 4) abort();
806 \\ if (wcslen(L"literal") != 7) abort();
807 \\ if (wcscmp(wide_hello, L"hello") != 0) abort();
808 \\
809 \\ const uint16_t *u16_str = u"wide";
810 \\ const uint16_t u16_hello[] = u"hello";
811 \\ if (u16_str[3] != u'e' || u16_str[4] != 0) abort();
812 \\ if (u16_hello[4] != u'o' || u16_hello[5] != 0) abort();
813 \\
814 \\ const uint32_t *u32_str = U"wide";
815 \\ const uint32_t u32_hello[] = U"hello";
816 \\ if (u32_str[3] != U'e' || u32_str[4] != 0) abort();
817 \\ if (u32_hello[4] != U'o' || u32_hello[5] != 0) abort();
818 \\ return 0;
819 \\}
820 , "");
797}821}