From 081698156141ba16a168d9448b9883c0f5a3edd0 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 23 Feb 2021 23:25:11 -0800 Subject: [PATCH 1/4] translate-c: add typeof support --- src/clang.zig | 10 ++++++++++ src/translate_c.zig | 14 ++++++++++++++ src/zig_clang.cpp | 10 ++++++++++ src/zig_clang.h | 4 ++++ test/run_translated_c.zig | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+) diff --git a/src/clang.zig b/src/clang.zig index 270f477ddb..5ae4f50711 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -583,6 +583,16 @@ pub const MacroQualifiedType = opaque { extern fn ZigClangMacroQualifiedType_getModifiedType(*const MacroQualifiedType) QualType; }; +pub const TypeOfType = opaque { + pub const getUnderlyingType = ZigClangTypeOfType_getUnderlyingType; + extern fn ZigClangTypeOfType_getUnderlyingType(*const TypeOfType) QualType; +}; + +pub const TypeOfExprType = opaque { + pub const getUnderlyingExpr = ZigClangTypeOfExprType_getUnderlyingExpr; + extern fn ZigClangTypeOfExprType_getUnderlyingExpr(*const TypeOfExprType) *const Expr; +}; + pub const MemberExpr = opaque { pub const getBase = ZigClangMemberExpr_getBase; extern fn ZigClangMemberExpr_getBase(*const MemberExpr) *const Expr; diff --git a/src/translate_c.zig b/src/translate_c.zig index 4f1de2a617..42f11f473e 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3827,6 +3827,20 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty); return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc); }, + .TypeOf => { + const typeof_ty = @ptrCast(*const clang.TypeOfType, ty); + return transQualType(c, scope, typeof_ty.getUnderlyingType(), source_loc); + }, + .TypeOfExpr => { + const typeofexpr_ty = @ptrCast(*const clang.TypeOfExprType, ty); + const underlying_expr = transExpr(c, scope, typeofexpr_ty.getUnderlyingExpr(), .used) catch |err| switch (err) { + error.UnsupportedTranslation => { + return fail(c, error.UnsupportedType, source_loc, "unsupported underlying expression for TypeOfExpr", .{}); + }, + else => |e| return e, + }; + return Tag.typeof.create(c.arena, underlying_expr); + }, else => { const type_name = c.str(ty.getTypeClassName()); return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index d9e5e527ac..8387f6fa80 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2599,6 +2599,16 @@ struct ZigClangQualType ZigClangMacroQualifiedType_getModifiedType(const struct return bitcast(casted->getModifiedType()); } +struct ZigClangQualType ZigClangTypeOfType_getUnderlyingType(const struct ZigClangTypeOfType *self) { + auto casted = reinterpret_cast(self); + return bitcast(casted->getUnderlyingType()); +} + +const struct ZigClangExpr *ZigClangTypeOfExprType_getUnderlyingExpr(const struct ZigClangTypeOfExprType *self) { + auto casted = reinterpret_cast(self); + return reinterpret_cast(casted->getUnderlyingExpr()); +} + struct ZigClangQualType ZigClangElaboratedType_getNamedType(const struct ZigClangElaboratedType *self) { auto casted = reinterpret_cast(self); return bitcast(casted->getNamedType()); diff --git a/src/zig_clang.h b/src/zig_clang.h index a697c58b4f..3c3e97f4a3 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1157,6 +1157,10 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangAttributedType_getEquivalentType(co ZIG_EXTERN_C struct ZigClangQualType ZigClangMacroQualifiedType_getModifiedType(const struct ZigClangMacroQualifiedType *); +ZIG_EXTERN_C struct ZigClangQualType ZigClangTypeOfType_getUnderlyingType(const struct ZigClangTypeOfType *); + +ZIG_EXTERN_C const struct ZigClangExpr *ZigClangTypeOfExprType_getUnderlyingExpr(const struct ZigClangTypeOfExprType *); + ZIG_EXTERN_C struct ZigClangQualType ZigClangElaboratedType_getNamedType(const struct ZigClangElaboratedType *); ZIG_EXTERN_C enum ZigClangElaboratedTypeKeyword ZigClangElaboratedType_getKeyword(const struct ZigClangElaboratedType *); diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 61569678c3..70dc1c835d 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1054,4 +1054,23 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("typeof operator", + \\#include + \\static int FOO = 42; + \\typedef typeof(FOO) foo_type; + \\typeof(foo_type) myfunc(typeof(FOO) x) { return (typeof(FOO)) x; } + \\int main(void) { + \\ int x = FOO; + \\ typeof(x) y = x; + \\ foo_type z = y; + \\ if (x != y) abort(); + \\ if (myfunc(z) != x) abort(); + \\ + \\ const char *my_string = "bar"; + \\ typeof (typeof (my_string)[4]) string_arr = {"a","b","c","d"}; + \\ if (string_arr[0][0] != 'a' || string_arr[3][0] != 'd') abort(); + \\ return 0; + \\} + , ""); } From 3f844cba0b601186bb20efa6090e944b9267f538 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Feb 2021 00:26:25 -0700 Subject: [PATCH 2/4] std.zig.fmt escaped string formatting recognizes single quote style This introduces {'} to indicate escape for a single-quoted string, and {} to indicate escape for a double quoted string. Without this, there would be unnecessary \' inside double quoted strings, and unnecessary \" inside single quoted strings. Motivated by the llvm12 branch, in the new tool I am writing for updating target CPU features. --- lib/std/zig/fmt.zig | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/std/zig/fmt.zig b/lib/std/zig/fmt.zig index 2ca5279263..56f480dc3e 100644 --- a/lib/std/zig/fmt.zig +++ b/lib/std/zig/fmt.zig @@ -12,7 +12,7 @@ pub fn formatId( return writer.writeAll(bytes); } try writer.writeAll("@\""); - try formatEscapes(bytes, fmt, options, writer); + try formatEscapes(bytes, "", options, writer); try writer.writeByte('"'); } @@ -32,6 +32,9 @@ pub fn isValidId(bytes: []const u8) bool { return std.zig.Token.getKeyword(bytes) == null; } +/// Print the string as escaped contents of a double quoted or single-quoted string. +/// Format `{}` treats contents as a double-quoted string. +/// Format `{'}` treats contents as a single-quoted string. pub fn formatEscapes( bytes: []const u8, comptime fmt: []const u8, @@ -43,8 +46,24 @@ pub fn formatEscapes( '\r' => try writer.writeAll("\\r"), '\t' => try writer.writeAll("\\t"), '\\' => try writer.writeAll("\\\\"), - '"' => try writer.writeAll("\\\""), - '\'' => try writer.writeAll("\\'"), + '"' => { + if (fmt.len == 1 and fmt[0] == '\'') { + try writer.writeByte('"'); + } else if (fmt.len == 0) { + try writer.writeAll("\\\""); + } else { + @compileError("expected {} or {'}, found {" ++ fmt ++ "}"); + } + }, + '\'' => { + if (fmt.len == 1 and fmt[0] == '\'') { + try writer.writeAll("\\'"); + } else if (fmt.len == 0) { + try writer.writeByte('\''); + } else { + @compileError("expected {} or {'}, found {" ++ fmt ++ "}"); + } + }, ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte), // Use hex escapes for rest any unprintable characters. else => { @@ -54,7 +73,10 @@ pub fn formatEscapes( }; } -/// Return a Formatter for Zig Escapes +/// Return a Formatter for Zig Escapes of a double quoted string. +/// The format specifier must be one of: +/// * `{}` treats contents as a double-quoted string. +/// * `{'}` treats contents as a single-quoted string. pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(formatEscapes) { return .{ .data = bytes }; } @@ -67,6 +89,9 @@ test "escape invalid identifiers" { try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); try expectFmt( - \\" \\ hi \x07 \x11 \" derp \'" + \\" \\ hi \x07 \x11 " derp \'" + , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); + try expectFmt( + \\" \\ hi \x07 \x11 \" derp '" , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); } From 4a548002afcff3850ab117a263f20ef34259c884 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Feb 2021 01:20:19 -0700 Subject: [PATCH 3/4] translate-c: upgrade to new std.zig.fmtEscapes API fixes regressions from previous commit --- src/translate_c.zig | 2 +- src/translate_c/ast.zig | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 42f11f473e..e82583bd07 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -2475,7 +2475,7 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { return Tag.char_literal.create(c.arena, if (narrow) - try std.fmt.allocPrint(c.arena, "'{s}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})}) + try std.fmt.allocPrint(c.arena, "'{'}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})}) else try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val})); } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 47e4f520c5..c02b4048f7 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -995,7 +995,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const compile_error_tok = try c.addToken(.builtin, "@compileError"); _ = try c.addToken(.l_paren, "("); - const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(payload.mangled)}); + const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(payload.mangled)}); const err_msg = try c.addNode(.{ .tag = .string_literal, .main_token = err_msg_tok, @@ -2265,7 +2265,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { _ = try c.addToken(.l_paren, "("); const res = try c.addNode(.{ .tag = .string_literal, - .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}), + .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}), .data = undefined, }); _ = try c.addToken(.r_paren, ")"); @@ -2347,7 +2347,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { _ = try c.addToken(.l_paren, "("); const res = try c.addNode(.{ .tag = .string_literal, - .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}), + .main_token = try c.addTokenFmt(.string_literal, "\"{}\"", .{std.zig.fmtEscapes(some)}), .data = undefined, }); _ = try c.addToken(.r_paren, ")"); From 490654c332f2d8eaf7edffa35ea0523800df998d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 27 Feb 2021 01:21:01 -0700 Subject: [PATCH 4/4] std.ascii: add lessThanIgnoreCase and orderIgnoreCase For sorting ascii strings, case insensitively. --- lib/std/ascii.zig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/std/ascii.zig b/lib/std/ascii.zig index 7d391b7c4b..09ab1094fa 100644 --- a/lib/std/ascii.zig +++ b/lib/std/ascii.zig @@ -379,3 +379,23 @@ test "indexOfIgnoreCase" { std.testing.expect(indexOfIgnoreCase("FOO foo", "fOo").? == 0); } + +/// Compares two slices of numbers lexicographically. O(n). +pub fn orderIgnoreCase(lhs: []const u8, rhs: []const u8) std.math.Order { + const n = std.math.min(lhs.len, rhs.len); + var i: usize = 0; + while (i < n) : (i += 1) { + switch (std.math.order(toLower(lhs[i]), toLower(rhs[i]))) { + .eq => continue, + .lt => return .lt, + .gt => return .gt, + } + } + return std.math.order(lhs.len, rhs.len); +} + +/// Returns true if lhs < rhs, false otherwise +/// TODO rename "IgnoreCase" to "Insensitive" in this entire file. +pub fn lessThanIgnoreCase(lhs: []const u8, rhs: []const u8) bool { + return orderIgnoreCase(lhs, rhs) == .lt; +}