From 7051ef32bf8e1a16cfd73f2bfa09fdbdf39ffc54 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 18 Dec 2020 01:10:31 +0200 Subject: [PATCH 01/36] translate-c: start creating intermediate AST --- src/translate_c/ast.zig | 207 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 src/translate_c/ast.zig diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig new file mode 100644 index 0000000000..4418971269 --- /dev/null +++ b/src/translate_c/ast.zig @@ -0,0 +1,207 @@ +const std = @import("std"); +const Type = @import("../type.zig").Type; + +pub const Node = struct { + tag: Tag, + // type: Type = Type.initTag(.noreturn), + + pub const Tag = enum { + null_literal, + undefined_literal, + opaque_literal, + bool_literal, + int, + float, + string, + char, + identifier, + @"if", + @"while", + @"switch", + @"continue", + @"break", + @"return", + field_access, + field_access_arrow, + array_access, + call, + std_mem_zeroes, + var_decl, + func, + warning, + failed_decl, + @"enum", + @"struct", + @"union", + array_init, + container_init, + std_meta_cast, + discard, + block, + + pub fn Type(tag: Tag) ?type { + return switch (tag) { + .null_literal => null, + .undefined_literal => null, + .opaque_literal => null, + .bool_literal, + .int, + .float, + .string, + .char, + .identifier, + .field_access, + .field_access_arrow, + .warning, + .failed_decl, + => Value, + .@"if" => If, + .@"while" => While, + .@"switch" => Switch, + .@"break" => Break, + .call => Call, + .array_access, + .std_mem_zeroes, + .@"return", + .discard, + => SingleArg, + .var_decl => VarDecl, + .func => Func, + .@"enum" => Enum, + .@"struct", .@"union" => Record, + .array_init => ArrayInit, + .container_init => ContainerInit, + .std_meta_cast => Infix, + .block => Block, + }; + } + }; + + pub const Infix = struct { + base: Node, + lhs: *Node, + rhs: *Node, + }; + + pub const Value = struct { + base: Node, + val: []const u8, + }; + + pub const SingleArg = struct { + base: Node, + index: *Node, + }; + + pub const If = struct { + base: Node = .{ .tag = .@"if" }, + cond: *Node, + then: *Node, + @"else": ?*Node, + }; + + pub const While = struct { + base: Node = .{ .tag = .@"while" }, + cond: *Node, + body: *Node, + }; + + pub const Switch = struct { + base: Node = .{ .tag = .@"switch" }, + cond: *Node, + cases: []Prong, + default: ?[]const u8, + + pub const Prong = struct { + lhs: *Node, + rhs: ?*Node, + label: []const u8, + }; + }; + + pub const Break = struct { + base: Node = .{ .tag = .@"break" }, + label: ?[]const u8, + rhs: ?*Node, + }; + + pub const Call = struct { + base: Node = .{.call}, + lhs: *Node, + args: []*Node, + }; + + pub const VarDecl = struct { + base: Node = .{ .tag = .var_decl }, + @"pub": bool, + @"const": bool, + @"extern": bool, + @"export": bool, + name: []const u8, + type: Type, + init: *Node, + }; + + pub const Func = struct { + base: Node = .{.func}, + @"pub": bool, + @"extern": bool, + @"export": bool, + name: []const u8, + cc: std.builtin.CallingConvention, + params: []Param, + return_type: Type, + body: ?*Node, + + pub const Param = struct { + @"noalias": bool, + name: ?[]const u8, + type: Type, + }; + }; + + pub const Enum = struct { + base: Node = .{ .tag = .@"enum" }, + name: ?[]const u8, + fields: []Field, + + pub const Field = struct { + name: []const u8, + value: ?[]const u8, + }; + }; + + pub const Record = struct { + base: Node, + name: ?[]const u8, + @"packed": bool, + fields: []Field, + + pub const Field = struct { + name: []const u8, + type: Type, + alignment: c_uint, + }; + }; + + pub const ArrayInit = struct { + base: Node = .{ .tag = .array_init }, + values: []*Node, + }; + + pub const ContainerInit = struct { + base: Node = .{ .tag = .container_init }, + values: []Initializer, + + pub const Initializer = struct { + name: []const u8, + value: *Node, + }; + }; + + pub const Block = struct { + base: Node = .{ .tag = .block }, + label: ?[]const u8, + stmts: []*Node, + }; +}; From 6ecec4c8b761c9f8f272602ccb2abdfd9656c71c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 31 Jan 2021 12:19:09 +0200 Subject: [PATCH 02/36] translate-c: translate C types to stage2 types --- src/translate_c.zig | 182 ++++++++++++++++++++++++++++++++++++++++++++ src/type.zig | 56 ++++++++++++++ 2 files changed, 238 insertions(+) diff --git a/src/translate_c.zig b/src/translate_c.zig index a7d79e655a..e6629592ed 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -10,6 +10,7 @@ const ctok = std.c.tokenizer; const CToken = std.c.Token; const mem = std.mem; const math = std.math; +const Type = @import("type.zig").Type; const CallingConvention = std.builtin.CallingConvention; @@ -5178,6 +5179,176 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo } } +fn transType1(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Type { + switch (ty.getTypeClass()) { + .Builtin => { + const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); + return Type.initTag(switch (builtin_ty.getKind()) { + .Void => .c_void, + .Bool => .bool, + .Char_U, .UChar, .Char_S, .Char8 => .u8, + .SChar => .i8, + .UShort => .c_ushort, + .UInt => .c_uint, + .ULong => .c_ulong, + .ULongLong => .c_ulonglong, + .Short => .c_short, + .Int => .c_int, + .Long => .c_long, + .LongLong => .c_longlong, + .UInt128 => .u128, + .Int128 => .i128, + .Float => .f32, + .Double => .f64, + .Float128 => .f128, + .Float16 => .f16, + .LongDouble => .c_longdouble, + else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), + }); + }, + .FunctionProto => { + const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty); + return transFnProto(c, null, fn_proto_ty, source_loc, null, false); + }, + .FunctionNoProto => { + const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty); + return transFnNoProto(c, fn_no_proto_ty, source_loc, null, false); + }, + .Paren => { + const paren_ty = @ptrCast(*const clang.ParenType, ty); + return transQualType(c, paren_ty.getInnerType(), source_loc); + }, + .Pointer => { + const child_qt = ty.getPointeeType(); + if (qualTypeChildIsFnProto(child_qt)) { + return Type.optional_single_mut_pointer.create(c.arena, try transQualType(c, child_qt, source_loc)); + } + const is_const = child_qt.isConstQualified(); + const is_volatile = child_qt.isVolatileQualified(); + const elem_type = try transQualType(c, child_qt, source_loc); + if (elem_type.zigTypeTag() == .Opaque) { + if (!is_volatile) { + if (is_const) { + return Type.optional_single_const_pointer.create(c.arena, elem_type); + } else { + return Type.optional_single_mut_pointer.create(c.arena, elem_type); + } + } + + return Type.pointer.create(c.arena, .{ + .pointee_type = elem_type, + .sentinel = null, + .@"align" = 0, + .bit_offset = 0, + .host_size = 0, + .@"allowzero" = false, + .mutable = !is_const, + .@"volatile" = true, + .size = .Single, + }); + } + + if (!is_volatile) { + if (is_const) { + return Type.c_const_pointer.create(c.arena, elem_type); + } else { + return Type.c_mut_pointer.create(c.arena, elem_type); + } + } + + return Type.pointer.create(c.arena, .{ + .pointee_type = elem_type, + .sentinel = null, + .@"align" = 0, + .bit_offset = 0, + .host_size = 0, + .@"allowzero" = false, + .mutable = !is_const, + .@"volatile" = true, + .size = .C, + }); + }, + .ConstantArray => { + const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); + + const size_ap_int = const_arr_ty.getSize(); + const size = size_ap_int.getLimitedValue(math.maxInt(usize)); + const elem_type = try transType1(c, const_arr_ty.getElementType().getTypePtr(), source_loc); + + return Type.array.create(c.arena, .{ .len = size, .elem_type = elem_type }); + }, + .IncompleteArray => { + const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); + + const child_qt = incomplete_array_ty.getElementType(); + const is_const = child_qt.isConstQualified(); + const is_volatile = child_qt.isVolatileQualified(); + const elem_type = try transQualType(c, child_qt, source_loc); + + if (!is_volatile) { + if (is_const) { + return Type.c_const_pointer.create(c.arena, elem_type); + } else { + return Type.c_mut_pointer.create(c.arena, elem_type); + } + } + + return Type.pointer.create(c.arena, .{ + .pointee_type = elem_type, + .sentinel = null, + .@"align" = 0, + .bit_offset = 0, + .host_size = 0, + .@"allowzero" = false, + .mutable = !is_const, + .@"volatile" = true, + .size = .C, + }); + }, + .Typedef => { + const typedef_ty = @ptrCast(*const clang.TypedefType, ty); + + const typedef_decl = typedef_ty.getDecl(); + return (try transTypeDef(c, typedef_decl, false)) orelse + fail(c, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{}); + }, + .Record => { + const record_ty = @ptrCast(*const clang.RecordType, ty); + + const record_decl = record_ty.getDecl(); + return (try transRecordDecl(c, record_decl)) orelse + fail(c, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{}); + }, + .Enum => { + const enum_ty = @ptrCast(*const clang.EnumType, ty); + + const enum_decl = enum_ty.getDecl(); + return (try transEnumDecl(c, enum_decl)) orelse + fail(c, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{}); + }, + .Elaborated => { + const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); + return transQualType(c, elaborated_ty.getNamedType(), source_loc); + }, + .Decayed => { + const decayed_ty = @ptrCast(*const clang.DecayedType, ty); + return transQualType(c, decayed_ty.getDecayedType(), source_loc); + }, + .Attributed => { + const attributed_ty = @ptrCast(*const clang.AttributedType, ty); + return transQualType(c, attributed_ty.getEquivalentType(), source_loc); + }, + .MacroQualified => { + const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty); + return transQualType(c, macroqualified_ty.getModifiedType(), source_loc); + }, + else => { + const type_name = c.str(ty.getTypeClassName()); + return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name}); + }, + } +} + fn qualTypeWasDemotedToOpaque(c: *Context, qt: clang.QualType) bool { const ty = qt.getTypePtr(); switch (qt.getTypeClass()) { @@ -5474,6 +5645,17 @@ fn emitWarning(c: *Context, loc: clang.SourceLocation, comptime format: []const _ = try appendTokenFmt(c, .LineComment, "// {s}: warning: " ++ format, args_prefix ++ args); } +fn fail( + rp: RestorePoint, + err: anytype, + source_loc: clang.SourceLocation, + comptime format: []const u8, + args: anytype, +) (@TypeOf(err) || error{OutOfMemory}) { + try emitWarning(c, source_loc, format, args); + return err; +} + pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void { // pub const name = @compileError(msg); const pub_tok = try appendToken(c, .Keyword_pub, "pub"); diff --git a/src/type.zig b/src/type.zig index e1006e554c..7d3308e403 100644 --- a/src/type.zig +++ b/src/type.zig @@ -28,6 +28,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -357,6 +359,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -506,6 +510,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -772,6 +778,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -868,6 +876,7 @@ pub const Type = extern union { .i16, .u16 => return 2, .i32, .u32 => return 4, .i64, .u64 => return 8, + .u128, .i128 => return 16, .isize, .usize, @@ -1010,6 +1019,7 @@ pub const Type = extern union { .i16, .u16 => return 2, .i32, .u32 => return 4, .i64, .u64 => return 8, + .u128, .i128 => return 16, .@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8), @@ -1109,6 +1119,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1191,6 +1203,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1278,6 +1292,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1359,6 +1375,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1440,6 +1458,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1522,6 +1542,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1776,6 +1798,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -1856,6 +1880,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2009,6 +2035,8 @@ pub const Type = extern union { .i16, .i32, .i64, + .u128, + .i128, => true, }; } @@ -2061,6 +2089,8 @@ pub const Type = extern union { .i16, .i32, .i64, + .u128, + .i128, .optional, .optional_single_mut_pointer, .optional_single_const_pointer, @@ -2227,6 +2257,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .optional, .optional_single_mut_pointer, .optional_single_const_pointer, @@ -2333,6 +2365,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2417,6 +2451,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2500,6 +2536,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2583,6 +2621,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2663,6 +2703,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2743,6 +2785,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2793,6 +2837,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2874,6 +2920,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -2971,6 +3019,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -3060,6 +3110,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, @@ -3193,6 +3245,8 @@ pub const Type = extern union { i32, u64, i64, + u128, + i128, usize, isize, c_short, @@ -3277,6 +3331,8 @@ pub const Type = extern union { .i32, .u64, .i64, + .u128, + .i128, .usize, .isize, .c_short, From d835f5cce5fc3b296f55e208905d9ff4d368e497 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 31 Jan 2021 12:55:33 +0200 Subject: [PATCH 03/36] translate-c: make Node more like Type --- src/translate_c/ast.zig | 222 +++++++++++++++++++++++++--------------- src/type.zig | 5 + 2 files changed, 146 insertions(+), 81 deletions(-) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 4418971269..0585c26d18 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -2,14 +2,19 @@ const std = @import("std"); const Type = @import("../type.zig").Type; pub const Node = struct { - tag: Tag, - // type: Type = Type.initTag(.noreturn), + /// If the tag value is less than Tag.no_payload_count, then no pointer + /// dereference is needed. + tag_if_small_enough: usize, + ptr_otherwise: *Payload, pub const Tag = enum { null_literal, undefined_literal, opaque_literal, - bool_literal, + true_literal, + false_literal, + // After this, the tag requires a payload. + int, float, string, @@ -39,12 +44,18 @@ pub const Node = struct { discard, block, + pub const last_no_payload_tag = Tag.false_literal; + pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; + pub fn Type(tag: Tag) ?type { return switch (tag) { - .null_literal => null, - .undefined_literal => null, - .opaque_literal => null, - .bool_literal, + .null_literal, + .undefined_literal, + .opaque_literal, + .true_literal, + .false_litral, + => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), + .int, .float, .string, @@ -54,144 +65,186 @@ pub const Node = struct { .field_access_arrow, .warning, .failed_decl, - => Value, - .@"if" => If, - .@"while" => While, - .@"switch" => Switch, - .@"break" => Break, - .call => Call, + => Payload.Value, + .@"if" => Payload.If, + .@"while" => Payload.While, + .@"switch" => Payload.Switch, + .@"break" => Payload.Break, + .call => Payload.Call, .array_access, .std_mem_zeroes, .@"return", .discard, - => SingleArg, - .var_decl => VarDecl, - .func => Func, - .@"enum" => Enum, - .@"struct", .@"union" => Record, - .array_init => ArrayInit, - .container_init => ContainerInit, - .std_meta_cast => Infix, - .block => Block, + => Payload.SingleArg, + .var_decl => Payload.VarDecl, + .func => Payload.Func, + .@"enum" => Payload.Enum, + .@"struct", .@"union" => Payload.Record, + .array_init => Payload.ArrayInit, + .container_init => Payload.ContainerInit, + .std_meta_cast => Payload.Infix, + .block => Payload.Block, }; } + + pub fn init(comptime t: Tag) Node { + comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count); + return .{ .tag_if_small_enough = @enumToInt(t) }; + } + + pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Node { + const ptr = try ally.create(t.Type()); + ptr.* = .{ + .base = .{ .tag = t }, + .data = data, + }; + return Node{ .ptr_otherwise = &ptr.base }; + } + + pub fn Data(comptime t: Tag) type { + return std.meta.fieldInfo(t.Type(), .data).field_type; + } }; +}; + +pub const Payload = struct { + tag: Tag, pub const Infix = struct { base: Node, - lhs: *Node, - rhs: *Node, + data: struct { + lhs: *Node, + rhs: *Node, + }, }; pub const Value = struct { base: Node, - val: []const u8, + data: []const u8, }; pub const SingleArg = struct { base: Node, - index: *Node, + data: *Node, }; pub const If = struct { base: Node = .{ .tag = .@"if" }, - cond: *Node, - then: *Node, - @"else": ?*Node, + data: struct { + cond: *Node, + then: *Node, + @"else": ?*Node, + }, }; pub const While = struct { base: Node = .{ .tag = .@"while" }, - cond: *Node, - body: *Node, + data: struct { + cond: *Node, + body: *Node, + }, }; pub const Switch = struct { base: Node = .{ .tag = .@"switch" }, - cond: *Node, - cases: []Prong, - default: ?[]const u8, + data: struct { + cond: *Node, + cases: []Prong, + default: ?[]const u8, - pub const Prong = struct { - lhs: *Node, - rhs: ?*Node, - label: []const u8, - }; + pub const Prong = struct { + lhs: *Node, + rhs: ?*Node, + label: []const u8, + }; + }, }; pub const Break = struct { base: Node = .{ .tag = .@"break" }, - label: ?[]const u8, - rhs: ?*Node, + data: struct { + label: ?[]const u8, + rhs: ?*Node, + }, }; pub const Call = struct { base: Node = .{.call}, - lhs: *Node, - args: []*Node, + data: struct { + lhs: *Node, + args: []*Node, + }, }; pub const VarDecl = struct { base: Node = .{ .tag = .var_decl }, - @"pub": bool, - @"const": bool, - @"extern": bool, - @"export": bool, - name: []const u8, - type: Type, - init: *Node, + data: struct { + @"pub": bool, + @"const": bool, + @"extern": bool, + @"export": bool, + name: []const u8, + type: Type, + init: *Node, + }, }; pub const Func = struct { base: Node = .{.func}, - @"pub": bool, - @"extern": bool, - @"export": bool, - name: []const u8, - cc: std.builtin.CallingConvention, - params: []Param, - return_type: Type, - body: ?*Node, + data: struct { + @"pub": bool, + @"extern": bool, + @"export": bool, + name: []const u8, + cc: std.builtin.CallingConvention, + params: []Param, + return_type: Type, + body: ?*Node, - pub const Param = struct { - @"noalias": bool, - name: ?[]const u8, - type: Type, - }; + pub const Param = struct { + @"noalias": bool, + name: ?[]const u8, + type: Type, + }; + }, }; pub const Enum = struct { base: Node = .{ .tag = .@"enum" }, - name: ?[]const u8, - fields: []Field, + data: struct { + name: ?[]const u8, + fields: []Field, - pub const Field = struct { - name: []const u8, - value: ?[]const u8, - }; + pub const Field = struct { + name: []const u8, + value: ?[]const u8, + }; + }, }; pub const Record = struct { base: Node, - name: ?[]const u8, - @"packed": bool, - fields: []Field, + data: struct { + name: ?[]const u8, + @"packed": bool, + fields: []Field, - pub const Field = struct { - name: []const u8, - type: Type, - alignment: c_uint, - }; + pub const Field = struct { + name: []const u8, + type: Type, + alignment: c_uint, + }; + }, }; pub const ArrayInit = struct { base: Node = .{ .tag = .array_init }, - values: []*Node, + data: []*Node, }; pub const ContainerInit = struct { base: Node = .{ .tag = .container_init }, - values: []Initializer, + data: []Initializer, pub const Initializer = struct { name: []const u8, @@ -201,7 +254,14 @@ pub const Node = struct { pub const Block = struct { base: Node = .{ .tag = .block }, - label: ?[]const u8, - stmts: []*Node, + data: struct { + label: ?[]const u8, + stmts: []*Node, + }, }; }; + +/// Converts the nodes into a Zig ast and then renders it. +pub fn render(allocator: *Allocator, nodes: []const Node) !void { + @panic("TODO"); +} diff --git a/src/type.zig b/src/type.zig index 7d3308e403..8fcaba6fad 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3408,6 +3408,11 @@ pub const Type = extern union { }; } + pub fn init(comptime t: Tag) Type { + comptime std.debug.assert(@enumToInt(t) < Tag.no_payload_count); + return .{ .tag_if_small_enough = @enumToInt(t) }; + } + pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type { const ptr = try ally.create(t.Type()); ptr.* = .{ From 4c0c9b07555bb69d05142dfe038a7cad79068ba9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 6 Feb 2021 18:45:45 +0200 Subject: [PATCH 04/36] translate-c: convert transBinaryOperator --- src/translate_c.zig | 653 ++++++++-------------------------------- src/translate_c/ast.zig | 151 +++++++++- 2 files changed, 269 insertions(+), 535 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index e6629592ed..f885ea98b9 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3,14 +3,14 @@ const std = @import("std"); const assert = std.debug.assert; -const ast = std.zig.ast; -const Token = std.zig.Token; const clang = @import("clang.zig"); const ctok = std.c.tokenizer; const CToken = std.c.Token; const mem = std.mem; const math = std.math; const Type = @import("type.zig").Type; +const ast = @import("translate_c/ast.zig"); +const Node = ast.Node; const CallingConvention = std.builtin.CallingConvention; @@ -348,47 +348,6 @@ pub const Context = struct { } }; -fn addCBuiltinsNamespace(c: *Context) Error!void { - // pub usingnamespace @import("std").c.builtins; - const pub_tok = try appendToken(c, .Keyword_pub, "pub"); - const use_tok = try appendToken(c, .Keyword_usingnamespace, "usingnamespace"); - const import_tok = try appendToken(c, .Builtin, "@import"); - const lparen_tok = try appendToken(c, .LParen, "("); - const std_tok = try appendToken(c, .StringLiteral, "\"std\""); - const rparen_tok = try appendToken(c, .RParen, ")"); - - const std_node = try c.arena.create(ast.Node.OneToken); - std_node.* = .{ - .base = .{ .tag = .StringLiteral }, - .token = std_tok, - }; - - const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1); - call_node.* = .{ - .builtin_token = import_tok, - .params_len = 1, - .rparen_token = rparen_tok, - }; - call_node.params()[0] = &std_node.base; - - var access_chain = &call_node.base; - access_chain = try transCreateNodeFieldAccess(c, access_chain, "c"); - access_chain = try transCreateNodeFieldAccess(c, access_chain, "builtins"); - - const semi_tok = try appendToken(c, .Semicolon, ";"); - - const bytes = try c.gpa.alignedAlloc(u8, @alignOf(ast.Node.Use), @sizeOf(ast.Node.Use)); - const using_node = @ptrCast(*ast.Node.Use, bytes.ptr); - using_node.* = .{ - .doc_comments = null, - .visib_token = pub_tok, - .use_token = use_tok, - .expr = access_chain, - .semicolon_token = semi_tok, - }; - try c.root_decls.append(c.gpa, &using_node.base); -} - pub fn translate( gpa: *mem.Allocator, args_begin: [*]?[*]const u8, @@ -446,7 +405,7 @@ pub fn translate( context.opaque_demotes.deinit(gpa); } - try addCBuiltinsNamespace(&context); + _ = try Node.usingnamespace_builtins.init(); try prepopulateGlobalNameTable(ast_unit, &context); @@ -1318,26 +1277,6 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node return transCreateNodeIdentifier(c, name); } -fn createAlias(c: *Context, alias: anytype) !void { - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, alias.alias); - const eq_token = try appendToken(c, .Equal, "="); - const init_node = try transCreateNodeIdentifier(c, alias.name); - const semicolon_token = try appendToken(c, .Semicolon, ";"); - - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); - return addTopLevelDecl(c, alias.alias, &node.base); -} - const ResultUsed = enum { used, unused, @@ -1349,78 +1288,63 @@ const LRValue = enum { }; fn transStmt( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.Stmt, result_used: ResultUsed, lrvalue: LRValue, -) TransError!*ast.Node { +) TransError!Node { const sc = stmt.getStmtClass(); switch (sc) { - .BinaryOperatorClass => return transBinaryOperator(rp, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used), - .CompoundStmtClass => return transCompoundStmt(rp, scope, @ptrCast(*const clang.CompoundStmt, stmt)), - .CStyleCastExprClass => return transCStyleCastExprClass(rp, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue), - .DeclStmtClass => return transDeclStmt(rp, scope, @ptrCast(*const clang.DeclStmt, stmt)), - .DeclRefExprClass => return transDeclRefExpr(rp, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue), - .ImplicitCastExprClass => return transImplicitCastExpr(rp, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used), - .IntegerLiteralClass => return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as), - .ReturnStmtClass => return transReturnStmt(rp, scope, @ptrCast(*const clang.ReturnStmt, stmt)), - .StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used), + .BinaryOperatorClass => return transBinaryOperator(c, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used), + .CompoundStmtClass => return transCompoundStmt(c, scope, @ptrCast(*const clang.CompoundStmt, stmt)), + .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue), + .DeclStmtClass => return transDeclStmt(c, scope, @ptrCast(*const clang.DeclStmt, stmt)), + .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue), + .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used), + .IntegerLiteralClass => return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as), + .ReturnStmtClass => return transReturnStmt(c, scope, @ptrCast(*const clang.ReturnStmt, stmt)), + .StringLiteralClass => return transStringLiteral(c, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used), .ParenExprClass => { - const expr = try transExpr(rp, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue); - if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr); - const node = try rp.c.arena.create(ast.Node.GroupedExpression); - node.* = .{ - .lparen = try appendToken(rp.c, .LParen, "("), - .expr = expr, - .rparen = try appendToken(rp.c, .RParen, ")"), - }; - return maybeSuppressResult(rp, scope, result_used, &node.base); + const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue); + return maybeSuppressResult(c, scope, result_used, expr); }, - .InitListExprClass => return transInitListExpr(rp, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used), - .ImplicitValueInitExprClass => return transImplicitValueInitExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used), - .IfStmtClass => return transIfStmt(rp, scope, @ptrCast(*const clang.IfStmt, stmt)), - .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const clang.WhileStmt, stmt)), - .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const clang.DoStmt, stmt)), + .InitListExprClass => return transInitListExpr(c, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used), + .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), + .IfStmtClass => return transIfStmt(c, scope, @ptrCast(*const clang.IfStmt, stmt)), + .WhileStmtClass => return transWhileLoop(c, scope, @ptrCast(*const clang.WhileStmt, stmt)), + .DoStmtClass => return transDoWhileLoop(c, scope, @ptrCast(*const clang.DoStmt, stmt)), .NullStmtClass => { - const block = try rp.c.createBlock(0); - block.rbrace = try appendToken(rp.c, .RBrace, "}"); - return &block.base; + return Node.empty_block.init(); }, - .ContinueStmtClass => return try transCreateNodeContinue(rp.c), - .BreakStmtClass => return transBreak(rp, scope), - .ForStmtClass => return transForLoop(rp, scope, @ptrCast(*const clang.ForStmt, stmt)), - .FloatingLiteralClass => return transFloatingLiteral(rp, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), + .ContinueStmtClass => return try transCreateNodeContinue(c), + .BreakStmtClass => return transBreak(c, scope), + .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), + .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), .ConditionalOperatorClass => { - return transConditionalOperator(rp, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used); + return transConditionalOperator(c, scope, @ptrCast(*const clang.ConditionalOperator, stmt), result_used); }, .BinaryConditionalOperatorClass => { - return transBinaryConditionalOperator(rp, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); + return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); }, - .SwitchStmtClass => return transSwitch(rp, scope, @ptrCast(*const clang.SwitchStmt, stmt)), - .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const clang.CaseStmt, stmt)), - .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const clang.DefaultStmt, stmt)), - .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const clang.Expr, stmt), result_used), - .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), - .CharacterLiteralClass => return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), - .StmtExprClass => return transStmtExpr(rp, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used), - .MemberExprClass => return transMemberExpr(rp, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used), - .ArraySubscriptExprClass => return transArrayAccess(rp, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used), - .CallExprClass => return transCallExpr(rp, scope, @ptrCast(*const clang.CallExpr, stmt), result_used), - .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(rp, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used), - .UnaryOperatorClass => return transUnaryOperator(rp, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used), - .CompoundAssignOperatorClass => return transCompoundAssignOperator(rp, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used), + .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)), + .CaseStmtClass => return transCase(c, scope, @ptrCast(*const clang.CaseStmt, stmt)), + .DefaultStmtClass => return transDefault(c, scope, @ptrCast(*const clang.DefaultStmt, stmt)), + .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), + .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), + .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), + .StmtExprClass => return transStmtExpr(c, scope, @ptrCast(*const clang.StmtExpr, stmt), result_used), + .MemberExprClass => return transMemberExpr(c, scope, @ptrCast(*const clang.MemberExpr, stmt), result_used), + .ArraySubscriptExprClass => return transArrayAccess(c, scope, @ptrCast(*const clang.ArraySubscriptExpr, stmt), result_used), + .CallExprClass => return transCallExpr(c, scope, @ptrCast(*const clang.CallExpr, stmt), result_used), + .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @ptrCast(*const clang.UnaryExprOrTypeTraitExpr, stmt), result_used), + .UnaryOperatorClass => return transUnaryOperator(c, scope, @ptrCast(*const clang.UnaryOperator, stmt), result_used), + .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used), .OpaqueValueExprClass => { const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?; - const expr = try transExpr(rp, scope, source_expr, .used, lrvalue); - if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr); - const node = try rp.c.arena.create(ast.Node.GroupedExpression); - node.* = .{ - .lparen = try appendToken(rp.c, .LParen, "("), - .expr = expr, - .rparen = try appendToken(rp.c, .RParen, ")"), - }; - return maybeSuppressResult(rp, scope, result_used, &node.base); + const expr = try transExpr(c, scope, source_expr, .used, lrvalue); + return maybeSuppressResult(c, scope, result_used, expr); + const node = try c.arena.create(Node.GroupedExpression); }, else => { return revertAndWarn( @@ -1435,175 +1359,139 @@ fn transStmt( } fn transBinaryOperator( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.BinaryOperator, result_used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const op = stmt.getOpcode(); const qt = stmt.getType(); - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (op) { - .Assign => return try transCreateNodeAssign(rp, scope, result_used, stmt.getLHS(), stmt.getRHS()), + .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), .Comma => { var block_scope = try Scope.Block.init(rp.c, scope, true); - const lparen = try appendToken(rp.c, .LParen, "("); + defer block_scope.deinit(); - const lhs = try transExpr(rp, &block_scope.base, stmt.getLHS(), .unused, .r_value); + + const lhs = try transExpr(c, &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); - _ = 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 break_node = try Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = rhs, + }); + try block_scope.statements.append(break_node); 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); + return maybeSuppressResult(rp, scope, result_used, block_node); }, .Div => { if (cIsSignedInteger(qt)) { // signed integer division uses @divTrunc - const div_trunc_node = try rp.c.createBuiltinCall("@divTrunc", 2); - div_trunc_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value); - _ = try appendToken(rp.c, .Comma, ","); - const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value); - div_trunc_node.params()[1] = rhs; - div_trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, &div_trunc_node.base); + const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); + const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); + const div_trunc = try Node.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs}); + return maybeSuppressResult(c, scope, result_used, div_trunc); } }, .Rem => { if (cIsSignedInteger(qt)) { // signed integer division uses @rem - const rem_node = try rp.c.createBuiltinCall("@rem", 2); - rem_node.params()[0] = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value); - _ = try appendToken(rp.c, .Comma, ","); - const rhs = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value); - rem_node.params()[1] = rhs; - rem_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, &rem_node.base); + const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); + const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); + const rem = try Node.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs}); + return maybeSuppressResult(c, scope, result_used, rem); } }, .Shl => { - const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<"); - return maybeSuppressResult(rp, scope, result_used, node); + const node = try transCreateNodeShiftOp(c, scope, stmt, .shl); + return maybeSuppressResult(c, scope, result_used, node); }, .Shr => { - const node = try transCreateNodeShiftOp(rp, scope, stmt, .BitShiftRight, .AngleBracketAngleBracketRight, ">>"); - return maybeSuppressResult(rp, scope, result_used, node); - }, - .LAnd => { - const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, result_used, true); - return maybeSuppressResult(rp, scope, result_used, node); - }, - .LOr => { - const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, result_used, true); - return maybeSuppressResult(rp, scope, result_used, node); + const node = try transCreateNodeShiftOp(c, scope, stmt, .shr); + return maybeSuppressResult(c, scope, result_used, node); }, else => {}, } - const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .l_value); + var op_id: Node.Tag = undefined; switch (op) { .Add => { if (cIsUnsignedInteger(qt)) { - op_token = try appendToken(rp.c, .PlusPercent, "+%"); - op_id = .AddWrap; + op_id = .add_wrap; } else { - op_token = try appendToken(rp.c, .Plus, "+"); - op_id = .Add; + op_id = .add; } }, .Sub => { if (cIsUnsignedInteger(qt)) { - op_token = try appendToken(rp.c, .MinusPercent, "-%"); - op_id = .SubWrap; + op_id = .sub_wrap; } else { - op_token = try appendToken(rp.c, .Minus, "-"); - op_id = .Sub; + op_id = .sub; } }, .Mul => { if (cIsUnsignedInteger(qt)) { - op_token = try appendToken(rp.c, .AsteriskPercent, "*%"); - op_id = .MulWrap; + op_id = .mul_wrap; } else { - op_token = try appendToken(rp.c, .Asterisk, "*"); - op_id = .Mul; + op_id = .mul; } }, .Div => { // unsigned/float division uses the operator - op_id = .Div; - op_token = try appendToken(rp.c, .Slash, "/"); + op_id = .div; }, .Rem => { // unsigned/float division uses the operator - op_id = .Mod; - op_token = try appendToken(rp.c, .Percent, "%"); + op_id = .mod; }, .LT => { - op_id = .LessThan; - op_token = try appendToken(rp.c, .AngleBracketLeft, "<"); + op_id = .less_than; }, .GT => { - op_id = .GreaterThan; - op_token = try appendToken(rp.c, .AngleBracketRight, ">"); + op_id = .greater_than; }, .LE => { - op_id = .LessOrEqual; - op_token = try appendToken(rp.c, .AngleBracketLeftEqual, "<="); + op_id = .less_than_equal; }, .GE => { - op_id = .GreaterOrEqual; - op_token = try appendToken(rp.c, .AngleBracketRightEqual, ">="); + op_id = .greater_than_equal; }, .EQ => { - op_id = .EqualEqual; - op_token = try appendToken(rp.c, .EqualEqual, "=="); + op_id = .equal; }, .NE => { - op_id = .BangEqual; - op_token = try appendToken(rp.c, .BangEqual, "!="); + op_id = .not_equal; }, .And => { - op_id = .BitAnd; - op_token = try appendToken(rp.c, .Ampersand, "&"); + op_id = .bit_and; }, .Xor => { - op_id = .BitXor; - op_token = try appendToken(rp.c, .Caret, "^"); + op_id = .bit_xor; }, .Or => { - op_id = .BitOr; - op_token = try appendToken(rp.c, .Pipe, "|"); + op_id = .bit_or; + }, + .LAnd => { + op_id = .@"and"; + }, + .LOr => { + op_id = .@"or"; }, else => unreachable, } - const rhs_node = try transExpr(rp, scope, stmt.getRHS(), .used, .r_value); + const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); + const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); - const lhs = if (isBoolRes(lhs_node)) init: { - const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1); - cast_node.params()[0] = lhs_node; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - break :init &cast_node.base; - } else lhs_node; - - const rhs = if (isBoolRes(rhs_node)) init: { - const cast_node = try rp.c.createBuiltinCall("@boolToInt", 1); - cast_node.params()[0] = rhs_node; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - break :init &cast_node.base; - } else rhs_node; - - return transCreateNodeInfixOp(rp, scope, lhs, op_id, op_token, rhs, result_used, true); + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ + .base = .{ .tag = op_id }, + .data = .{ + .lhs = lhs, + .rhs = rhs, + }, + }; + return maybeSuppressResult(c, scope, used, &payload.base); } fn transCompoundStmtInline( @@ -2365,40 +2253,13 @@ fn transEnumToInt(c: *Context, enum_expr: *ast.Node) TypeError!*ast.Node { } fn transExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed, lrvalue: LRValue, -) TransError!*ast.Node { - return transStmt(rp, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue); -} - -/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore -/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. -fn transExprCoercing( - rp: RestorePoint, - scope: *Scope, - expr: *const clang.Expr, - used: ResultUsed, - lrvalue: LRValue, -) TransError!*ast.Node { - switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { - .IntegerLiteralClass => { - return transIntegerLiteral(rp, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as); - }, - .CharacterLiteralClass => { - return transCharLiteral(rp, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as); - }, - .UnaryOperatorClass => { - const un_expr = @ptrCast(*const clang.UnaryOperator, expr); - if (un_expr.getOpcode() == .Extension) { - return transExprCoercing(rp, scope, un_expr.getSubExpr(), used, lrvalue); - } - }, - else => {}, - } - return transExpr(rp, scope, expr, .used, .r_value); +) TransError!Node { + return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue); } fn transInitListExprRecord( @@ -4150,7 +4011,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool { return qualTypeCanon(qt).isBooleanType(); } -fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 { +fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 { const ty = qt.getTypePtr(); switch (ty.getTypeClass()) { @@ -4174,7 +4035,7 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); const typedef_decl = typedef_ty.getDecl(); - const type_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); + const type_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) { return 8; @@ -4194,51 +4055,17 @@ fn qualTypeIntBitWidth(rp: RestorePoint, qt: clang.QualType, source_loc: clang.S unreachable; } -fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) !*ast.Node { - const int_bit_width = try qualTypeIntBitWidth(rp, qt, source_loc); +fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !Node { + const int_bit_width = try qualTypeIntBitWidth(c, qt, source_loc); if (int_bit_width != 0) { // we can perform the log2 now. const cast_bit_width = math.log2_int(u64, int_bit_width); - const node = try rp.c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .IntegerLiteral }, - .token = try appendTokenFmt(rp.c, .Identifier, "u{d}", .{cast_bit_width}), - }; - return &node.base; + return Node.uint_type.create(c.arena, cast_bit_width); } - const zig_type_node = try transQualType(rp, qt, source_loc); - - // @import("std").math.Log2Int(c_long); - // - // FnCall - // FieldAccess - // FieldAccess - // FnCall (.builtin = true) - // Symbol "import" - // StringLiteral "std" - // Symbol "math" - // Symbol "Log2Int" - // Symbol (var from above) - - const import_fn_call = try rp.c.createBuiltinCall("@import", 1); - const std_token = try appendToken(rp.c, .StringLiteral, "\"std\""); - const std_node = try rp.c.arena.create(ast.Node.OneToken); - std_node.* = .{ - .base = .{ .tag = .StringLiteral }, - .token = std_token, - }; - import_fn_call.params()[0] = &std_node.base; - import_fn_call.rparen_token = try appendToken(rp.c, .RParen, ")"); - - const inner_field_access = try transCreateNodeFieldAccess(rp.c, &import_fn_call.base, "math"); - const outer_field_access = try transCreateNodeFieldAccess(rp.c, inner_field_access, "Log2Int"); - const log2int_fn_call = try rp.c.createCall(outer_field_access, 1); - log2int_fn_call.params()[0] = zig_type_node; - log2int_fn_call.rtoken = try appendToken(rp.c, .RParen, ")"); - - return &log2int_fn_call.base; + const zig_type = try transQualType(c, qt, source_loc); + return Node.std_math_Log2Int.create(c.arena, zig_type); } fn qualTypeChildIsFnProto(qt: clang.QualType) bool { @@ -4506,67 +4333,6 @@ fn transCreateNodeSimplePrefixOp( return node; } -fn transCreateNodeInfixOp( - rp: RestorePoint, - scope: *Scope, - lhs_node: *ast.Node, - op: ast.Node.Tag, - op_token: ast.TokenIndex, - rhs_node: *ast.Node, - used: ResultUsed, - grouped: bool, -) !*ast.Node { - var lparen = if (grouped) - try appendToken(rp.c, .LParen, "(") - else - null; - const node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - node.* = .{ - .base = .{ .tag = op }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = rhs_node, - }; - if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base); - const rparen = try appendToken(rp.c, .RParen, ")"); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = lparen.?, - .expr = &node.base, - .rparen = rparen, - }; - return maybeSuppressResult(rp, scope, used, &grouped_expr.base); -} - -fn transCreateNodeBoolInfixOp( - rp: RestorePoint, - scope: *Scope, - stmt: *const clang.BinaryOperator, - op: ast.Node.Tag, - used: ResultUsed, - grouped: bool, -) !*ast.Node { - std.debug.assert(op == .BoolAnd or op == .BoolOr); - - const lhs_hode = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value, true); - const op_token = if (op == .BoolAnd) - try appendToken(rp.c, .Keyword_and, "and") - else - try appendToken(rp.c, .Keyword_or, "or"); - const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value, true); - - return transCreateNodeInfixOp( - rp, - scope, - lhs_hode, - op, - op_token, - rhs, - used, - grouped, - ); -} - fn transCreateNodePtrType( c: *Context, is_const: bool, @@ -4968,40 +4734,33 @@ fn transCreateNodeSwitchElse(c: *Context) !*ast.Node { } fn transCreateNodeShiftOp( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.BinaryOperator, - op: ast.Node.Tag, - op_tok_id: std.zig.Token.Id, - bytes: []const u8, -) !*ast.Node { - std.debug.assert(op == .BitShiftLeft or op == .BitShiftRight); + op: Node.Tag, +) !Node { + std.debug.assert(op == .shl or op == .shr); const lhs_expr = stmt.getLHS(); const rhs_expr = stmt.getRHS(); const rhs_location = rhs_expr.getBeginLoc(); // lhs >> @as(u5, rh) - const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value); - const op_token = try appendToken(rp.c, op_tok_id, bytes); + const lhs = try transExpr(c, scope, lhs_expr, .used, .l_value); - const cast_node = try rp.c.createBuiltinCall("@intCast", 2); - const rhs_type = try qualTypeToLog2IntRef(rp, stmt.getType(), rhs_location); - cast_node.params()[0] = rhs_type; - _ = try appendToken(rp.c, .Comma, ","); - const rhs = try transExprCoercing(rp, scope, rhs_expr, .used, .r_value); - cast_node.params()[1] = rhs; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location); + const rhs = try transExpr(c, scope, rhs_expr, .used, .r_value); + const rhs_casted = try Node.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type }); - const node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - node.* = .{ + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ .base = .{ .tag = op }, - .op_token = op_token, - .lhs = lhs, - .rhs = &cast_node.base, + .data = .{ + .lhs = lhs, + .rhs = rhs_casted, + } }; - - return &node.base; + return &payload.base; } fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { @@ -5025,161 +4784,7 @@ fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAcces return node; } -const RestorePoint = struct { - c: *Context, - token_index: ast.TokenIndex, - src_buf_index: usize, - - fn activate(self: RestorePoint) void { - self.c.token_ids.shrinkAndFree(self.c.gpa, self.token_index); - self.c.token_locs.shrinkAndFree(self.c.gpa, self.token_index); - self.c.source_buffer.shrinkAndFree(self.src_buf_index); - } -}; - -fn makeRestorePoint(c: *Context) RestorePoint { - return RestorePoint{ - .c = c, - .token_index = c.token_ids.items.len, - .src_buf_index = c.source_buffer.items.len, - }; -} - -fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!*ast.Node { - switch (ty.getTypeClass()) { - .Builtin => { - const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); - return transCreateNodeIdentifier(rp.c, switch (builtin_ty.getKind()) { - .Void => "c_void", - .Bool => "bool", - .Char_U, .UChar, .Char_S, .Char8 => "u8", - .SChar => "i8", - .UShort => "c_ushort", - .UInt => "c_uint", - .ULong => "c_ulong", - .ULongLong => "c_ulonglong", - .Short => "c_short", - .Int => "c_int", - .Long => "c_long", - .LongLong => "c_longlong", - .UInt128 => "u128", - .Int128 => "i128", - .Float => "f32", - .Double => "f64", - .Float128 => "f128", - .Float16 => "f16", - .LongDouble => "c_longdouble", - else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), - }); - }, - .FunctionProto => { - const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty); - const fn_proto = try transFnProto(rp, null, fn_proto_ty, source_loc, null, false); - return &fn_proto.base; - }, - .FunctionNoProto => { - const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty); - const fn_proto = try transFnNoProto(rp, fn_no_proto_ty, source_loc, null, false); - return &fn_proto.base; - }, - .Paren => { - const paren_ty = @ptrCast(*const clang.ParenType, ty); - return transQualType(rp, paren_ty.getInnerType(), source_loc); - }, - .Pointer => { - const child_qt = ty.getPointeeType(); - if (qualTypeChildIsFnProto(child_qt)) { - const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); - optional_node.rhs = try transQualType(rp, child_qt, source_loc); - return &optional_node.base; - } - if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) { - const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); - const pointer_node = try transCreateNodePtrType( - rp.c, - child_qt.isConstQualified(), - child_qt.isVolatileQualified(), - .Asterisk, - ); - optional_node.rhs = &pointer_node.base; - pointer_node.rhs = try transQualType(rp, child_qt, source_loc); - return &optional_node.base; - } - const pointer_node = try transCreateNodePtrType( - rp.c, - child_qt.isConstQualified(), - child_qt.isVolatileQualified(), - .Identifier, - ); - pointer_node.rhs = try transQualType(rp, child_qt, source_loc); - return &pointer_node.base; - }, - .ConstantArray => { - const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); - - const size_ap_int = const_arr_ty.getSize(); - const size = size_ap_int.getLimitedValue(math.maxInt(usize)); - const elem_ty = const_arr_ty.getElementType().getTypePtr(); - return try transCreateNodeArrayType(rp, source_loc, elem_ty, size); - }, - .IncompleteArray => { - const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); - - const child_qt = incomplete_array_ty.getElementType(); - var node = try transCreateNodePtrType( - rp.c, - child_qt.isConstQualified(), - child_qt.isVolatileQualified(), - .Identifier, - ); - node.rhs = try transQualType(rp, child_qt, source_loc); - return &node.base; - }, - .Typedef => { - const typedef_ty = @ptrCast(*const clang.TypedefType, ty); - - const typedef_decl = typedef_ty.getDecl(); - return (try transTypeDef(rp.c, typedef_decl, false)) orelse - revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{}); - }, - .Record => { - const record_ty = @ptrCast(*const clang.RecordType, ty); - - const record_decl = record_ty.getDecl(); - return (try transRecordDecl(rp.c, record_decl)) orelse - revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{}); - }, - .Enum => { - const enum_ty = @ptrCast(*const clang.EnumType, ty); - - const enum_decl = enum_ty.getDecl(); - return (try transEnumDecl(rp.c, enum_decl)) orelse - revertAndWarn(rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{}); - }, - .Elaborated => { - const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); - return transQualType(rp, elaborated_ty.getNamedType(), source_loc); - }, - .Decayed => { - const decayed_ty = @ptrCast(*const clang.DecayedType, ty); - return transQualType(rp, decayed_ty.getDecayedType(), source_loc); - }, - .Attributed => { - const attributed_ty = @ptrCast(*const clang.AttributedType, ty); - return transQualType(rp, attributed_ty.getEquivalentType(), source_loc); - }, - .MacroQualified => { - const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty); - return transQualType(rp, macroqualified_ty.getModifiedType(), source_loc); - }, - else => { - const type_name = rp.c.str(ty.getTypeClassName()); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); - }, - } -} - -fn transType1(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Type { +fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Type { switch (ty.getTypeClass()) { .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 0585c26d18..3ceccf16b8 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1,7 +1,7 @@ const std = @import("std"); const Type = @import("../type.zig").Type; -pub const Node = struct { +pub const Node = extern union { /// If the tag value is less than Tag.no_payload_count, then no pointer /// dereference is needed. tag_if_small_enough: usize, @@ -13,12 +13,15 @@ pub const Node = struct { opaque_literal, true_literal, false_literal, + empty_block, + /// pub usingnamespace @import("std").c.builtins; + usingnamespace_builtins, // After this, the tag requires a payload. - int, - float, - string, - char, + int_literal, + float_literal, + string_literal, + char_literal, identifier, @"if", @"while", @@ -44,6 +47,67 @@ pub const Node = struct { discard, block, + // a + b + add, + // a = b + add_assign, + // c = (a = b) + add_assign_value, + add_wrap, + add_wrap_assign, + add_wrap_assign_value, + sub, + sub_assign, + sub_assign_value, + sub_wrap, + sub_wrap_assign, + sub_wrap_assign_value, + mul, + mul_assign, + mul_assign_value, + mul_wrap, + mul_wrap_assign, + mul_wrap_assign_value, + div, + div_assign, + div_assign_value, + shl, + shl_assign, + shl_assign_value, + shr, + shr_assign, + shr_assign_value, + mod, + mod_assign, + mod_assign_value, + @"and", + and_assign, + and_assign_value, + @"or", + or_assign, + or_assign_value, + xor, + xor_assign, + xor_assign_value, + less_than, + less_than_equal, + greater_than, + greater_than_equal, + equal, + not_equal, + bit_and, + bit_or, + bit_xor, + + /// @import("std").math.Log2Int(operand) + std_math_Log2Int, + /// @intCast(lhs, rhs) + int_cast, + /// @rem(lhs, rhs) + rem, + /// @divTrunc(lhs, rhs) + div_trunc, + pub const last_no_payload_tag = Tag.false_literal; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -54,8 +118,70 @@ pub const Node = struct { .opaque_literal, .true_literal, .false_litral, + .empty_block, + .usingnamespace_builtins, => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), + .array_access, + .std_mem_zeroes, + .@"return", + .discard, + .std_math_Log2Int, + => Payload.UnOp, + + .add, + .add_assign, + .add_assign_value, + .add_wrap, + .add_wrap_assign, + .add_wrap_assign_value, + .sub, + .sub_assign, + .sub_assign_value, + .sub_wrap, + .sub_wrap_assign, + .sub_wrap_assign_value, + .mul, + .mul_assign, + .mul_assign_value, + .mul_wrap, + .mul_wrap_assign, + .mul_wrap_assign_value, + .div, + .div_assign, + .div_assign_value, + .shl, + .shl_assign, + .shl_assign_value, + .shr, + .shr_assign, + .shr_assign_value, + .mod, + .mod_assign, + .mod_assign_value, + .@"and", + .and_assign, + .and_assign_value, + .@"or", + .or_assign, + .or_assign_value, + .xor, + .xor_assign, + .xor_assign_value, + .less_than, + .less_than_equal, + .greater_than, + .greater_than_equal, + .equal, + .not_equal, + .bit_and, + .bit_or, + .bit_xor, + .div_trunc, + .rem, + .int_cast, + => Payload.BinOp, + .int, .float, .string, @@ -71,11 +197,6 @@ pub const Node = struct { .@"switch" => Payload.Switch, .@"break" => Payload.Break, .call => Payload.Call, - .array_access, - .std_mem_zeroes, - .@"return", - .discard, - => Payload.SingleArg, .var_decl => Payload.VarDecl, .func => Payload.Func, .@"enum" => Payload.Enum, @@ -123,11 +244,19 @@ pub const Payload = struct { data: []const u8, }; - pub const SingleArg = struct { + pub const UnOp = struct { base: Node, data: *Node, }; + pub const BinOp = struct { + base: Node, + data: struct { + lhs: *Node, + rhs: *Node, + }, + }; + pub const If = struct { base: Node = .{ .tag = .@"if" }, data: struct { From f5041caa2e9d1e891bd93aa2721a4a283123f0d9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 7 Feb 2021 22:02:42 +0200 Subject: [PATCH 05/36] translate-c: more binaryoperator chagnes, blocks and unary type expressions --- src/translate_c.zig | 273 +++++++++++++++------------------------- src/translate_c/ast.zig | 80 ++++++++---- 2 files changed, 156 insertions(+), 197 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index f885ea98b9..6570766988 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -68,16 +68,15 @@ const Scope = struct { } }; - /// Represents an in-progress ast.Node.Block. This struct is stack-allocated. - /// When it is deinitialized, it produces an ast.Node.Block which is allocated + /// Represents an in-progress Node.Block. This struct is stack-allocated. + /// When it is deinitialized, it produces an Node.Block which is allocated /// into the main arena. const Block = struct { base: Scope, - statements: std.ArrayList(*ast.Node), + statements: std.ArrayList(Node), variables: AliasList, - label: ?ast.TokenIndex, mangle_count: u32 = 0, - lbrace: ast.TokenIndex, + label: ?[]const u8 = null, /// When the block corresponds to a function, keep track of the return type /// so that the return expression can be cast, if necessary @@ -89,14 +88,11 @@ const Scope = struct { .id = .Block, .parent = parent, }, - .statements = std.ArrayList(*ast.Node).init(c.gpa), + .statements = std.ArrayList(Node).init(c.gpa), .variables = AliasList.init(c.gpa), - .label = null, - .lbrace = try appendToken(c, .LBrace, "{"), }; if (labeled) { - blk.label = try appendIdentifier(c, try blk.makeMangledName(c, "blk")); - _ = try appendToken(c, .Colon, ":"); + blk.label = try blk.makeMangledName(c, "blk"); } return blk; } @@ -107,31 +103,16 @@ const Scope = struct { self.* = undefined; } - fn complete(self: *Block, c: *Context) !*ast.Node { + fn complete(self: *Block, c: *Context) !Node { // We reserve 1 extra statement if the parent is a Loop. This is in case of // do while, we want to put `if (cond) break;` at the end. const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop); - const rbrace = try appendToken(c, .RBrace, "}"); - if (self.label) |label| { - const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len); - node.* = .{ - .statements_len = self.statements.items.len, - .lbrace = self.lbrace, - .rbrace = rbrace, - .label = label, - }; - mem.copy(*ast.Node, node.statements(), self.statements.items); - return &node.base; - } else { - const node = try ast.Node.Block.alloc(c.arena, alloc_len); - node.* = .{ - .statements_len = self.statements.items.len, - .lbrace = self.lbrace, - .rbrace = rbrace, - }; - mem.copy(*ast.Node, node.statements(), self.statements.items); - return &node.base; - } + const stmts = try c.arena.alloc(Node, alloc_len); + mem.copy(Node, stmts, self.statements.items); + return Node.block.create(c.arena, .{ + .lable = self.label, + .stmts = stmts, + }); } /// Given the desired name, return a name that does not shadow anything from outer scopes. @@ -1390,7 +1371,7 @@ fn transBinaryOperator( // signed integer division uses @divTrunc const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); - const div_trunc = try Node.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs}); + const div_trunc = try Node.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); return maybeSuppressResult(c, scope, result_used, div_trunc); } }, @@ -1399,7 +1380,7 @@ fn transBinaryOperator( // signed integer division uses @rem const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); - const rem = try Node.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs}); + const rem = try Node.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); return maybeSuppressResult(c, scope, result_used, rem); } }, @@ -1411,6 +1392,12 @@ fn transBinaryOperator( const node = try transCreateNodeShiftOp(c, scope, stmt, .shr); return maybeSuppressResult(c, scope, result_used, node); }, + .LAnd => { + return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_and, result_used, true); + }, + .LOr => { + return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_or, result_used, true); + }, else => {}, } var op_id: Node.Tag = undefined; @@ -1471,17 +1458,19 @@ fn transBinaryOperator( .Or => { op_id = .bit_or; }, - .LAnd => { - op_id = .@"and"; - }, - .LOr => { - op_id = .@"or"; - }, else => unreachable, } - const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); - const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); + const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); + const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); + + const lhs = if (isBoolRes(lhs_uncasted)) + try Node.bool_to_int.create(c.arena, lhs_uncasted) + else lhs_uncasted; + + const rhs = if (isBoolRes(rhs_uncasted)) + try Node.bool_to_int.create(c.arena, rhs_uncasted) + else rhs_uncasted; const payload = try c.arena.create(ast.Payload.BinOp); payload.* = .{ @@ -1495,7 +1484,7 @@ fn transBinaryOperator( } fn transCompoundStmtInline( - rp: RestorePoint, + c: *Context, parent_scope: *Scope, stmt: *const clang.CompoundStmt, block: *Scope.Block, @@ -1503,16 +1492,16 @@ fn transCompoundStmtInline( var it = stmt.body_begin(); const end_it = stmt.body_end(); while (it != end_it) : (it += 1) { - const result = try transStmt(rp, parent_scope, it[0], .unused, .r_value); + const result = try transStmt(c, parent_scope, it[0], .unused, .r_value); try block.statements.append(result); } } -fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!*ast.Node { - var block_scope = try Scope.Block.init(rp.c, scope, false); +fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { + var block_scope = try Scope.Block.init(c, scope, false); defer block_scope.deinit(); - try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope); - return try block_scope.complete(rp.c); + try transCompoundStmtInline(c, &block_scope.base, stmt, &block_scope); + return try block_scope.complete(c); } fn transCStyleCastExprClass( @@ -3233,22 +3222,18 @@ fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType { } fn transUnaryExprOrTypeTraitExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.UnaryExprOrTypeTraitExpr, result_used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const loc = stmt.getBeginLoc(); - const type_node = try transQualType( - rp, - stmt.getTypeOfArgument(), - loc, - ); + const type_node = try transQualType(rp, stmt.getTypeOfArgument(), loc); const kind = stmt.getKind(); - const kind_str = switch (kind) { - .SizeOf => "@sizeOf", - .AlignOf => "@alignOf", + switch (kind) { + .SizeOf => return Node.sizeof.create(c.arena, type_node), + .AlignOf => return Node.alignof.create(c.arena, type_node), .PreferredAlignOf, .VecStep, .OpenMPRequiredSimdAlign, @@ -3259,12 +3244,7 @@ fn transUnaryExprOrTypeTraitExpr( "Unsupported type trait kind {}", .{kind}, ), - }; - - const builtin_node = try rp.c.createBuiltinCall(kind_str, 1); - builtin_node.params()[0] = type_node; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, &builtin_node.base); + } } fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { @@ -3967,8 +3947,8 @@ fn transQualTypeInitialized( return transQualType(rp, qt, source_loc); } -fn transQualType(rp: RestorePoint, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!*ast.Node { - return transType(rp, qt.getTypePtr(), source_loc); +fn transQualType(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node { + return transType(c, qt.getTypePtr(), source_loc); } /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. @@ -4318,19 +4298,27 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c return &field_access_node.base; } -fn transCreateNodeSimplePrefixOp( +fn transCreateNodeBoolInfixOp( c: *Context, - comptime tag: ast.Node.Tag, - op_tok_id: std.zig.Token.Id, - bytes: []const u8, -) !*ast.Node.SimplePrefixOp { - const node = try c.arena.create(ast.Node.SimplePrefixOp); - node.* = .{ - .base = .{ .tag = tag }, - .op_token = try appendToken(c, op_tok_id, bytes), - .rhs = undefined, // translate and set afterward + scope: *Scope, + stmt: *const clang.BinaryOperator, + op: ast.Node.Tag, + used: ResultUsed, +) !Node { + std.debug.assert(op == .bool_and or op == .bool_or); + + const lhs = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value, true); + const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value, true); + + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ + .base = .{ .tag = op }, + .data = .{ + .lhs = lhs, + .rhs = rhs, + }, }; - return node; + return maybeSuppressResult(c, scope, used, &payload.base); } fn transCreateNodePtrType( @@ -4784,30 +4772,30 @@ fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAcces return node; } -fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Type { +fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { switch (ty.getTypeClass()) { .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); - return Type.initTag(switch (builtin_ty.getKind()) { - .Void => .c_void, - .Bool => .bool, - .Char_U, .UChar, .Char_S, .Char8 => .u8, - .SChar => .i8, - .UShort => .c_ushort, - .UInt => .c_uint, - .ULong => .c_ulong, - .ULongLong => .c_ulonglong, - .Short => .c_short, - .Int => .c_int, - .Long => .c_long, - .LongLong => .c_longlong, - .UInt128 => .u128, - .Int128 => .i128, - .Float => .f32, - .Double => .f64, - .Float128 => .f128, - .Float16 => .f16, - .LongDouble => .c_longdouble, + return Node.type.create(c.arena, switch (builtin_ty.getKind()) { + .Void => "c_void", + .Bool => "bool", + .Char_U, .UChar, .Char_S, .Char8 => "u8", + .SChar => "i8", + .UShort => "c_ushort", + .UInt => "c_uint", + .ULong => "c_ulong", + .ULongLong => "c_ulonglong", + .Short => "c_short", + .Int => "c_int", + .Long => "c_long", + .LongLong => "c_longlong", + .UInt128 => "u128", + .Int128 => "i128", + .Float => "f32", + .Double => "f64", + .Float128 => "f128", + .Float16 => "f16", + .LongDouble => "c_longdouble", else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), }); }, @@ -4826,61 +4814,25 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio .Pointer => { const child_qt = ty.getPointeeType(); if (qualTypeChildIsFnProto(child_qt)) { - return Type.optional_single_mut_pointer.create(c.arena, try transQualType(c, child_qt, source_loc)); + return Node.optional_type.create(c.arena, try transQualType(c, child_qt, source_loc)); } const is_const = child_qt.isConstQualified(); const is_volatile = child_qt.isVolatileQualified(); const elem_type = try transQualType(c, child_qt, source_loc); - if (elem_type.zigTypeTag() == .Opaque) { - if (!is_volatile) { - if (is_const) { - return Type.optional_single_const_pointer.create(c.arena, elem_type); - } else { - return Type.optional_single_mut_pointer.create(c.arena, elem_type); - } - } - - return Type.pointer.create(c.arena, .{ - .pointee_type = elem_type, - .sentinel = null, - .@"align" = 0, - .bit_offset = 0, - .host_size = 0, - .@"allowzero" = false, - .mutable = !is_const, - .@"volatile" = true, - .size = .Single, - }); + if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) { + return Node.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); } - if (!is_volatile) { - if (is_const) { - return Type.c_const_pointer.create(c.arena, elem_type); - } else { - return Type.c_mut_pointer.create(c.arena, elem_type); - } - } - - return Type.pointer.create(c.arena, .{ - .pointee_type = elem_type, - .sentinel = null, - .@"align" = 0, - .bit_offset = 0, - .host_size = 0, - .@"allowzero" = false, - .mutable = !is_const, - .@"volatile" = true, - .size = .C, - }); + return Node.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); }, .ConstantArray => { const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); const size_ap_int = const_arr_ty.getSize(); const size = size_ap_int.getLimitedValue(math.maxInt(usize)); - const elem_type = try transType1(c, const_arr_ty.getElementType().getTypePtr(), source_loc); - - return Type.array.create(c.arena, .{ .len = size, .elem_type = elem_type }); + const elem_type = try transType(c, const_arr_ty.getElementType().getTypePtr(), source_loc); + + return Node.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); }, .IncompleteArray => { const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); @@ -4890,25 +4842,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const is_volatile = child_qt.isVolatileQualified(); const elem_type = try transQualType(c, child_qt, source_loc); - if (!is_volatile) { - if (is_const) { - return Type.c_const_pointer.create(c.arena, elem_type); - } else { - return Type.c_mut_pointer.create(c.arena, elem_type); - } - } - - return Type.pointer.create(c.arena, .{ - .pointee_type = elem_type, - .sentinel = null, - .@"align" = 0, - .bit_offset = 0, - .host_size = 0, - .@"allowzero" = false, - .mutable = !is_const, - .@"volatile" = true, - .size = .C, - }); + return Node.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); }, .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); @@ -5233,25 +5167,14 @@ fn finishTransFnProto( return fn_proto; } -fn revertAndWarn( - rp: RestorePoint, - err: anytype, - source_loc: clang.SourceLocation, - comptime format: []const u8, - args: anytype, -) (@TypeOf(err) || error{OutOfMemory}) { - rp.activate(); - try emitWarning(rp.c, source_loc, format, args); - return err; -} - -fn emitWarning(c: *Context, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { +fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { const args_prefix = .{c.locStr(loc)}; - _ = try appendTokenFmt(c, .LineComment, "// {s}: warning: " ++ format, args_prefix ++ args); + const value = std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, args_prefix ++ args); + try scope.appendNode(c.gpa, try Node.warning.create(c.arena, value)); } fn fail( - rp: RestorePoint, + c: *Context, err: anytype, source_loc: clang.SourceLocation, comptime format: []const u8, diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 3ceccf16b8..638d4cefa0 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -107,6 +107,25 @@ pub const Node = extern union { rem, /// @divTrunc(lhs, rhs) div_trunc, + /// @boolToInt(lhs, rhs) + bool_to_int, + + negate, + negate_wrap, + bit_not, + not, + + block, + @"break", + + sizeof, + alignof, + type, + + optional_type, + c_pointer, + single_pointer, + array_type, pub const last_no_payload_tag = Tag.false_literal; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -127,6 +146,14 @@ pub const Node = extern union { .@"return", .discard, .std_math_Log2Int, + .negate, + .negate_wrap, + .bit_not, + .not, + .optional_type, + .c_pointer, + .single_pointer, + .array_type, => Payload.UnOp, .add, @@ -180,6 +207,7 @@ pub const Node = extern union { .div_trunc, .rem, .int_cast, + .bool_to_int, => Payload.BinOp, .int, @@ -191,6 +219,9 @@ pub const Node = extern union { .field_access_arrow, .warning, .failed_decl, + .sizeof, + .alignof, + .type, => Payload.Value, .@"if" => Payload.If, .@"while" => Payload.While, @@ -234,8 +265,8 @@ pub const Payload = struct { pub const Infix = struct { base: Node, data: struct { - lhs: *Node, - rhs: *Node, + lhs: Node, + rhs: Node, }, }; @@ -246,44 +277,44 @@ pub const Payload = struct { pub const UnOp = struct { base: Node, - data: *Node, + data: Node, }; pub const BinOp = struct { base: Node, data: struct { - lhs: *Node, - rhs: *Node, + lhs: Node, + rhs: Node, }, }; pub const If = struct { base: Node = .{ .tag = .@"if" }, data: struct { - cond: *Node, - then: *Node, - @"else": ?*Node, + cond: Node, + then: Node, + @"else": ?Node, }, }; pub const While = struct { base: Node = .{ .tag = .@"while" }, data: struct { - cond: *Node, - body: *Node, + cond: Node, + body: Node, }, }; pub const Switch = struct { base: Node = .{ .tag = .@"switch" }, data: struct { - cond: *Node, + cond: Node, cases: []Prong, default: ?[]const u8, pub const Prong = struct { - lhs: *Node, - rhs: ?*Node, + lhs: Node, + rhs: ?Node, label: []const u8, }; }, @@ -293,15 +324,15 @@ pub const Payload = struct { base: Node = .{ .tag = .@"break" }, data: struct { label: ?[]const u8, - rhs: ?*Node, + rhs: ?Node, }, }; pub const Call = struct { base: Node = .{.call}, data: struct { - lhs: *Node, - args: []*Node, + lhs: Node, + args: []Node, }, }; @@ -314,7 +345,7 @@ pub const Payload = struct { @"export": bool, name: []const u8, type: Type, - init: *Node, + init: Node, }, }; @@ -328,7 +359,7 @@ pub const Payload = struct { cc: std.builtin.CallingConvention, params: []Param, return_type: Type, - body: ?*Node, + body: ?Node, pub const Param = struct { @"noalias": bool, @@ -368,7 +399,7 @@ pub const Payload = struct { pub const ArrayInit = struct { base: Node = .{ .tag = .array_init }, - data: []*Node, + data: []Node, }; pub const ContainerInit = struct { @@ -377,17 +408,22 @@ pub const Payload = struct { pub const Initializer = struct { name: []const u8, - value: *Node, + value: Node, }; }; pub const Block = struct { - base: Node = .{ .tag = .block }, + base: Node, data: struct { label: ?[]const u8, - stmts: []*Node, + stmts: []Node }, }; + + pub const Break = struct { + base: Node = .{ .tag = .@"break" }, + data: *Block + }; }; /// Converts the nodes into a Zig ast and then renders it. From 7514c0ad0d327d2427009c26e5930540297e396e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 7 Feb 2021 22:28:41 +0200 Subject: [PATCH 06/36] translate-c: unary operator, integers and misc --- src/translate_c.zig | 279 ++++++++++++++++------------------------ src/translate_c/ast.zig | 35 ++++- 2 files changed, 138 insertions(+), 176 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 6570766988..a8285e3036 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -8,7 +8,6 @@ const ctok = std.c.tokenizer; const CToken = std.c.Token; const mem = std.mem; const math = std.math; -const Type = @import("type.zig").Type; const ast = @import("translate_c/ast.zig"); const Node = ast.Node; @@ -20,7 +19,7 @@ pub const Error = error{OutOfMemory}; const TypeError = Error || error{UnsupportedType}; const TransError = TypeError || error{UnsupportedTranslation}; -const SymbolTable = std.StringArrayHashMap(*ast.Node); +const SymbolTable = std.StringArrayHashMap(Node); const AliasList = std.ArrayList(struct { alias: []const u8, name: []const u8, @@ -38,13 +37,13 @@ const Scope = struct { Loop, }; - /// Represents an in-progress ast.Node.Switch. This struct is stack-allocated. - /// When it is deinitialized, it produces an ast.Node.Switch which is allocated + /// Represents an in-progress Node.Switch. This struct is stack-allocated. + /// When it is deinitialized, it produces an Node.Switch which is allocated /// into the main arena. const Switch = struct { base: Scope, pending_block: Block, - cases: []*ast.Node, + cases: []Node, case_index: usize, switch_label: ?[]const u8, default_label: ?[]const u8, @@ -156,6 +155,7 @@ const Scope = struct { sym_table: SymbolTable, macro_table: SymbolTable, context: *Context, + nodes: std.ArrayList(Node), fn init(c: *Context) Root { return .{ @@ -163,12 +163,19 @@ const Scope = struct { .id = .Root, .parent = null, }, - .sym_table = SymbolTable.init(c.arena), - .macro_table = SymbolTable.init(c.arena), + .sym_table = SymbolTable.init(c.gpa), + .macro_table = SymbolTable.init(c.gpa), .context = c, + .nodes = std.ArrayList(Node).init(c.gpa), }; } + fn deinit(scope: *Root) void { + scope.sym_table.deinit(); + scope.macro_table.deinit(); + scope.nodes.deinit(); + } + /// Check if the global scope contains this name, without looking into the "future", e.g. /// ignore the preprocessed decl and macro names. fn containsNow(scope: *Root, name: []const u8) bool { @@ -195,11 +202,11 @@ const Scope = struct { } } - fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType { + fn findBlockReturnType(inner: *Scope, c: *Context) clang.QualType { var scope = inner; while (true) { switch (scope.id) { - .Root => return null, + .Root => unreachable, .Block => { const block = @fieldParentPtr(Block, "base", scope); if (block.return_type) |qt| return qt; @@ -248,23 +255,35 @@ const Scope = struct { } } } + + /// Appends a node to the first block scope if inside a function, or to the root tree if not. + fn appendNode(scope: *Scope, node: Node) !void { + var scope = inner; + while (true) { + switch (scope.id) { + .Root => { + const root = @fieldParentPtr(Root, "base", scope).contains(name); + return root.nodes.append(node); + }, + .Block => { + const block = @fieldParentPtr(Block, "base", scope).contains(name); + return block.statements.append(node); + }, + else => scope = scope.parent.?, + } + } + } }; pub const Context = struct { gpa: *mem.Allocator, arena: *mem.Allocator, - token_ids: std.ArrayListUnmanaged(Token.Id) = .{}, - token_locs: std.ArrayListUnmanaged(Token.Loc) = .{}, - errors: std.ArrayListUnmanaged(ast.Error) = .{}, - source_buffer: *std.ArrayList(u8), - err: Error, source_manager: *clang.SourceManager, decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{}, alias_list: AliasList, global_scope: *Scope.Root, clang_context: *clang.ASTContext, mangle_count: u32 = 0, - root_decls: std.ArrayListUnmanaged(*ast.Node) = .{}, opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{}, /// This one is different than the root scope's name table. This contains @@ -293,40 +312,6 @@ pub const Context = struct { const column = c.source_manager.getSpellingColumnNumber(spelling_loc); return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); } - - fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call { - _ = try appendToken(c, .LParen, "("); - const node = try ast.Node.Call.alloc(c.arena, params_len); - node.* = .{ - .lhs = fn_expr, - .params_len = params_len, - .async_token = null, - .rtoken = undefined, // set after appending args - }; - return node; - } - - fn createBuiltinCall(c: *Context, name: []const u8, params_len: ast.NodeIndex) !*ast.Node.BuiltinCall { - const builtin_token = try appendToken(c, .Builtin, name); - _ = try appendToken(c, .LParen, "("); - const node = try ast.Node.BuiltinCall.alloc(c.arena, params_len); - node.* = .{ - .builtin_token = builtin_token, - .params_len = params_len, - .rparen_token = undefined, // set after appending args - }; - return node; - } - - fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block { - const block_node = try ast.Node.Block.alloc(c.arena, statements_len); - block_node.* = .{ - .lbrace = try appendToken(c, .LBrace, "{"), - .statements_len = statements_len, - .rbrace = undefined, - }; - return block_node; - } }; pub fn translate( @@ -348,9 +333,6 @@ pub fn translate( }; defer ast_unit.delete(); - var source_buffer = std.ArrayList(u8).init(gpa); - defer source_buffer.deinit(); - // For memory that has the same lifetime as the Tree that we return // from this function. var arena = std.heap.ArenaAllocator.init(gpa); @@ -367,9 +349,7 @@ pub fn translate( var context = Context{ .gpa = gpa, .arena = &arena.allocator, - .source_buffer = &source_buffer, .source_manager = ast_unit.getSourceManager(), - .err = undefined, .alias_list = AliasList.init(gpa), .global_scope = try arena.allocator.create(Scope.Root), .clang_context = ast_unit.getASTContext(), @@ -378,15 +358,12 @@ pub fn translate( defer { context.decl_table.deinit(gpa); context.alias_list.deinit(); - context.token_ids.deinit(gpa); - context.token_locs.deinit(gpa); - context.errors.deinit(gpa); context.global_names.deinit(gpa); - context.root_decls.deinit(gpa); context.opaque_demotes.deinit(gpa); + context.global_scope.deini(); } - _ = try Node.usingnamespace_builtins.init(); + try context.global_scope.nodes.append(try Node.usingnamespace_builtins.init()); try prepopulateGlobalNameTable(ast_unit, &context); @@ -403,29 +380,7 @@ pub fn translate( } } - const eof_token = try appendToken(&context, .Eof, ""); - const root_node = try ast.Node.Root.create(&arena.allocator, context.root_decls.items.len, eof_token); - mem.copy(*ast.Node, root_node.decls(), context.root_decls.items); - - if (false) { - std.debug.warn("debug source:\n{s}\n==EOF==\ntokens:\n", .{source_buffer.items}); - for (context.token_ids.items) |token| { - std.debug.warn("{}\n", .{token}); - } - } - - const tree = try arena.allocator.create(ast.Tree); - tree.* = .{ - .gpa = gpa, - .source = try arena.allocator.dupe(u8, source_buffer.items), - .token_ids = context.token_ids.toOwnedSlice(gpa), - .token_locs = context.token_locs.toOwnedSlice(gpa), - .errors = context.errors.toOwnedSlice(gpa), - .root_node = root_node, - .arena = arena.state, - .generated = true, - }; - return tree; + return ast.render(context.global_scope.nodes.items); } fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { @@ -498,7 +453,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { }, else => { const decl_name = try c.str(decl.getDeclKindName()); - try emitWarning(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); + try warn(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); }, } } @@ -1880,21 +1835,21 @@ const SuppressCast = enum { no_as, }; fn transIntegerLiteral( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.IntegerLiteral, result_used: ResultUsed, suppress_as: SuppressCast, -) TransError!*ast.Node { +) TransError!Node { var eval_result: clang.ExprEvalResult = undefined; - if (!expr.EvaluateAsInt(&eval_result, rp.c.clang_context)) { + if (!expr.EvaluateAsInt(&eval_result, c.clang_context)) { const loc = expr.getBeginLoc(); - return revertAndWarn(rp, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); + return revertAndWarn(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); } if (suppress_as == .no_as) { - const int_lit_node = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt()); - return maybeSuppressResult(rp, scope, result_used, int_lit_node); + const int_lit_node = try transCreateNodeAPInt(c, eval_result.Val.getInt()); + return maybeSuppressResult(c, scope, result_used, int_lit_node); } // Integer literals in C have types, and this can matter for several reasons. @@ -1908,51 +1863,26 @@ fn transIntegerLiteral( // But the first step is to be correct, and the next step is to make the output more elegant. // @as(T, x) - const expr_base = @ptrCast(*const clang.Expr, expr); - const as_node = try rp.c.createBuiltinCall("@as", 2); - const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc()); - as_node.params()[0] = ty_node; - _ = try appendToken(rp.c, .Comma, ","); - as_node.params()[1] = try transCreateNodeAPInt(rp.c, eval_result.Val.getInt()); - - as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - 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.?); + const ty_node = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()); + const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt()); + const as = try Node.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs }); + return maybeSuppressResult(c, scope, result_used, as); } fn transReturnStmt( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.ReturnStmt, ) TransError!*ast.Node { - const return_kw = try appendToken(rp.c, .Keyword_return, "return"); - 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, ")"); + const val_expr = expr.getRetValue() orelse + return Node.return_void.init(); - rhs = &bool_to_int_node.base; + var rhs = try transExprCoercing(c, scope, val_expr, .used, .r_value); + const return_qt = scope.findBlockReturnType(c); + if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) { + rhs = try Node.bool_to_int.create(c.arena, rhs); } - const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ - .ltoken = return_kw, - .tag = .Return, - }, .{ - .rhs = rhs, - }); - _ = try appendToken(rp.c, .Semicolon, ";"); - return &return_expr.base; + return Node.@"return".create(c.arena, rhs); } fn transStringLiteral( @@ -2251,6 +2181,33 @@ fn transExpr( return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue); } +/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore +/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. +fn transExprCoercing( + c: *Context, + scope: *Scope, + expr: *const clang.Expr, + used: ResultUsed, + lrvalue: LRValue, +) TransError!Node { + switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { + .IntegerLiteralClass => { + return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as); + }, + .CharacterLiteralClass => { + return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, expr), .used, .no_as); + }, + .UnaryOperatorClass => { + const un_expr = @ptrCast(*const clang.UnaryOperator, expr); + if (un_expr.getOpcode() == .Extension) { + return transExprCoercing(c, scope, un_expr.getSubExpr(), used, lrvalue); + } + }, + else => {}, + } + return transExpr(c, scope, expr, .used, .r_value); +} + fn transInitListExprRecord( rp: RestorePoint, scope: *Scope, @@ -3257,71 +3214,59 @@ fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool { } } -fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!*ast.Node { +fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!Node { const op_expr = stmt.getSubExpr(); switch (stmt.getOpcode()) { .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePostCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) + return transCreatePostCrement(c, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) else - return transCreatePostCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), + return transCreatePostCrement(c, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePostCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) + return transCreatePostCrement(c, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) else - return transCreatePostCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), + return transCreatePostCrement(c, scope, stmt, .AssignSub, .MinusEqual, "-=", used), .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePreCrement(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) + return transCreatePreCrement(c, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) else - return transCreatePreCrement(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), + return transCreatePreCrement(c, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePreCrement(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) + return transCreatePreCrement(c, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) else - return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), + return transCreatePreCrement(c, 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; + return Node.address_of.create(c.arena, try transExpr(c, scope, op_expr, used, .r_value)); }, .Deref => { - const value_node = try transExpr(rp, scope, op_expr, used, .r_value); + const node = try transExpr(c, scope, op_expr, used, .r_value); var is_ptr = false; const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr); if (fn_ty != null and is_ptr) - return value_node; - const unwrapped = try transCreateNodeUnwrapNull(rp.c, value_node); - return transCreateNodePtrDeref(rp.c, unwrapped); + return node; + return Node.unwrap_deref.create(c.arena, node); }, - .Plus => return transExpr(rp, scope, op_expr, used, .r_value), + .Plus => return transExpr(c, scope, op_expr, used, .r_value), .Minus => { if (!qualTypeHasWrappingOverflow(op_expr.getType())) { - const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-"); - op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); - return &op_node.base; + return Node.negate.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); } else if (cIsUnsignedInteger(op_expr.getType())) { - // we gotta emit 0 -% x - const zero = try transCreateNodeInt(rp.c, 0); - const token = try appendToken(rp.c, .MinusPercent, "-%"); - const expr = try transExpr(rp, scope, op_expr, .used, .r_value); - return transCreateNodeInfixOp(rp, scope, zero, .SubWrap, token, expr, used, true); + // use -% x for unsigned integers + return Node.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); } else - return revertAndWarn(rp, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); + return revertAndWarn(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); }, .Not => { - const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~"); - op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); - return &op_node.base; + return Node.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); }, .LNot => { - const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); - op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true); - return &op_node.base; + return Node.not.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); }, .Extension => { - return transExpr(rp, scope, stmt.getSubExpr(), used, .l_value); + return transExpr(c, scope, stmt.getSubExpr(), used, .l_value); }, - else => return revertAndWarn(rp, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), + else => return revertAndWarn(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), } } @@ -3910,8 +3855,7 @@ fn maybeSuppressResult( return &op_node.base; } -fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: *ast.Node) !void { - try c.root_decls.append(c.gpa, decl_node); +fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { _ = try c.global_scope.sym_table.put(name, decl_node); } @@ -4356,7 +4300,7 @@ fn transCreateNodePtrType( return node; } -fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node { +fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { const num_limbs = math.cast(usize, int.getNumWords()) catch |err| switch (err) { error.Overflow => return error.OutOfMemory, }; @@ -4396,14 +4340,7 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !*ast.Node { const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, }; - defer c.arena.free(str); - const token = try appendToken(c, .IntegerLiteral, str); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .IntegerLiteral }, - .token = token, - }; - return &node.base; + return Node.int_literal.create(c.arena, str); } fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 638d4cefa0..cc5e8dd7ce 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -109,11 +109,16 @@ pub const Node = extern union { div_trunc, /// @boolToInt(lhs, rhs) bool_to_int, + /// @as(lhs, rhs) + as, negate, negate_wrap, bit_not, not, + address_of, + // operand.?.* + unwrap_deref, block, @"break", @@ -151,9 +156,8 @@ pub const Node = extern union { .bit_not, .not, .optional_type, - .c_pointer, - .single_pointer, - .array_type, + .address_of, + .unwrap_deref, => Payload.UnOp, .add, @@ -208,6 +212,7 @@ pub const Node = extern union { .rem, .int_cast, .bool_to_int, + .as, => Payload.BinOp, .int, @@ -236,6 +241,9 @@ pub const Node = extern union { .container_init => Payload.ContainerInit, .std_meta_cast => Payload.Infix, .block => Payload.Block, + .c_pointer => Payload.Pointer, + .single_pointer => Payload.Pointer, + .array_type => Payload.Array, }; } @@ -424,9 +432,26 @@ pub const Payload = struct { base: Node = .{ .tag = .@"break" }, data: *Block }; + + pub const Array = struct { + base: Node, + data: struct { + elem_type: Node, + len: Node, + }, + }; + + pub const Pointer = struct { + base: Node, + data: struct { + elem_type: Node, + is_const: bool, + is_volatile: bool, + }, + }; }; -/// Converts the nodes into a Zig ast and then renders it. -pub fn render(allocator: *Allocator, nodes: []const Node) !void { +/// Converts the nodes into a Zig ast. +pub fn render(allocator: *Allocator, nodes: []const Node) !*ast.Tree { @panic("TODO"); } From f36849fed24b54476ecadacb52d9a1b55ae14274 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 7 Feb 2021 23:13:40 +0200 Subject: [PATCH 07/36] translate-c: convert function translation --- src/translate_c.zig | 378 ++++++++++------------------------------ src/translate_c/ast.zig | 68 +++++++- 2 files changed, 153 insertions(+), 293 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index a8285e3036..2bbfe0a562 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -469,7 +469,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { return visitFnDecl(c, def); } - const rp = makeRestorePoint(c); const fn_decl_loc = fn_decl.getLocation(); const has_body = fn_decl.hasBody(); const storage_class = fn_decl.getStorageClass(); @@ -513,9 +512,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { decl_ctx.has_body = false; decl_ctx.storage_class = .Extern; decl_ctx.is_export = false; - try emitWarning(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{}); + try warn(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{}); } - break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { + break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { error.UnsupportedType => { return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); }, @@ -524,7 +523,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }, .FunctionNoProto => blk: { const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type); - break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { + break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { error.UnsupportedType => { return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{}); }, @@ -535,13 +534,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }; if (!decl_ctx.has_body) { - const semi_tok = try appendToken(c, .Semicolon, ";"); return addTopLevelDecl(c, fn_name, &proto_node.base); } // 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); + var block_scope = try Scope.Block.init(c, &c.global_scope.base, false); block_scope.return_type = return_qt; defer block_scope.deinit(); @@ -559,34 +557,22 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const is_const = qual_type.isConstQualified(); const mangled_param_name = try block_scope.makeMangledName(c, param_name); + param.name = mangled_param_name; if (!is_const) { const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); const arg_name = try block_scope.makeMangledName(c, bare_arg_name); + param.name = arg_name; - const mut_tok = try appendToken(c, .Keyword_var, "var"); - const name_tok = try appendIdentifier(c, mangled_param_name); - const eq_token = try appendToken(c, .Equal, "="); - const init_node = try transCreateNodeIdentifier(c, arg_name); - const semicolon_token = try appendToken(c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(c.arena, .{ - .mut_token = mut_tok, - .name_token = name_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .init_node = init_node, - }); - try block_scope.statements.append(&node.base); - param.name_token = try appendIdentifier(c, arg_name); - _ = try appendToken(c, .Colon, ":"); + const redecl_node = try Node.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); + try block_scope.statements.append(redecl_node); } param_id += 1; } const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt); - transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) { + transCompoundStmtInline(c, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, @@ -600,37 +586,31 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { if (block_scope.statements.items.len > 0) { var last = block_scope.statements.items[block_scope.statements.items.len - 1]; while (true) { - switch (last.tag) { - .Block, .LabeledBlock => { - const stmts = last.blockStatements(); - if (stmts.len == 0) break; + switch (last.tag()) { + .block => { + const block = last.castTag(.block).?; + if (block.data.stmts.len == 0) break; - last = stmts[stmts.len - 1]; + last = block.data.stmts[block.data.stmts.len - 1]; }, // no extra return needed - .Return => break :blk, + .@"return", .return_void => break :blk, else => break, } } } - const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ - .ltoken = try appendToken(rp.c, .Keyword_return, "return"), - .tag = .Return, - }, .{ - .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { - error.OutOfMemory => |e| return e, - error.UnsupportedTranslation, - error.UnsupportedType, - => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}), - }, - }); - _ = try appendToken(rp.c, .Semicolon, ";"); - try block_scope.statements.append(&return_expr.base); + const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.UnsupportedTranslation, + error.UnsupportedType, + => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}), + }; + const ret = try Node.@"return".create(c.arena, rhs); + try block_scope.statements.append(ret); } - const body_node = try block_scope.complete(rp.c); - proto_node.setBodyNode(body_node); + proto_node.body = try block_scope.complete(c); return addTopLevelDecl(c, fn_name, &proto_node.base); } @@ -2440,16 +2420,16 @@ fn transInitListExpr( } fn transZeroInitExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, source_loc: clang.SourceLocation, ty: *const clang.Type, -) TransError!*ast.Node { +) TransError!Node { switch (ty.getTypeClass()) { .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); switch (builtin_ty.getKind()) { - .Bool => return try transCreateNodeBoolLiteral(rp.c, false), + .Bool => return Node.false_literal.init(), .Char_U, .UChar, .Char_S, @@ -2470,16 +2450,16 @@ fn transZeroInitExpr( .Float128, .Float16, .LongDouble, - => return transCreateNodeInt(rp.c, 0), - else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), + => return Node.zero_literal.init(), + else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), } }, - .Pointer => return transCreateNodeNullLiteral(rp.c), + .Pointer => return Node.null_literal.init(), .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); const typedef_decl = typedef_ty.getDecl(); return transZeroInitExpr( - rp, + c, scope, source_loc, typedef_decl.getUnderlyingType().getTypePtr(), @@ -2488,7 +2468,7 @@ fn transZeroInitExpr( else => {}, } - return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}); + return fail(c, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}); } fn transImplicitValueInitExpr( @@ -3985,7 +3965,7 @@ fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.Sourc if (int_bit_width != 0) { // we can perform the log2 now. const cast_bit_width = math.log2_int(u64, int_bit_width); - return Node.uint_type.create(c.arena, cast_bit_width); + return Node.log2_int_type.create(c.arena, cast_bit_width); } const zig_type = try transQualType(c, qt, source_loc); @@ -4886,7 +4866,7 @@ const FnDeclContext = struct { }; fn transCC( - rp: RestorePoint, + c: *Context, fn_ty: *const clang.FunctionType, source_loc: clang.SourceLocation, ) !CallingConvention { @@ -4899,7 +4879,7 @@ fn transCC( .X86ThisCall => return CallingConvention.Thiscall, .AAPCS => return CallingConvention.AAPCS, .AAPCS_VFP => return CallingConvention.AAPCSVFP, - else => return revertAndWarn( + else => return fail( rp, error.UnsupportedType, source_loc, @@ -4910,33 +4890,33 @@ fn transCC( } fn transFnProto( - rp: RestorePoint, + c: *Context, fn_decl: ?*const clang.FunctionDecl, fn_proto_ty: *const clang.FunctionProtoType, source_loc: clang.SourceLocation, fn_decl_context: ?FnDeclContext, is_pub: bool, -) !*ast.Node.FnProto { +) !Node.FnProto { const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty); - const cc = try transCC(rp, fn_ty, source_loc); + const cc = try transCC(c, fn_ty, source_loc); const is_var_args = fn_proto_ty.isVariadic(); - return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); + return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); } fn transFnNoProto( - rp: RestorePoint, + c: *Context, fn_ty: *const clang.FunctionType, source_loc: clang.SourceLocation, fn_decl_context: ?FnDeclContext, is_pub: bool, -) !*ast.Node.FnProto { - const cc = try transCC(rp, fn_ty, source_loc); +) !Node.FnProto { + const cc = try transCC(c, fn_ty, source_loc); const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true; - return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); + return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); } fn finishTransFnProto( - rp: RestorePoint, + c: *Context, fn_decl: ?*const clang.FunctionDecl, fn_proto_ty: ?*const clang.FunctionProtoType, fn_ty: *const clang.FunctionType, @@ -4945,128 +4925,77 @@ fn finishTransFnProto( is_var_args: bool, cc: CallingConvention, is_pub: bool, -) !*ast.Node.FnProto { +) !*ast.Payload.Func { const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false; // TODO check for always_inline attribute // TODO check for align attribute - // pub extern fn name(...) T - const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null; - const extern_export_inline_tok = if (is_export) - try appendToken(rp.c, .Keyword_export, "export") - else if (is_extern) - try appendToken(rp.c, .Keyword_extern, "extern") - else - null; - const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn"); - const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null; - const lparen_tok = try appendToken(rp.c, .LParen, "("); - - var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa); + var fn_params = std.ArrayList(ast.Payload.Func.Param).init(c.gpa); defer fn_params.deinit(); const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0; - try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node + try fn_params.ensureCapacity(param_count); var i: usize = 0; while (i < param_count) : (i += 1) { const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i)); + const is_noalias = param_qt.isRestrictQualified(); - const noalias_tok = if (param_qt.isRestrictQualified()) try appendToken(rp.c, .Keyword_noalias, "noalias") else null; + const param_name: ?[]const u8 = + if (fn_decl) |decl| + blk: { + const param = decl.getParamDecl(@intCast(c_uint, i)); + const param_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin()); + if (param_name.len < 1) + break :blk null; - const param_name_tok: ?ast.TokenIndex = blk: { - if (fn_decl) |decl| { - const param = decl.getParamDecl(@intCast(c_uint, i)); - const param_name: []const u8 = try rp.c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin()); - if (param_name.len < 1) - break :blk null; - - const result = try appendIdentifier(rp.c, param_name); - _ = try appendToken(rp.c, .Colon, ":"); - break :blk result; - } - break :blk null; - }; - - const type_node = try transQualType(rp, param_qt, source_loc); + break :blk param_name; + } else null; + const type_node = try transQualType(c, param_qt, source_loc); fn_params.addOneAssumeCapacity().* = .{ - .doc_comments = null, - .comptime_token = null, - .noalias_token = noalias_tok, - .name_token = param_name_tok, - .param_type = .{ .type_expr = type_node }, + .is_noalias = is_noalias, + .name = param_name, + .type = type_node, }; - - if (i + 1 < param_count) { - _ = try appendToken(rp.c, .Comma, ","); - } } - const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: { - if (param_count > 0) { - _ = try appendToken(rp.c, .Comma, ","); - } - break :blk try appendToken(rp.c, .Ellipsis3, "..."); - } else null; - - const rparen_tok = try appendToken(rp.c, .RParen, ")"); - - const linksection_expr = blk: { + const link_section_string: ?[]const u8 = blk: { if (fn_decl) |decl| { var str_len: usize = undefined; if (decl.getSectionAttribute(&str_len)) |str_ptr| { - _ = try appendToken(rp.c, .Keyword_linksection, "linksection"); - _ = try appendToken(rp.c, .LParen, "("); - const expr = try transCreateNodeStringLiteral( - rp.c, - try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), - ); - _ = try appendToken(rp.c, .RParen, ")"); - - break :blk expr; + break :blk str_ptr[0..str_len]; } } break :blk null; }; - const align_expr = blk: { + const alignment: c_uint = blk: { if (fn_decl) |decl| { - const alignment = decl.getAlignedAttribute(rp.c.clang_context); + const alignment = decl.getAlignedAttribute(c.clang_context); if (alignment != 0) { - _ = try appendToken(rp.c, .Keyword_align, "align"); - _ = try appendToken(rp.c, .LParen, "("); // Clang reports the alignment in bits - const expr = try transCreateNodeInt(rp.c, alignment / 8); - _ = try appendToken(rp.c, .RParen, ")"); - - break :blk expr; + break :blk alignment / 8; } } break :blk null; }; - const callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: { - _ = try appendToken(rp.c, .Keyword_callconv, "callconv"); - _ = try appendToken(rp.c, .LParen, "("); - const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc)); - _ = try appendToken(rp.c, .RParen, ")"); - break :blk expr; - }; + const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc; const return_type_node = blk: { if (fn_ty.getNoReturnAttr()) { - break :blk try transCreateNodeIdentifier(rp.c, "noreturn"); + break :blk Node.noreturn_type.init(); } else { const return_qt = fn_ty.getReturnType(); if (isCVoid(return_qt)) { // convert primitive c_void to actual void (only for return type) - break :blk try transCreateNodeIdentifier(rp.c, "void"); + break :blk Node.void_type.init(); } else { - break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) { + break :blk transQualType(c, return_qt, source_loc) catch |err| switch (err) { error.UnsupportedType => { - try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{}); + try warn(c, source_loc, "unsupported function proto return type", .{}); return err; }, error.OutOfMemory => |e| return e, @@ -5075,32 +5004,23 @@ fn finishTransFnProto( } }; - // We need to reserve an undefined (but non-null) body node to set later. - var body_node: ?*ast.Node = null; - if (fn_decl_context) |ctx| { - if (ctx.has_body) { - // TODO: we should be able to use undefined here but - // it causes a bug. This is undefined without zig language - // being aware of it. - body_node = @intToPtr(*ast.Node, 0x08); - } - } - - const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{ - .params_len = fn_params.items.len, - .return_type = .{ .Explicit = return_type_node }, - .fn_token = fn_tok, - }, .{ - .visib_token = pub_tok, - .name_token = name_tok, - .extern_export_inline_token = extern_export_inline_tok, - .align_expr = align_expr, - .section_expr = linksection_expr, - .callconv_expr = callconv_expr, - .body_node = body_node, - .var_args_token = var_args_token, - }); - mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); + const fn_proto = try c.arena.create(ast.Payload.Func); + fn_proto.* = .{ + .base = .{ .tag = .func }, + .data = .{ + .is_pub = is_pub, + .is_extern = is_extern, + .is_export = is_export, + .is_var_args = is_var_args, + .name = name, + .link_section_string = link_section_string, + .explicit_callconv = explicit_callconv, + .params = c.arena.dupe(ast.Payload.Func.Param, fn_params.items), + .return_type = return_node, + .body = null, + .alignment = alignment, + }, + }; return fn_proto; } @@ -5122,124 +5042,12 @@ fn fail( } pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void { + // location // pub const name = @compileError(msg); - const pub_tok = try appendToken(c, .Keyword_pub, "pub"); - const const_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, name); - const eq_tok = try appendToken(c, .Equal, "="); - const builtin_tok = try appendToken(c, .Builtin, "@compileError"); - const lparen_tok = try appendToken(c, .LParen, "("); - const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args); - const rparen_tok = try appendToken(c, .RParen, ")"); - const semi_tok = try appendToken(c, .Semicolon, ";"); - _ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)}); - - const msg_node = try c.arena.create(ast.Node.OneToken); - msg_node.* = .{ - .base = .{ .tag = .StringLiteral }, - .token = msg_tok, - }; - - const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1); - call_node.* = .{ - .builtin_token = builtin_tok, - .params_len = 1, - .rparen_token = rparen_tok, - }; - call_node.params()[0] = &msg_node.base; - - const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = const_tok, - .semicolon_token = semi_tok, - }, .{ - .visib_token = pub_tok, - .eq_token = eq_tok, - .init_node = &call_node.base, - }); - try addTopLevelDecl(c, name, &var_decl_node.base); -} - -fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex { - std.debug.assert(token_id != .Identifier); // use appendIdentifier - return appendTokenFmt(c, token_id, "{s}", .{bytes}); -} - -fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex { - assert(token_id != .Invalid); - - try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1); - try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1); - - const start_index = c.source_buffer.items.len; - try c.source_buffer.writer().print(format ++ " ", args); - - c.token_ids.appendAssumeCapacity(token_id); - c.token_locs.appendAssumeCapacity(.{ - .start = start_index, - .end = c.source_buffer.items.len - 1, // back up before the space - }); - - return c.token_ids.items.len - 1; -} - -// TODO hook up with codegen -fn isZigPrimitiveType(name: []const u8) bool { - if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { - for (name[1..]) |c| { - switch (c) { - '0'...'9' => {}, - else => return false, - } - } - return true; - } - // void is invalid in c so it doesn't need to be checked. - return mem.eql(u8, name, "comptime_float") or - mem.eql(u8, name, "comptime_int") or - mem.eql(u8, name, "bool") or - mem.eql(u8, name, "isize") or - mem.eql(u8, name, "usize") or - mem.eql(u8, name, "f16") or - mem.eql(u8, name, "f32") or - mem.eql(u8, name, "f64") or - mem.eql(u8, name, "f128") or - mem.eql(u8, name, "c_longdouble") or - mem.eql(u8, name, "noreturn") or - mem.eql(u8, name, "type") or - mem.eql(u8, name, "anyerror") or - mem.eql(u8, name, "c_short") or - mem.eql(u8, name, "c_ushort") or - mem.eql(u8, name, "c_int") or - mem.eql(u8, name, "c_uint") or - mem.eql(u8, name, "c_long") or - mem.eql(u8, name, "c_ulong") or - mem.eql(u8, name, "c_longlong") or - mem.eql(u8, name, "c_ulonglong"); -} - -fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex { - return appendTokenFmt(c, .Identifier, "{}", .{std.zig.fmtId(name)}); -} - -fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { - const token_index = try appendIdentifier(c, name); - const identifier = try c.arena.create(ast.Node.OneToken); - identifier.* = .{ - .base = .{ .tag = .Identifier }, - .token = token_index, - }; - return &identifier.base; -} - -fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node { - const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name}); - const identifier = try c.arena.create(ast.Node.OneToken); - identifier.* = .{ - .base = .{ .tag = .Identifier }, - .token = token_index, - }; - return &identifier.base; + const location_comment = std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)}); + try c.global_scope.nodes.append(try Node.warning.create(c.arena, location_comment)); + const fail_msg = std.fmt.allocPrint(c.arena, format, args); + try c.global_scope.nodes.append(try Node.fail_decl.create(c.arena, fail_msg)); } pub fn freeErrors(errors: []ClangErrMsg) void { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index cc5e8dd7ce..825d36add1 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -14,6 +14,10 @@ pub const Node = extern union { true_literal, false_literal, empty_block, + return_void, + zero_literal, + void_type, + noreturn_type, /// pub usingnamespace @import("std").c.builtins; usingnamespace_builtins, // After this, the tag requires a payload. @@ -99,6 +103,7 @@ pub const Node = extern union { bit_or, bit_xor, + log2_int_type, /// @import("std").math.Log2Int(operand) std_math_Log2Int, /// @intCast(lhs, rhs) @@ -132,7 +137,13 @@ pub const Node = extern union { single_pointer, array_type, - pub const last_no_payload_tag = Tag.false_literal; + + // pub const name = @compileError(msg); + fail_decl, + // var actual = mangled; + arg_redecl, + + pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; pub fn Type(tag: Tag) ?type { @@ -144,6 +155,10 @@ pub const Node = extern union { .false_litral, .empty_block, .usingnamespace_builtins, + .return_void, + .zero_literal, + .void_type, + .noreturn_type, => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .array_access, @@ -227,6 +242,7 @@ pub const Node = extern union { .sizeof, .alignof, .type, + .fail_decl, => Payload.Value, .@"if" => Payload.If, .@"while" => Payload.While, @@ -244,6 +260,8 @@ pub const Node = extern union { .c_pointer => Payload.Pointer, .single_pointer => Payload.Pointer, .array_type => Payload.Array, + .arg_redecl => Payload.ArgRedecl, + .log2_int_type => Payload.Log2IntType, }; } @@ -265,6 +283,24 @@ pub const Node = extern union { return std.meta.fieldInfo(t.Type(), .data).field_type; } }; + + pub fn tag(self: Node) Tag { + if (self.tag_if_small_enough < Tag.no_payload_count) { + return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough)); + } else { + return self.ptr_otherwise.tag; + } + } + + pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() { + if (self.tag_if_small_enough < Tag.no_payload_count) + return null; + + if (self.ptr_otherwise.tag == t) + return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise); + + return null; + } }; pub const Payload = struct { @@ -360,19 +396,22 @@ pub const Payload = struct { pub const Func = struct { base: Node = .{.func}, data: struct { - @"pub": bool, - @"extern": bool, - @"export": bool, + is_pub: bool, + is_extern: bool, + is_export: bool, + is_var_args: bool, name: []const u8, - cc: std.builtin.CallingConvention, + link_section_string: ?[]const u8, + explicit_callconv: ?std.builtin.CallingConvention, params: []Param, - return_type: Type, + return_type: Node, body: ?Node, + alignment: c_uint, pub const Param = struct { - @"noalias": bool, + is_noalias: bool, name: ?[]const u8, - type: Type, + type: Node, }; }, }; @@ -449,6 +488,19 @@ pub const Payload = struct { is_volatile: bool, }, }; + + pub const ArgRedecl = struct { + base: Node, + data: struct { + actual: []const u8, + mangled: []const u8, + }, + }; + + pub const Log2IntType = struct { + base: Node, + data: std.math.Log2Int(u64), + }; }; /// Converts the nodes into a Zig ast. From bb867b071a9e2fa69f39a45532dffa0f51ffdbfd Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 8 Feb 2021 11:07:44 +0200 Subject: [PATCH 08/36] translate-c: convert vardecl and typedef --- src/astgen.zig | 70 +++---- src/translate_c.zig | 425 ++++++++++++++++------------------------ src/translate_c/ast.zig | 42 ++-- 3 files changed, 231 insertions(+), 306 deletions(-) diff --git a/src/astgen.zig b/src/astgen.zig index 56d1497f63..dd600e2840 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -2664,8 +2664,11 @@ fn identifier( return mod.failNode(scope, ident, "TODO implement '_' identifier", .{}); } - if (getSimplePrimitiveValue(ident_name)) |typed_value| { - const result = try addZIRInstConst(mod, scope, src, typed_value); + if (simple_types.get(ident_name)) |val_tag| { + const result = try addZIRInstConst(mod, scope, src, TypedValue{ + .ty = Type.initTag(.type), + .val = Value.initTag(val_tag), + }); return rvalue(mod, scope, rl, result); } @@ -3325,42 +3328,33 @@ fn callExpr( return rvalue(mod, scope, rl, result); } -fn getSimplePrimitiveValue(name: []const u8) ?TypedValue { - const simple_types = std.ComptimeStringMap(Value.Tag, .{ - .{ "u8", .u8_type }, - .{ "i8", .i8_type }, - .{ "isize", .isize_type }, - .{ "usize", .usize_type }, - .{ "c_short", .c_short_type }, - .{ "c_ushort", .c_ushort_type }, - .{ "c_int", .c_int_type }, - .{ "c_uint", .c_uint_type }, - .{ "c_long", .c_long_type }, - .{ "c_ulong", .c_ulong_type }, - .{ "c_longlong", .c_longlong_type }, - .{ "c_ulonglong", .c_ulonglong_type }, - .{ "c_longdouble", .c_longdouble_type }, - .{ "f16", .f16_type }, - .{ "f32", .f32_type }, - .{ "f64", .f64_type }, - .{ "f128", .f128_type }, - .{ "c_void", .c_void_type }, - .{ "bool", .bool_type }, - .{ "void", .void_type }, - .{ "type", .type_type }, - .{ "anyerror", .anyerror_type }, - .{ "comptime_int", .comptime_int_type }, - .{ "comptime_float", .comptime_float_type }, - .{ "noreturn", .noreturn_type }, - }); - if (simple_types.get(name)) |tag| { - return TypedValue{ - .ty = Type.initTag(.type), - .val = Value.initTag(tag), - }; - } - return null; -} +pub const simple_types = std.ComptimeStringMap(Value.Tag, .{ + .{ "u8", .u8_type }, + .{ "i8", .i8_type }, + .{ "isize", .isize_type }, + .{ "usize", .usize_type }, + .{ "c_short", .c_short_type }, + .{ "c_ushort", .c_ushort_type }, + .{ "c_int", .c_int_type }, + .{ "c_uint", .c_uint_type }, + .{ "c_long", .c_long_type }, + .{ "c_ulong", .c_ulong_type }, + .{ "c_longlong", .c_longlong_type }, + .{ "c_ulonglong", .c_ulonglong_type }, + .{ "c_longdouble", .c_longdouble_type }, + .{ "f16", .f16_type }, + .{ "f32", .f32_type }, + .{ "f64", .f64_type }, + .{ "f128", .f128_type }, + .{ "c_void", .c_void_type }, + .{ "bool", .bool_type }, + .{ "void", .void_type }, + .{ "type", .type_type }, + .{ "anyerror", .anyerror_type }, + .{ "comptime_int", .comptime_int_type }, + .{ "comptime_float", .comptime_float_type }, + .{ "noreturn", .noreturn_type }, +}); fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool { var node = start_node; diff --git a/src/translate_c.zig b/src/translate_c.zig index 2bbfe0a562..bcefc41971 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -614,25 +614,21 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { return addTopLevelDecl(c, fn_name, &proto_node.base); } -fn transQualTypeMaybeInitialized(rp: RestorePoint, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!*ast.Node { +fn transQualTypeMaybeInitialized(c: *Context, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node { return if (decl_init) |init_expr| - transQualTypeInitialized(rp, qt, init_expr, loc) + transQualTypeInitialized(c, qt, init_expr, loc) else - transQualType(rp, qt, loc); + transQualType(c, qt, loc); } + /// if mangled_name is not null, this var decl was declared in a block scope. fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void { const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); if (c.global_scope.sym_table.contains(var_name)) return; // Avoid processing this decl twice - const rp = makeRestorePoint(c); - const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub"); - - const thread_local_token = if (var_decl.getTLSKind() == .None) - null - else - try appendToken(c, .Keyword_threadlocal, "threadlocal"); + const is_pub = mangled_name == null; + const is_thread_local = var_decl.getTLSKind() != .None; const scope = &c.global_scope.base; // TODO https://github.com/ziglang/zig/issues/3756 @@ -651,42 +647,27 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co // does the same as: // extern int foo; // int foo = 2; - const extern_tok = if (storage_class == .Extern and !has_init) - try appendToken(c, .Keyword_extern, "extern") - else if (storage_class != .Static) - try appendToken(c, .Keyword_export, "export") - else - null; + const is_extern = storage_class == .Extern and !has_init; + const is_export = !is_extern and storage_class != .Static; - const mut_tok = if (is_const) - try appendToken(c, .Keyword_const, "const") - else - try appendToken(c, .Keyword_var, "var"); - - const name_tok = try appendIdentifier(c, checked_name); - - _ = try appendToken(c, .Colon, ":"); - - const type_node = transQualTypeMaybeInitialized(rp, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { + const type_node = transQualTypeMaybeInitialized(c, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType => { return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); }, error.OutOfMemory => |e| return e, }; - var eq_tok: ast.TokenIndex = undefined; - var init_node: ?*ast.Node = null; + var init_node: ?Node = null; // If the initialization expression is not present, initialize with undefined. // If it is an integer literal, we can skip the @as since it will be redundant // with the variable type. if (has_init) { - 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), zigArraySize(rp.c, type_node) catch 0) + transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0) else - transExprCoercing(rp, scope, expr, .used, .r_value); + transExprCoercing(c, scope, expr, .used, .r_value); init_node = node_or_error catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType, @@ -695,118 +676,83 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co }, error.OutOfMemory => |e| return e, }; + if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { + init_node = try Node.bool_to_int.create(c.arena, init_node); + } } else { - init_node = try transCreateNodeUndefinedLiteral(c); + init_node = Node.undefined_literal.init(); } } else if (storage_class != .Extern) { - eq_tok = try appendToken(c, .Equal, "="); // The C language specification states that variables with static or threadlocal // storage without an initializer are initialized to a zero value. // @import("std").mem.zeroes(T) - const import_fn_call = try c.createBuiltinCall("@import", 1); - const std_node = try transCreateNodeStringLiteral(c, "\"std\""); - import_fn_call.params()[0] = std_node; - import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); - const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); - const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes"); - - const zero_init_call = try c.createCall(outer_field_access, 1); - zero_init_call.params()[0] = type_node; - zero_init_call.rtoken = try appendToken(c, .RParen, ")"); - - init_node = &zero_init_call.base; + init_node = try Node.std_mem_zeroes.create(c.arena, type_node); } - const linksection_expr = blk: { + const linksection_string = blk: { var str_len: usize = undefined; if (var_decl.getSectionAttribute(&str_len)) |str_ptr| { - _ = try appendToken(rp.c, .Keyword_linksection, "linksection"); - _ = try appendToken(rp.c, .LParen, "("); - const expr = try transCreateNodeStringLiteral( - rp.c, - try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), - ); - _ = try appendToken(rp.c, .RParen, ")"); - - break :blk expr; + break :blk str_ptr[0..str_len]; } break :blk null; }; - const align_expr = blk: { - const alignment = var_decl.getAlignedAttribute(rp.c.clang_context); + const alignment = blk: { + const alignment = var_decl.getAlignedAttribute(c.clang_context); if (alignment != 0) { - _ = try appendToken(rp.c, .Keyword_align, "align"); - _ = try appendToken(rp.c, .LParen, "("); // Clang reports the alignment in bits - const expr = try transCreateNodeInt(rp.c, alignment / 8); - _ = try appendToken(rp.c, .RParen, ")"); - - break :blk expr; + break :blk alignment / 8; } break :blk null; }; - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = try appendToken(c, .Semicolon, ";"), - }, .{ - .visib_token = visib_tok, - .thread_local_token = thread_local_token, - .eq_token = eq_tok, - .extern_export_token = extern_tok, - .type_node = type_node, - .align_node = align_expr, - .section_node = linksection_expr, - .init_node = init_node, + const node = try Node.var_decl.create(c.arena, .{ + .is_pub = is_pub, + .is_const = is_const, + .is_extern = is_extern, + .is_export = is_export, + .linksection_string = linksection_string, + .alignment = alignment, + .name = checked_name, + .type = type_node, + .init = init_node, }); return addTopLevelDecl(c, checked_name, &node.base); } -fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node { +fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !Node { _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name); - return transCreateNodeIdentifier(c, builtin_name); + return Node.identifier.create(c.arena, builtin_name); } -fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 { - const table = [_][2][]const u8{ - .{ "uint8_t", "u8" }, - .{ "int8_t", "i8" }, - .{ "uint16_t", "u16" }, - .{ "int16_t", "i16" }, - .{ "uint32_t", "u32" }, - .{ "int32_t", "i32" }, - .{ "uint64_t", "u64" }, - .{ "int64_t", "i64" }, - .{ "intptr_t", "isize" }, - .{ "uintptr_t", "usize" }, - .{ "ssize_t", "isize" }, - .{ "size_t", "usize" }, - }; +const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ + .{ "uint8_t", "u8" }, + .{ "int8_t", "i8" }, + .{ "uint16_t", "u16" }, + .{ "int16_t", "i16" }, + .{ "uint32_t", "u32" }, + .{ "int32_t", "i32" }, + .{ "uint64_t", "u64" }, + .{ "int64_t", "i64" }, + .{ "intptr_t", "isize" }, + .{ "uintptr_t", "usize" }, + .{ "ssize_t", "isize" }, + .{ "size_t", "usize" }, +}); - for (table) |entry| { - if (mem.eql(u8, checked_name, entry[0])) { - return entry[1]; - } - } - - return null; -} - -fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?*ast.Node { +fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?Node { if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name| return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice - const rp = makeRestorePoint(c); const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name; - if (checkForBuiltinTypedef(checked_name)) |builtin| { - return transTypeDefAsBuiltin(c, typedef_decl, builtin); + if (builtin_typedef_map.get(checked_name)) |builtin| { + _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin); + return Node.identifier.create(c.arena, builtin); } if (!top_level_visit) { @@ -814,42 +760,36 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev } _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name); - const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null; + const node = (try transCreateNodeTypedef(c, typedef_decl, true, checked_name)) orelse return null; try addTopLevelDecl(c, checked_name, node); return transCreateNodeIdentifier(c, checked_name); } fn transCreateNodeTypedef( - rp: RestorePoint, + c: *Context, typedef_decl: *const clang.TypedefNameDecl, toplevel: bool, checked_name: []const u8, -) Error!?*ast.Node { - const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null; - const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(rp.c, checked_name); - const eq_token = try appendToken(rp.c, .Equal, "="); +) Error!?Node { const child_qt = typedef_decl.getUnderlyingType(); const typedef_loc = typedef_decl.getLocation(); - const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) { + const init_node = transQualType(c, child_qt, typedef_loc) catch |err| switch (err) { error.UnsupportedType => { - try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{}); + try failDecl(c, typedef_loc, checked_name, "unable to resolve typedef child type", .{}); return null; }, error.OutOfMemory => |e| return e, }; - const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); - return &node.base; + const payload = try c.arena.create(ast.Payload.Typedef); + payload.* = .{ + .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[toplevel] }, + .data = .{ + .name = checked_name, + .init = init_node, + }, + }; + return Node.initPayload(&payload.base); } fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node { @@ -1399,13 +1339,15 @@ fn transBinaryOperator( const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); - const lhs = if (isBoolRes(lhs_uncasted)) + const lhs = if (isBoolRes(lhs_uncasted)) try Node.bool_to_int.create(c.arena, lhs_uncasted) - else lhs_uncasted; + else + lhs_uncasted; - const rhs = if (isBoolRes(rhs_uncasted)) + const rhs = if (isBoolRes(rhs_uncasted)) try Node.bool_to_int.create(c.arena, rhs_uncasted) - else rhs_uncasted; + else + rhs_uncasted; const payload = try c.arena.create(ast.Payload.BinOp); payload.* = .{ @@ -1415,7 +1357,7 @@ fn transBinaryOperator( .rhs = rhs, }, }; - return maybeSuppressResult(c, scope, used, &payload.base); + return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base)); } fn transCompoundStmtInline( @@ -1459,13 +1401,11 @@ fn transCStyleCastExprClass( } fn transDeclStmtOne( - rp: RestorePoint, + c: *Context, scope: *Scope, decl: *const clang.Decl, block_scope: *Scope.Block, -) TransError!*ast.Node { - const c = rp.c; - +) TransError!Node { switch (decl.getKind()) { .Var => { const var_decl = @ptrCast(*const clang.VarDecl, decl); @@ -1479,47 +1419,38 @@ fn transDeclStmtOne( .Extern, .Static => { // This is actually a global variable, put it in the global scope and reference it. // `_ = mangled_name;` - try visitVarDecl(rp.c, var_decl, mangled_name); - return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name)); + try visitVarDecl(c, var_decl, mangled_name); + return try maybeSuppressResult(c, scope, .unused, try Node.identifier.create(c.arena, mangled_name)); }, else => {}, } - const mut_tok = if (qual_type.isConstQualified()) - try appendToken(c, .Keyword_const, "const") - else - try appendToken(c, .Keyword_var, "var"); - const name_tok = try appendIdentifier(c, mangled_name); + const is_const = qual_type.isConstQualified(); - _ = try appendToken(c, .Colon, ":"); const loc = decl.getLocation(); - const type_node = try transQualTypeMaybeInitialized(rp, qual_type, decl_init, loc); + const type_node = try transQualTypeMaybeInitialized(c, qual_type, decl_init, loc); - 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), try zigArraySize(rp.c, type_node)) + try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node)) else - try transExprCoercing(rp, scope, expr, .used, .r_value) + try transExprCoercing(c, scope, expr, .used, .r_value) else try transCreateNodeUndefinedLiteral(c); if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); - builtin_node.params()[0] = init_node; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - init_node = &builtin_node.base; + init_node = try Node.bool_to_int.create(c.arena, init_node); } - const semicolon_token = try appendToken(c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .type_node = type_node, - .init_node = init_node, + return Node.var_decl.create(c.arena, .{ + .is_pub = false, + .is_const = is_const, + .is_extern = false, + .is_export = false, + .linksection_string = null, + .alignment = null, + .name = mangled_name, + .type = type_node, + .init = init_node, }); - return &node.base; }, .Typedef => { const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl); @@ -1529,7 +1460,7 @@ fn transDeclStmtOne( const underlying_type = underlying_qual.getTypePtr(); const mangled_name = try block_scope.makeMangledName(c, name); - const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse + const node = (try transCreateNodeTypedef(c, typedef_decl, false, mangled_name)) orelse return error.UnsupportedTranslation; return node; }, @@ -1543,14 +1474,14 @@ fn transDeclStmtOne( } } -fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) TransError!*ast.Node { - const block_scope = scope.findBlockScope(rp.c) catch unreachable; +fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node { + const block_scope = scope.findBlockScope(c) catch unreachable; var it = stmt.decl_begin(); const end_it = stmt.decl_end(); assert(it != end_it); while (true) : (it += 1) { - const node = try transDeclStmtOne(rp, scope, it[0], block_scope); + const node = try transDeclStmtOne(c, scope, it[0], block_scope); if (it + 1 == end_it) { return node; @@ -1562,15 +1493,15 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) T } fn transDeclRefExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.DeclRefExpr, lrvalue: LRValue, -) TransError!*ast.Node { +) TransError!Node { const value_decl = expr.getDecl(); - const name = try rp.c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin()); + const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin()); const mangled_name = scope.getAlias(name); - return transCreateNodeIdentifier(rp.c, mangled_name); + return Node.identifier.create(c.arena, mangled_name); } fn transImplicitCastExpr( @@ -1642,52 +1573,29 @@ fn transImplicitCastExpr( } fn transBoolExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed, lrvalue: LRValue, - grouped: bool, -) TransError!*ast.Node { +) TransError!Node { if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) { var is_zero: bool = undefined; - if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, rp.c.clang_context))) { - return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); + if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) { + return revertAndWarn(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); } - return try transCreateNodeBoolLiteral(rp.c, !is_zero); + return Node{ .tag = ([2]ast.Node.Tag{ .true_literal, .false_literal })[is_zero] }; } - const lparen = if (grouped) - try appendToken(rp.c, .LParen, "(") - else - undefined; - var res = try transExpr(rp, scope, expr, used, lrvalue); - + var res = try transExpr(c, scope, expr, used, lrvalue); if (isBoolRes(res)) { - if (!grouped and res.tag == .GroupedExpression) { - const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res); - res = group.expr; - // get zig fmt to work properly - tokenSlice(rp.c, group.lparen)[0] = ')'; - } return res; } - const ty = getExprQualType(rp.c, expr).getTypePtr(); - const node = try finishBoolExpr(rp, scope, expr.getBeginLoc(), ty, res, used); + const ty = getExprQualType(c, expr).getTypePtr(); + const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used); - if (grouped) { - const rparen = try appendToken(rp.c, .RParen, ")"); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = lparen, - .expr = node, - .rparen = rparen, - }; - return maybeSuppressResult(rp, scope, used, &grouped_expr.base); - } else { - return maybeSuppressResult(rp, scope, used, node); - } + return maybeSuppressResult(c, scope, used, node); } fn exprIsBooleanType(expr: *const clang.Expr) bool { @@ -1713,34 +1621,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool { } } -fn isBoolRes(res: *ast.Node) bool { - switch (res.tag) { - .BoolOr, - .BoolAnd, - .EqualEqual, - .BangEqual, - .LessThan, - .GreaterThan, - .LessOrEqual, - .GreaterOrEqual, - .BoolNot, - .BoolLiteral, +fn isBoolRes(res: Node) bool { + switch (res.tag()) { + .@"or", + .@"and", + .equal, + .note_equal, + .less_than, + .less_than_equal, + .greater_than, + .greater_than_equal, + .not, + .false_literal, + .true_literal, => return true, - - .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr), - else => return false, } } fn finishBoolExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, loc: clang.SourceLocation, ty: *const clang.Type, - node: *ast.Node, + node: Node, used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { switch (ty.getTypeClass()) { .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); @@ -1772,42 +1678,39 @@ fn finishBoolExpr( .WChar_S, .Float16, => { - const op_token = try appendToken(rp.c, .BangEqual, "!="); - const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); + // node != 0 + return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init()}); }, .NullPtr => { - const op_token = try appendToken(rp.c, .EqualEqual, "=="); - const rhs_node = try transCreateNodeNullLiteral(rp.c); - return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false); + // node == null + return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init()}); }, else => {}, } }, .Pointer => { - const op_token = try appendToken(rp.c, .BangEqual, "!="); - const rhs_node = try transCreateNodeNullLiteral(rp.c); - return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); + // node == null + return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init()}); }, .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); const typedef_decl = typedef_ty.getDecl(); const underlying_type = typedef_decl.getUnderlyingType(); - return finishBoolExpr(rp, scope, loc, underlying_type.getTypePtr(), node, used); + return finishBoolExpr(c, scope, loc, underlying_type.getTypePtr(), node, used); }, .Enum => { - const op_token = try appendToken(rp.c, .BangEqual, "!="); - const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); + // node != 0 + return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init()}); + const op_token = try appendToken(c, .BangEqual, "!="); }, .Elaborated => { const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); const named_type = elaborated_ty.getNamedType(); - return finishBoolExpr(rp, scope, loc, named_type.getTypePtr(), node, used); + return finishBoolExpr(c, scope, loc, named_type.getTypePtr(), node, used); }, else => {}, } - return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{}); + return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{}); } const SuppressCast = enum { @@ -4242,7 +4145,7 @@ fn transCreateNodeBoolInfixOp( .rhs = rhs, }, }; - return maybeSuppressResult(c, scope, used, &payload.base); + return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base)); } fn transCreateNodePtrType( @@ -4654,7 +4557,7 @@ fn transCreateNodeShiftOp( const lhs = try transExpr(c, scope, lhs_expr, .used, .l_value); const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location); - const rhs = try transExpr(c, scope, rhs_expr, .used, .r_value); + const rhs = try transExprCoercing(c, scope, rhs_expr, .used, .r_value); const rhs_casted = try Node.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type }); const payload = try c.arena.create(ast.Payload.BinOp); @@ -4663,9 +4566,9 @@ fn transCreateNodeShiftOp( .data = .{ .lhs = lhs, .rhs = rhs_casted, - } + }, }; - return &payload.base; + return Node.initPayload(&payload.base); } fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { @@ -4961,7 +4864,7 @@ fn finishTransFnProto( }; } - const link_section_string: ?[]const u8 = blk: { + const linksection_string = blk: { if (fn_decl) |decl| { var str_len: usize = undefined; if (decl.getSectionAttribute(&str_len)) |str_ptr| { @@ -5004,24 +4907,19 @@ fn finishTransFnProto( } }; - const fn_proto = try c.arena.create(ast.Payload.Func); - fn_proto.* = .{ - .base = .{ .tag = .func }, - .data = .{ - .is_pub = is_pub, - .is_extern = is_extern, - .is_export = is_export, - .is_var_args = is_var_args, - .name = name, - .link_section_string = link_section_string, - .explicit_callconv = explicit_callconv, - .params = c.arena.dupe(ast.Payload.Func.Param, fn_params.items), - .return_type = return_node, - .body = null, - .alignment = alignment, - }, - }; - return fn_proto; + return Node.func.create(c.arena, .{ + .is_pub = is_pub, + .is_extern = is_extern, + .is_export = is_export, + .is_var_args = is_var_args, + .name = name, + .linksection_string = linksection_string, + .explicit_callconv = explicit_callconv, + .params = try c.arena.dupe(ast.Payload.Func.Param, fn_params.items), + .return_type = return_node, + .body = null, + .alignment = alignment, + }); } fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { @@ -5054,6 +4952,19 @@ pub fn freeErrors(errors: []ClangErrMsg) void { errors.ptr.delete(errors.len); } +fn isZigPrimitiveType(name: []const u8) bool { + if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) { + for (name[1..]) |c| { + switch (c) { + '0'...'9' => {}, + else => return false, + } + } + return true; + } + return @import("astgen.zig").simple_types.has(name); +} + const MacroCtx = struct { source: []const u8, list: []const CToken, diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 825d36add1..9abfe215e6 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -137,11 +137,16 @@ pub const Node = extern union { single_pointer, array_type, - + /// @import("std").mem.zeroes(T) + std_mem_zeroes, // pub const name = @compileError(msg); fail_decl, // var actual = mangled; arg_redecl, + /// const name = init; + typedef, + /// pub const name = init; + pub_typedef, pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -257,11 +262,11 @@ pub const Node = extern union { .container_init => Payload.ContainerInit, .std_meta_cast => Payload.Infix, .block => Payload.Block, - .c_pointer => Payload.Pointer, - .single_pointer => Payload.Pointer, + .c_pointer, .single_pointer => Payload.Pointer, .array_type => Payload.Array, .arg_redecl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, + .typedef, .pub_typedef => Payload.Typedef, }; } @@ -301,6 +306,11 @@ pub const Node = extern union { return null; } + + pub fn initPayload(payload: *Payload) Node { + assert(@enumToInt(payload.tag) >= Tag.no_payload_count); + return .{ .ptr_otherwise = payload }; + } }; pub const Payload = struct { @@ -383,13 +393,15 @@ pub const Payload = struct { pub const VarDecl = struct { base: Node = .{ .tag = .var_decl }, data: struct { - @"pub": bool, - @"const": bool, - @"extern": bool, - @"export": bool, + is_pub: bool, + is_const: bool, + is_extern: bool, + is_export: bool, + alignment: ?c_uint, + linksection_string: ?[]const u8, name: []const u8, - type: Type, - init: Node, + type: Node, + init: ?Node, }, }; @@ -401,12 +413,12 @@ pub const Payload = struct { is_export: bool, is_var_args: bool, name: []const u8, - link_section_string: ?[]const u8, + linksection_string: ?[]const u8, explicit_callconv: ?std.builtin.CallingConvention, params: []Param, return_type: Node, body: ?Node, - alignment: c_uint, + alignment: ?c_uint, pub const Param = struct { is_noalias: bool, @@ -501,6 +513,14 @@ pub const Payload = struct { base: Node, data: std.math.Log2Int(u64), }; + + pub const Typedef = struct { + base: Node, + data: struct { + name: []const u8, + init: Node, + }, + }; }; /// Converts the nodes into a Zig ast. From 5dac3683c9e8819d6ceefb869577e3537d863c41 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 8 Feb 2021 11:52:21 +0200 Subject: [PATCH 09/36] translate-c: convert record and enum decls --- src/translate_c.zig | 296 ++++++++++++---------------------------- src/translate_c/ast.zig | 58 ++++---- 2 files changed, 125 insertions(+), 229 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index bcefc41971..3181f05f78 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -783,7 +783,7 @@ fn transCreateNodeTypedef( const payload = try c.arena.create(ast.Payload.Typedef); payload.* = .{ - .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[toplevel] }, + .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[@boolToInt(toplevel)] }, .data = .{ .name = checked_name, .init = init_node, @@ -792,7 +792,7 @@ fn transCreateNodeTypedef( return Node.initPayload(&payload.base); } -fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node { +fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Node { if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice const record_loc = record_decl.getLocation(); @@ -807,46 +807,30 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as } var container_kind_name: []const u8 = undefined; - var container_kind: std.zig.Token.Id = undefined; + var is_union = false; if (record_decl.isUnion()) { container_kind_name = "union"; - container_kind = .Keyword_union; + is_union = true; } else if (record_decl.isStruct()) { container_kind_name = "struct"; - container_kind = .Keyword_struct; } else { - try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name}); + try warn(c, record_loc, "record {s} is not a struct or union", .{bare_name}); return null; } const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); - const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, name); - - const eq_token = try appendToken(c, .Equal, "="); - - var semicolon: ast.TokenIndex = undefined; + const is_pub = !is_unnamed; const init_node = blk: { - const rp = makeRestorePoint(c); const record_def = record_decl.getDefinition() orelse { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - break :blk opaque_type; + break :blk Node.opaque_literal.init(); }; - const layout_tok = try if (record_decl.getPackedAttribute()) - appendToken(c, .Keyword_packed, "packed") - else - appendToken(c, .Keyword_extern, "extern"); - const container_tok = try appendToken(c, container_kind, container_kind_name); - const lbrace_token = try appendToken(c, .LBrace, "{"); - - var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa); - defer fields_and_decls.deinit(); + const is_packed = record_decl.getPackedAttribute(); + var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa); + defer fields.deinit(); var unnamed_field_count: u32 = 0; var it = record_def.field_begin(); @@ -858,110 +842,82 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as if (field_decl.isBitField()) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); - break :blk opaque_type; + try warn(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); + break :blk Node.opaque_literal.init(); } if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); - break :blk opaque_type; + try warn(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); + break :blk Node.opaque_literal.init(); } var is_anon = false; - var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); - if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) { + var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); + if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) { // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. - raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); + field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); unnamed_field_count += 1; is_anon = true; } - const field_name = try appendIdentifier(c, raw_name); - _ = try appendToken(c, .Colon, ":"); - const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) { + const field_type = transQualType(c, field_qt, field_loc) catch |err| switch (err) { error.UnsupportedType => { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - const opaque_type = try transCreateNodeOpaqueType(c); - semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); - break :blk opaque_type; + try warn(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); + break :blk Node.opaque_literal.init(); }, else => |e| return e, }; - const align_expr = blk_2: { + const alignment = blk_2: { const alignment = field_decl.getAlignedAttribute(c.clang_context); if (alignment != 0) { - _ = try appendToken(c, .Keyword_align, "align"); - _ = try appendToken(c, .LParen, "("); // Clang reports the alignment in bits - const expr = try transCreateNodeInt(c, alignment / 8); - _ = try appendToken(c, .RParen, ")"); - - break :blk_2 expr; + break :blk_2 alignment / 8; } break :blk_2 null; }; - const field_node = try c.arena.create(ast.Node.ContainerField); - field_node.* = .{ - .doc_comments = null, - .comptime_token = null, - .name_token = field_name, - .type_expr = field_type, - .value_expr = null, - .align_expr = align_expr, - }; - if (is_anon) { - _ = try c.decl_table.put( - c.gpa, - @ptrToInt(field_decl.getCanonicalDecl()), - raw_name, - ); + _ = try c.decl_table.put(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); } - try fields_and_decls.append(&field_node.base); - _ = try appendToken(c, .Comma, ","); + try fields.append(.{ + .name = field_name, + .type = field_type, + .alignment = alignment, + }); } - const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len); + + const payload = try c.arena.create(ast.Payload.Record); container_node.* = .{ - .layout_token = layout_tok, - .kind_token = container_tok, - .init_arg_expr = .None, - .fields_and_decls_len = fields_and_decls.items.len, - .lbrace_token = lbrace_token, - .rbrace_token = try appendToken(c, .RBrace, "}"), + .base = .{ .tag = ([2]ast.Node.Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, + .data = .{ + .is_packed = is_packed, + .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), + }, }; - mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items); - semicolon = try appendToken(c, .Semicolon, ";"); - break :blk &container_node.base; + break :blk Node.initPayload(&container_node.base); }; - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); + const payload = try c.arena.create(ast.Payload.SimpleVarDecl); + payload.* = .{ + .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, + .data = .{ + .name = name, + .init = init_node, + }, + }; - try addTopLevelDecl(c, name, &node.base); + try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); if (!is_unnamed) try c.alias_list.append(.{ .alias = bare_name, .name = name }); - return transCreateNodeIdentifier(c, name); + return Node.identifier.create(c.arena, name); } -fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node { +fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name| return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice - const rp = makeRestorePoint(c); const enum_loc = enum_decl.getLocation(); var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); @@ -974,10 +930,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); - const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, name); - const eq_token = try appendToken(c, .Equal, "="); + const is_pub = !is_unnamed; const init_node = if (enum_decl.getDefinition()) |enum_def| blk: { var pure_enum = true; @@ -991,11 +944,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node } } - const extern_tok = try appendToken(c, .Keyword_extern, "extern"); - const container_tok = try appendToken(c, .Keyword_enum, "enum"); - - var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa); - defer fields_and_decls.deinit(); + var fields = std.ArrayList(ast.Payload.Enum.Field).init(c.gpa); + defer fields.deinit(); const int_type = enum_decl.getIntegerType(); // The underlying type may be null in case of forward-declared enum @@ -1003,30 +953,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node // default to the usual integer type used for all the enums. // default to c_int since msvc and gcc default to different types - _ = try appendToken(c, .LParen, "("); - const init_arg_expr = ast.Node.ContainerDecl.InitArg{ - .Type = if (int_type.ptr != null and - !isCBuiltinType(int_type, .UInt) and - !isCBuiltinType(int_type, .Int)) - transQualType(rp, int_type, enum_loc) catch |err| switch (err) { - error.UnsupportedType => { - try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); - return null; - }, - else => |e| return e, - } - else - try transCreateNodeIdentifier(c, "c_int"), - }; - _ = try appendToken(c, .RParen, ")"); - - const lbrace_token = try appendToken(c, .LBrace, "{"); + const init_arg_expr = if (int_type.ptr != null and + !isCBuiltinType(int_type, .UInt) and + !isCBuiltinType(int_type, .Int)) + transQualType(c, int_type, enum_loc) catch |err| switch (err) { + error.UnsupportedType => { + try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); + return null; + }, + else => |e| return e, + } + else + try Node.type.create(c.arena, "c_int"); it = enum_def.enumerator_begin(); end_it = enum_def.enumerator_end(); while (it.neq(end_it)) : (it = it.next()) { const enum_const = it.deref(); - const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin()); const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name)) @@ -1034,100 +977,41 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node else enum_val_name; - const field_name_tok = try appendIdentifier(c, field_name); - - const int_node = if (!pure_enum) blk_2: { - _ = try appendToken(c, .Colon, "="); - break :blk_2 try transCreateNodeAPInt(c, enum_const.getInitVal()); - } else + const int_node = if (!pure_enum) + try transCreateNodeAPInt(c, enum_const.getInitVal()) + else null; - const field_node = try c.arena.create(ast.Node.ContainerField); - field_node.* = .{ - .doc_comments = null, - .comptime_token = null, - .name_token = field_name_tok, - .type_expr = null, - .value_expr = int_node, - .align_expr = null, - }; - - try fields_and_decls.append(&field_node.base); - _ = try appendToken(c, .Comma, ","); + try fields_and_decls.append(.{ + .name = field_name, + .value = int_node, + }); // In C each enum value is in the global namespace. So we put them there too. // At this point we can rely on the enum emitting successfully. - const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const tld_mut_tok = try appendToken(c, .Keyword_const, "const"); - const tld_name_tok = try appendIdentifier(c, enum_val_name); - const tld_eq_token = try appendToken(c, .Equal, "="); - const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1); - const enum_ident = try transCreateNodeIdentifier(c, name); - const period_tok = try appendToken(c, .Period, "."); - const field_ident = try transCreateNodeIdentifier(c, field_name); - const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp); - field_access_node.* = .{ - .base = .{ .tag = .Period }, - .op_token = period_tok, - .lhs = enum_ident, - .rhs = field_ident, - }; - cast_node.params()[0] = &field_access_node.base; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - const tld_init_node = &cast_node.base; - const tld_semicolon_token = try appendToken(c, .Semicolon, ";"); - const tld_node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = tld_name_tok, - .mut_token = tld_mut_tok, - .semicolon_token = tld_semicolon_token, - }, .{ - .visib_token = tld_visib_tok, - .eq_token = tld_eq_token, - .init_node = tld_init_node, - }); - try addTopLevelDecl(c, field_name, &tld_node.base); + try addTopLevelDecl(c, field_name, try Node.enum_redecl.create(c.arena, .{ + .enum_val_name = enum_val_name, + .field_name = field_name, + .enum_name = name, + })); } - // make non exhaustive - const field_node = try c.arena.create(ast.Node.ContainerField); - field_node.* = .{ - .doc_comments = null, - .comptime_token = null, - .name_token = try appendIdentifier(c, "_"), - .type_expr = null, - .value_expr = null, - .align_expr = null, - }; - try fields_and_decls.append(&field_node.base); - _ = try appendToken(c, .Comma, ","); - const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len); - container_node.* = .{ - .layout_token = extern_tok, - .kind_token = container_tok, - .init_arg_expr = init_arg_expr, - .fields_and_decls_len = fields_and_decls.items.len, - .lbrace_token = lbrace_token, - .rbrace_token = try appendToken(c, .RBrace, "}"), - }; - mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items); - break :blk &container_node.base; + break :blk try Node.@"enum".create(c.arena, try c.arena.dupe(ast.Payload.Enum.Field, fields.items)); } else blk: { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {}); - break :blk try transCreateNodeOpaqueType(c); + break :blk Node.opaque_literal.init(); }; - const semicolon_token = try appendToken(c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); + const payload = try c.arena.create(ast.Payload.SimpleVarDecl); + payload.* = .{ + .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, + .data = .{ + .name = name, + .init = init_node, + }, + }; - try addTopLevelDecl(c, name, &node.base); + try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); if (!is_unnamed) try c.alias_list.append(.{ .alias = bare_name, .name = name }); return transCreateNodeIdentifier(c, name); @@ -1382,22 +1266,22 @@ fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt } fn transCStyleCastExprClass( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.CStyleCastExpr, result_used: ResultUsed, lrvalue: LRValue, -) TransError!*ast.Node { +) TransError!Node { const sub_expr = stmt.getSubExpr(); const cast_node = (try transCCast( - rp, + c, scope, stmt.getBeginLoc(), stmt.getType(), sub_expr.getType(), - try transExpr(rp, scope, sub_expr, .used, lrvalue), + try transExpr(c, scope, sub_expr, .used, lrvalue), )); - return maybeSuppressResult(rp, scope, result_used, cast_node); + return maybeSuppressResult(c, scope, result_used, cast_node); } fn transDeclStmtOne( diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 9abfe215e6..b926efdbef 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -10,6 +10,7 @@ pub const Node = extern union { pub const Tag = enum { null_literal, undefined_literal, + /// opaque {} opaque_literal, true_literal, false_literal, @@ -42,6 +43,7 @@ pub const Node = extern union { func, warning, failed_decl, + /// All enums are non-exhaustive @"enum", @"struct", @"union", @@ -145,8 +147,12 @@ pub const Node = extern union { arg_redecl, /// const name = init; typedef, + var_simple, /// pub const name = init; pub_typedef, + pub_var_simple, + /// pub const enum_field_name = @enumToInt(enum_name.field_name); + enum_redecl, pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -266,7 +272,8 @@ pub const Node = extern union { .array_type => Payload.Array, .arg_redecl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, - .typedef, .pub_typedef => Payload.Typedef, + .typedef, .pub_typedef, .pub_var_simple => Payload.SimpleVarDecl, + .enum_redecl => Payload.EnumRedecl, }; } @@ -419,41 +426,37 @@ pub const Payload = struct { return_type: Node, body: ?Node, alignment: ?c_uint, - - pub const Param = struct { - is_noalias: bool, - name: ?[]const u8, - type: Node, - }; }, + + pub const Param = struct { + is_noalias: bool, + name: ?[]const u8, + type: Node, + }; }; pub const Enum = struct { base: Node = .{ .tag = .@"enum" }, - data: struct { - name: ?[]const u8, - fields: []Field, + data: []Field, - pub const Field = struct { - name: []const u8, - value: ?[]const u8, - }; - }, + pub const Field = struct { + name: []const u8, + value: ?Node, + }; }; pub const Record = struct { base: Node, data: struct { - name: ?[]const u8, @"packed": bool, fields: []Field, - - pub const Field = struct { - name: []const u8, - type: Type, - alignment: c_uint, - }; }, + + pub const Field = struct { + name: []const u8, + type: Node, + alignment: ?c_uint, + }; }; pub const ArrayInit = struct { @@ -514,13 +517,22 @@ pub const Payload = struct { data: std.math.Log2Int(u64), }; - pub const Typedef = struct { + pub const SimpleVarDecl = struct { base: Node, data: struct { name: []const u8, init: Node, }, }; + + pub const EnumRedecl = struct { + base: Node, + data: struct { + enum_val_name: []const u8, + field_name: []const u8, + enum_name: []const u8, + }, + }; }; /// Converts the nodes into a Zig ast. From d8b9fca0b1c8929be99b6dff8f961afbe2fe2e3c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 Feb 2021 22:04:19 +0200 Subject: [PATCH 10/36] translate-c: convert casts and string/array init --- src/translate_c.zig | 471 +++++++++++----------------------------- src/translate_c/ast.zig | 45 ++++ 2 files changed, 174 insertions(+), 342 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 3181f05f78..0bef2cb843 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1084,7 +1084,6 @@ fn transStmt( const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?; const expr = try transExpr(c, scope, source_expr, .used, lrvalue); return maybeSuppressResult(c, scope, result_used, expr); - const node = try c.arena.create(Node.GroupedExpression); }, else => { return revertAndWarn( @@ -1389,62 +1388,56 @@ fn transDeclRefExpr( } fn transImplicitCastExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.ImplicitCastExpr, result_used: ResultUsed, -) TransError!*ast.Node { - const c = rp.c; +) TransError!Node { const sub_expr = expr.getSubExpr(); const dest_type = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); const src_type = getExprQualType(c, sub_expr); switch (expr.getCastKind()) { .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => { - const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); - return try transCCast(rp, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node); + return maybeSuppressResult(c, scope, result_used, casted); }, .LValueToRValue, .NoOp, .FunctionToPointerDecay => { - const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); - return maybeSuppressResult(rp, scope, result_used, sub_expr_node); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + return maybeSuppressResult(c, scope, result_used, sub_expr_node); }, .ArrayToPointerDecay => { if (exprIsNarrowStringLiteral(sub_expr)) { - const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); - return maybeSuppressResult(rp, scope, result_used, sub_expr_node); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + return maybeSuppressResult(c, scope, result_used, sub_expr_node); } - const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); - prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value); - - return maybeSuppressResult(rp, scope, result_used, &prefix_op.base); + const addr = try Node.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used, .r_value)); + return maybeSuppressResult(c, scope, result_used, addr); }, .NullToPointer => { - return try transCreateNodeNullLiteral(rp.c); + return Node.null_literal.init(); }, .PointerToBoolean => { // @ptrToInt(val) != 0 - const ptr_to_int = try rp.c.createBuiltinCall("@ptrToInt", 1); - ptr_to_int.params()[0] = try transExpr(rp, scope, sub_expr, .used, .r_value); - ptr_to_int.rparen_token = try appendToken(rp.c, .RParen, ")"); + const ptr_to_int = try Node.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used, .r_value)); - const op_token = try appendToken(rp.c, .BangEqual, "!="); - const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, &ptr_to_int.base, .BangEqual, op_token, rhs_node, result_used, false); + const ne = try Node.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Node.zero_literal.init() }); + return maybeSuppressResult(c, scope, result_used, ne); }, .IntegralToBoolean => { - const sub_expr_node = try transExpr(rp, scope, sub_expr, .used, .r_value); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); // The expression is already a boolean one, return it as-is if (isBoolRes(sub_expr_node)) - return sub_expr_node; + return maybeSuppressResult(c, scope, result_used, sub_expr_node); // val != 0 - const op_token = try appendToken(rp.c, .BangEqual, "!="); - const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, sub_expr_node, .BangEqual, op_token, rhs_node, result_used, false); + const ne = try Node.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Node.zero_literal.init() }); + return maybeSuppressResult(c, scope, result_used, ne); }, .BuiltinFnToFnPtr => { - return transExpr(rp, scope, sub_expr, .used, .r_value); + return transExpr(rp, scope, sub_expr, result_used, .r_value); }, else => |kind| return revertAndWarn( rp, @@ -1468,12 +1461,12 @@ fn transBoolExpr( if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) { return revertAndWarn(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); } - return Node{ .tag = ([2]ast.Node.Tag{ .true_literal, .false_literal })[is_zero] }; + return Node{ .tag = ([2]ast.Node.Tag{ .true_literal, .false_literal })[@boolToInt(is_zero)] }; } var res = try transExpr(c, scope, expr, used, lrvalue); if (isBoolRes(res)) { - return res; + return maybeSuppressResult(c, scope, used, res); } const ty = getExprQualType(c, expr).getTypePtr(); @@ -1563,18 +1556,18 @@ fn finishBoolExpr( .Float16, => { // node != 0 - return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init()}); + return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() }); }, .NullPtr => { // node == null - return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init()}); + return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init() }); }, else => {}, } }, .Pointer => { // node == null - return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init()}); + return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init() }); }, .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); @@ -1584,7 +1577,7 @@ fn finishBoolExpr( }, .Enum => { // node != 0 - return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init()}); + return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() }); const op_token = try appendToken(c, .BangEqual, "!="); }, .Elaborated => { @@ -1653,11 +1646,11 @@ fn transReturnStmt( } fn transStringLiteral( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.StringLiteral, result_used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const kind = stmt.getKind(); switch (kind) { .Ascii, .UTF8 => { @@ -1665,55 +1658,28 @@ fn transStringLiteral( const bytes_ptr = stmt.getString_bytes_begin_size(&len); const str = bytes_ptr[0..len]; - const token = try appendTokenFmt(rp.c, .StringLiteral, "\"{}\"", .{std.zig.fmtEscapes(str)}); - const node = try rp.c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .StringLiteral }, - .token = token, - }; - return maybeSuppressResult(rp, scope, result_used, &node.base); + const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(str)}); + const node = try Node.string_literal.create(c.arena, str); + return maybeSuppressResult(c, scope, result_used, node); }, .UTF16, .UTF32, .Wide => { - const node = try transWideStringLiteral(rp, scope, stmt); - return maybeSuppressResult(rp, scope, result_used, node); + const str_type = @tagName(stmt.getKind()); + const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); + const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1); + + const decl = try Node.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); + try scope.appendNode(name, decl); + const node = try Node.identifier.create(c.arena, name); + return maybeSuppressResult(c, 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| { - if (array.len_expr.castTag(.IntegerLiteral)) |int_lit| { - const tok = tokenSlice(c, int_lit.token); - return std.fmt.parseUnsigned(usize, tok, 10) catch error.UnsupportedTranslation; +fn zigArraySize(c: *Context, node: Node) TransError!usize { + if (node.castTag(.array_type)) |array| { + if (array.data.len.castTag(.int_literal)) |int_lit| { + return std.fmt.parseUnsigned(usize, int_lit.data, 10) catch error.UnsupportedTranslation; } } return error.UnsupportedTranslation; @@ -1725,11 +1691,11 @@ fn zigArraySize(c: *Context, node: *ast.Node) TransError!usize { /// 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, + c: *Context, scope: *Scope, stmt: *const clang.StringLiteral, array_size: usize, -) TransError!*ast.Node { +) TransError!Node { if (array_size == 0) return error.UnsupportedType; const str_length = stmt.getLength(); @@ -1738,40 +1704,21 @@ fn transStringLiteralAsArray( const ty = expr_base.getType().getTypePtr(); const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); - const ty_node = try rp.c.arena.create(ast.Node.ArrayType); - const op_token = try appendToken(rp.c, .LBracket, "["); - const len_expr = try transCreateNodeInt(rp.c, array_size); - _ = try appendToken(rp.c, .RBracket, "]"); - - ty_node.* = .{ - .op_token = op_token, - .rhs = try transQualType(rp, const_arr_ty.getElementType(), expr_base.getBeginLoc()), - .len_expr = len_expr, - }; - _ = try appendToken(rp.c, .LBrace, "{"); - var init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, array_size); - init_node.* = .{ - .lhs = &ty_node.base, - .rtoken = undefined, - .list_len = array_size, - }; - const init_list = init_node.list(); + const arr_type = try transQualType(c, const_arr_ty.getElementType(), expr_base.getBeginLoc()); + const init_list = try c.arena.alloc(Node, array_size); var i: c_uint = 0; const kind = stmt.getKind(); const narrow = kind == .Ascii or kind == .UTF8; while (i < str_length and i < array_size) : (i += 1) { const code_unit = stmt.getCodeUnit(i); - init_list[i] = try transCreateCharLitNode(rp.c, narrow, code_unit); - _ = try appendToken(rp.c, .Comma, ","); + init_list[i] = try transCreateCharLitNode(c, narrow, code_unit); } while (i < array_size) : (i += 1) { - init_list[i] = try transCreateNodeInt(rp.c, 0); - _ = try appendToken(rp.c, .Comma, ","); + init_list[i] = try transCreateNodeInt(c, 0); } - init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); - return &init_node.base; + return Node.array_init.create(c.arena, init_list); } fn cIsEnum(qt: clang.QualType) bool { @@ -1790,152 +1737,87 @@ fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType { } fn transCCast( - rp: RestorePoint, + c: *Context, scope: *Scope, loc: clang.SourceLocation, dst_type: clang.QualType, src_type: clang.QualType, - expr: *ast.Node, -) !*ast.Node { + expr: Node, +) !Node { if (qualTypeCanon(dst_type).isVoidType()) return expr; if (dst_type.eq(src_type)) return expr; if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) - return transCPtrCast(rp, loc, dst_type, src_type, expr); + return transCPtrCast(c, loc, dst_type, src_type, expr); + + const dst_node = try transQualType(c, dst_type, loc); if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) { // 1. If src_type is an enum, determine the underlying signed int type // 2. Extend or truncate without changing signed-ness. // 3. Bit-cast to correct signed-ness const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type); const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type); - var src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr); - - // @bitCast(dest_type, intermediate_value) - const cast_node = try rp.c.createBuiltinCall("@bitCast", 2); - cast_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); + var src_int_expr = if (cIsInteger(src_type)) expr else Node.enum_to_int.create(c.arena, expr); if (isBoolRes(src_int_expr)) { - const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1); - bool_to_int_node.params()[0] = src_int_expr; - bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - src_int_expr = &bool_to_int_node.base; + src_int_expr = try Node.bool_to_int.create(c.arena, src_int_expr); } switch (cIntTypeCmp(dst_type, src_int_type)) { .lt => { // @truncate(SameSignSmallerInt, src_int_expr) - const trunc_node = try rp.c.createBuiltinCall("@truncate", 2); - const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed); - trunc_node.params()[0] = ty_node; - _ = try appendToken(rp.c, .Comma, ","); - trunc_node.params()[1] = src_int_expr; - trunc_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - cast_node.params()[1] = &trunc_node.base; + const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); + src_int_expr = try Node.truncate.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); }, .gt => { // @as(SameSignBiggerInt, src_int_expr) - const as_node = try rp.c.createBuiltinCall("@as", 2); - const ty_node = try transQualTypeIntWidthOf(rp.c, dst_type, src_type_is_signed); - as_node.params()[0] = ty_node; - _ = try appendToken(rp.c, .Comma, ","); - as_node.params()[1] = src_int_expr; - as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - cast_node.params()[1] = &as_node.base; + const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); + src_int_expr = try Node.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); }, .eq => { - cast_node.params()[1] = src_int_expr; + // src_int_expr = src_int_expr }, } - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &cast_node.base; + // @bitCast(dest_type, intermediate_value) + return Node.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr }); } if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { // @intCast(dest_type, @ptrToInt(val)) - const cast_node = try rp.c.createBuiltinCall("@intCast", 2); - cast_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - const builtin_node = try rp.c.createBuiltinCall("@ptrToInt", 1); - builtin_node.params()[0] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - cast_node.params()[1] = &builtin_node.base; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &cast_node.base; + const ptr_to_int = try Node.ptr_to_int.create(c.arena, expr); + return Node.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int }); } if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) { // @intToPtr(dest_type, val) - const builtin_node = try rp.c.createBuiltinCall("@intToPtr", 2); - builtin_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &builtin_node.base; + return Node.int_to_ptr.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsFloating(src_type) and cIsFloating(dst_type)) { - const builtin_node = try rp.c.createBuiltinCall("@floatCast", 2); - builtin_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &builtin_node.base; + // @floatCast(dest_type, val) + return Node.float_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsFloating(src_type) and !cIsFloating(dst_type)) { - const builtin_node = try rp.c.createBuiltinCall("@floatToInt", 2); - builtin_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &builtin_node.base; + // @floatToInt(dest_type, val) + return Node.float_to_int.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (!cIsFloating(src_type) and cIsFloating(dst_type)) { - const builtin_node = try rp.c.createBuiltinCall("@intToFloat", 2); - builtin_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &builtin_node.base; + // @intToFloat(dest_type, val) + return Node.int_to_float.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) { // @boolToInt returns either a comptime_int or a u1 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast // instead of @as - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); - builtin_node.params()[0] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - const as_node = try rp.c.createBuiltinCall("@as", 2); - as_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - as_node.params()[1] = &builtin_node.base; - as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - return &as_node.base; + const bool_to_int = Node.bool_to_int.create(c.arena, expr); + return Node.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); } if (cIsEnum(dst_type)) { - const builtin_node = try rp.c.createBuiltinCall("@intToEnum", 2); - builtin_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = expr; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &builtin_node.base; + // @intToEnum(dest_type, val) + return Node.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsEnum(src_type) and !cIsEnum(dst_type)) { - return transEnumToInt(rp.c, expr); + // @enumToInt(val) + return Node.enum_to_int.create(c.arena, expr); } - const cast_node = try rp.c.createBuiltinCall("@as", 2); - cast_node.params()[0] = try transQualType(rp, dst_type, loc); - _ = try appendToken(rp.c, .Comma, ","); - cast_node.params()[1] = expr; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &cast_node.base; -} - -fn transEnumToInt(c: *Context, enum_expr: *ast.Node) TypeError!*ast.Node { - const builtin_node = try c.createBuiltinCall("@enumToInt", 1); - builtin_node.params()[0] = enum_expr; - builtin_node.rparen_token = try appendToken(c, .RParen, ")"); - return &builtin_node.base; + // @as(dest_type, val) + return Node.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } fn transExpr( @@ -1976,13 +1858,12 @@ fn transExprCoercing( } fn transInitListExprRecord( - rp: RestorePoint, + c: *Context, scope: *Scope, loc: clang.SourceLocation, expr: *const clang.InitListExpr, ty: *const clang.Type, - used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { var is_union_type = false; // Unions and Structs are both represented as RecordDecl const record_ty = ty.getAsRecordType() orelse @@ -1994,13 +1875,11 @@ fn transInitListExprRecord( const record_def = record_decl.getDefinition() orelse unreachable; - const ty_node = try transType(rp, ty, loc); + const ty_node = try transType(c, ty, loc); const init_count = expr.getNumInits(); - var field_inits = std.ArrayList(*ast.Node).init(rp.c.gpa); + var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa); defer field_inits.deinit(); - _ = try appendToken(rp.c, .LBrace, "{"); - var init_i: c_uint = 0; var it = record_def.field_begin(); const end_it = record_def.field_end(); @@ -2018,76 +1897,28 @@ fn transInitListExprRecord( // Generate the field assignment expression: // .field_name = expr - const period_tok = try appendToken(rp.c, .Period, "."); - - var raw_name = try rp.c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); + var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); if (field_decl.isAnonymousStructOrUnion()) { - const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; - raw_name = try mem.dupe(rp.c.arena, u8, name); + const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; + raw_name = try mem.dupe(c.arena, u8, name); } - const field_name_tok = try appendIdentifier(rp.c, raw_name); - _ = try appendToken(rp.c, .Equal, "="); - - const field_init_node = try rp.c.arena.create(ast.Node.FieldInitializer); - field_init_node.* = .{ - .period_token = period_tok, - .name_token = field_name_tok, - .expr = try transExpr(rp, scope, elem_expr, .used, .r_value), - }; - - try field_inits.append(&field_init_node.base); - _ = try appendToken(rp.c, .Comma, ","); + try field_inits.append(.{ + .name = raw_name, + .value = try transExpr(c, scope, elem_expr, .used, .r_value), + }); } - const node = try ast.Node.StructInitializer.alloc(rp.c.arena, field_inits.items.len); - node.* = .{ - .lhs = ty_node, - .rtoken = try appendToken(rp.c, .RBrace, "}"), - .list_len = field_inits.items.len, - }; - mem.copy(*ast.Node, node.list(), field_inits.items); - return &node.base; -} - -fn transCreateNodeArrayType( - rp: RestorePoint, - source_loc: clang.SourceLocation, - ty: *const clang.Type, - len: anytype, -) !*ast.Node { - const node = try rp.c.arena.create(ast.Node.ArrayType); - const op_token = try appendToken(rp.c, .LBracket, "["); - const len_expr = try transCreateNodeInt(rp.c, len); - _ = try appendToken(rp.c, .RBracket, "]"); - node.* = .{ - .op_token = op_token, - .rhs = try transType(rp, ty, source_loc), - .len_expr = len_expr, - }; - return &node.base; -} - -fn transCreateEmptyArray(rp: RestorePoint, loc: clang.SourceLocation, ty: *const clang.Type) TransError!*ast.Node { - const ty_node = try transCreateNodeArrayType(rp, loc, ty, 0); - _ = try appendToken(rp.c, .LBrace, "{"); - const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 0); - filler_init_node.* = .{ - .lhs = ty_node, - .rtoken = try appendToken(rp.c, .RBrace, "}"), - .list_len = 0, - }; - return &filler_init_node.base; + return Node.container_init.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items)); } fn transInitListExprArray( - rp: RestorePoint, + c: *Context, scope: *Scope, loc: clang.SourceLocation, expr: *const clang.InitListExpr, ty: *const clang.Type, - used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const arr_type = ty.getAsArrayTypeUnsafe(); const child_qt = arr_type.getElementType(); const init_count = expr.getNumInits(); @@ -2098,111 +1929,67 @@ fn transInitListExprArray( const leftover_count = all_count - init_count; if (all_count == 0) { - return transCreateEmptyArray(rp, loc, child_qt.getTypePtr()); + return Node.empty_array.create(c.arena, try transQualType(c, child_qt, source_loc)); } + + const ty_node = try transType(ty); + const init_node = if (init_count != 0) blk: { + const init_list = try c.arena.alloc(Node, init_count); - var init_node: *ast.Node.ArrayInitializer = undefined; - var cat_tok: ast.TokenIndex = undefined; - if (init_count != 0) { - const ty_node = try transCreateNodeArrayType( - rp, - loc, - child_qt.getTypePtr(), - init_count, - ); - _ = try appendToken(rp.c, .LBrace, "{"); - init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, init_count); - init_node.* = .{ - .lhs = ty_node, - .rtoken = undefined, - .list_len = init_count, - }; - const init_list = init_node.list(); - - var i: c_uint = 0; - while (i < init_count) : (i += 1) { + for (init_list) |*init, i| { const elem_expr = expr.getInit(i); - init_list[i] = try transExpr(rp, scope, elem_expr, .used, .r_value); - _ = try appendToken(rp.c, .Comma, ","); + init.* = try transExpr(c, scope, elem_expr, .used, .r_value); } - init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); + const init_node = try Node.array_init.create(c.arena, init_list); if (leftover_count == 0) { - return &init_node.base; + return init_node; } - cat_tok = try appendToken(rp.c, .PlusPlus, "++"); - } + break :blk init_node; + } else null; - const ty_node = try transCreateNodeArrayType(rp, loc, child_qt.getTypePtr(), 1); - _ = try appendToken(rp.c, .LBrace, "{"); - const filler_init_node = try ast.Node.ArrayInitializer.alloc(rp.c.arena, 1); - filler_init_node.* = .{ - .lhs = ty_node, - .rtoken = undefined, - .list_len = 1, - }; const filler_val_expr = expr.getArrayFiller(); - filler_init_node.list()[0] = try transExpr(rp, scope, filler_val_expr, .used, .r_value); - filler_init_node.rtoken = try appendToken(rp.c, .RBrace, "}"); + const filler_node = try Node.array_filler.create(c.arena, .{ + .type = ty_node, + .filler = try transExpr(c, scope, filler_val_expr, .used, .r_value), + .count = leftover_count, + }); - const rhs_node = if (leftover_count == 1) - &filler_init_node.base - else blk: { - const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**"); - const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - mul_node.* = .{ - .base = .{ .tag = .ArrayMult }, - .op_token = mul_tok, - .lhs = &filler_init_node.base, - .rhs = try transCreateNodeInt(rp.c, leftover_count), - }; - break :blk &mul_node.base; - }; - - if (init_count == 0) { - return rhs_node; + if (init_node) |some| { + return Node.array_cat.create(c.arena, some, filler_node); + } else { + return filler_node; } - - const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - cat_node.* = .{ - .base = .{ .tag = .ArrayCat }, - .op_token = cat_tok, - .lhs = &init_node.base, - .rhs = rhs_node, - }; - return &cat_node.base; } fn transInitListExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.InitListExpr, used: ResultUsed, -) TransError!*ast.Node { - const qt = getExprQualType(rp.c, @ptrCast(*const clang.Expr, expr)); +) TransError!Node { + const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); var qual_type = qt.getTypePtr(); const source_loc = @ptrCast(*const clang.Expr, expr).getBeginLoc(); if (qual_type.isRecordType()) { - return transInitListExprRecord( + return maybeSuppressResult(c, scope, used, try transInitListExprRecord( rp, scope, source_loc, expr, qual_type, - used, - ); + )); } else if (qual_type.isArrayType()) { - return transInitListExprArray( + return maybeSuppressResult(c, scope, used, try transInitListExprArray( rp, scope, source_loc, expr, qual_type, - used, - ); + )); } else { - const type_name = rp.c.str(qual_type.getTypeClassName()); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); + const type_name = c.str(qual_type.getTypeClassName()); + return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); } } @@ -2259,15 +2046,15 @@ fn transZeroInitExpr( } fn transImplicitValueInitExpr( - rp: RestorePoint, + c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const source_loc = expr.getBeginLoc(); - const qt = getExprQualType(rp.c, expr); + const qt = getExprQualType(c, expr); const ty = qt.getTypePtr(); - return transZeroInitExpr(rp, scope, source_loc, ty); + return transZeroInitExpr(c, scope, source_loc, ty); } fn transIfStmt( diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index b926efdbef..8cf6cf74d5 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -104,6 +104,7 @@ pub const Node = extern union { bit_and, bit_or, bit_xor, + array_cat, log2_int_type, /// @import("std").math.Log2Int(operand) @@ -118,6 +119,24 @@ pub const Node = extern union { bool_to_int, /// @as(lhs, rhs) as, + /// @truncate(lhs, rhs) + truncate, + /// @bitCast(lhs, rhs) + bit_cast, + /// @floatCast(lhs, rhs) + float_cast, + /// @floatToInt(lhs, rhs) + float_to_int, + /// @intToFloat(lhs, rhs) + int_to_float, + /// @intToEnum(lhs, rhs) + int_to_enum, + /// @enumToInt(operand) + enum_to_int, + /// @intToPtr(lhs, rhs) + int_to_ptr, + /// @ptrToInt(operand) + ptr_to_int, negate, negate_wrap, @@ -154,6 +173,11 @@ pub const Node = extern union { /// pub const enum_field_name = @enumToInt(enum_name.field_name); enum_redecl, + /// [0]type{} + empty_array, + /// [1]type{val} ** count + array_filler, + pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -184,6 +208,9 @@ pub const Node = extern union { .optional_type, .address_of, .unwrap_deref, + .ptr_to_int, + .enum_to_int, + .empty_array, => Payload.UnOp, .add, @@ -239,6 +266,14 @@ pub const Node = extern union { .int_cast, .bool_to_int, .as, + .truncate, + .bit_cast, + .float_cast, + .float_to_int, + .int_to_float, + .int_to_enum, + .int_to_ptr, + .array_cat, => Payload.BinOp, .int, @@ -274,6 +309,7 @@ pub const Node = extern union { .log2_int_type => Payload.Log2IntType, .typedef, .pub_typedef, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, + .array_filler => Payload.ArrayFiller, }; } @@ -533,6 +569,15 @@ pub const Payload = struct { enum_name: []const u8, }, }; + + pub const ArrayFiller = struct { + base: Node, + data: struct { + type: Node, + filler: Node, + count: usize, + }, + }; }; /// Converts the nodes into a Zig ast. From 66dd64ec15d787bb43cb0ab1169e8bac6cfe762e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 Feb 2021 22:30:19 +0200 Subject: [PATCH 11/36] translate-c: convert most control flow --- src/translate_c.zig | 123 ++++++++++++++++------------------------ src/translate_c/ast.zig | 7 +++ 2 files changed, 56 insertions(+), 74 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 0bef2cb843..e256d9813f 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -107,6 +107,7 @@ const Scope = struct { // do while, we want to put `if (cond) break;` at the end. const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop); const stmts = try c.arena.alloc(Node, alloc_len); + stmts.len -= 1; mem.copy(Node, stmts, self.statements.items); return Node.block.create(c.arena, .{ .lable = self.label, @@ -1748,7 +1749,7 @@ fn transCCast( if (dst_type.eq(src_type)) return expr; if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) return transCPtrCast(c, loc, dst_type, src_type, expr); - + const dst_node = try transQualType(c, dst_type, loc); if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) { // 1. If src_type is an enum, determine the underlying signed int type @@ -1931,7 +1932,7 @@ fn transInitListExprArray( if (all_count == 0) { return Node.empty_array.create(c.arena, try transQualType(c, child_qt, source_loc)); } - + const ty_node = try transType(ty); const init_node = if (init_count != 0) blk: { const init_list = try c.arena.alloc(Node, init_count); @@ -2058,14 +2059,12 @@ fn transImplicitValueInitExpr( } fn transIfStmt( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.IfStmt, -) TransError!*ast.Node { +) TransError!Node { // if (c) t // if (c) t else e - const if_node = try transCreateNodeIf(rp.c); - var cond_scope = Scope.Condition{ .base = .{ .parent = scope, @@ -2074,26 +2073,21 @@ fn transIfStmt( }; defer cond_scope.deinit(); const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); - if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); - _ = try appendToken(rp.c, .RParen, ")"); + const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used, .r_value); - if_node.body = try transStmt(rp, scope, stmt.getThen(), .unused, .r_value); - - if (stmt.getElse()) |expr| { - if_node.@"else" = try transCreateNodeElse(rp.c); - if_node.@"else".?.body = try transStmt(rp, scope, expr, .unused, .r_value); - } - _ = try appendToken(rp.c, .Semicolon, ";"); - return &if_node.base; + const then_body = try transStmt(c, scope, stmt.getThen(), .unused, .r_value); + const else_body = if (stmt.getElse()) |expr| + try transStmt(c, scope, expr, .unused, .r_value) + else + null; + return Node.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); } fn transWhileLoop( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.WhileStmt, -) TransError!*ast.Node { - const while_node = try transCreateNodeWhile(rp.c); - +) TransError!Node { var cond_scope = Scope.Condition{ .base = .{ .parent = scope, @@ -2102,35 +2096,28 @@ fn transWhileLoop( }; defer cond_scope.deinit(); const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); - while_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); - _ = try appendToken(rp.c, .RParen, ")"); + const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used, .r_value); var loop_scope = Scope{ .parent = scope, .id = .Loop, }; - while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); - _ = try appendToken(rp.c, .Semicolon, ";"); - return &while_node.base; + const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); + return Node.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); } fn transDoWhileLoop( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.DoStmt, -) TransError!*ast.Node { - const while_node = try transCreateNodeWhile(rp.c); - - while_node.condition = try transCreateNodeBoolLiteral(rp.c, true); - _ = try appendToken(rp.c, .RParen, ")"); - var new = false; +) TransError!Node { var loop_scope = Scope{ .parent = scope, .id = .Loop, }; // if (!cond) break; - const if_node = try transCreateNodeIf(rp.c); + const if_node = try transCreateNodeIf(c); var cond_scope = Scope.Condition{ .base = .{ .parent = scope, @@ -2138,12 +2125,8 @@ fn transDoWhileLoop( }, }; defer cond_scope.deinit(); - const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); - prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used, .r_value, true); - _ = try appendToken(rp.c, .RParen, ")"); - if_node.condition = &prefix_op.base; - if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base; - _ = try appendToken(rp.c, .Semicolon, ";"); + const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used, .r_value); + const if_not_break = try Node.if_not_break.create(c.arena, cond); const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. @@ -2156,8 +2139,12 @@ fn transDoWhileLoop( // zig: b; // zig: if (!cond) break; // zig: } - const node = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); - break :blk node.castTag(.Block).?; + const node = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); + const block = node.castTag(.block); + block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete. + block.data.stmts[block.data.stmts.len - 1] = if_not_break; + break :blk node; + } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do @@ -2167,27 +2154,19 @@ fn transDoWhileLoop( // zig: a; // zig: if (!cond) break; // zig: } - new = true; - const block = try rp.c.createBlock(2); - block.statements_len = 1; // over-allocated so we can add another below - block.statements()[0] = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); - break :blk block; + const statements = try c.arena.create(Node, 2); + statements[0] = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); + statements[1] = if_not_break; + break :blk try Node.block.create(c.arena, .{ .label = null, .stmts = statements }); }; - - // In both cases above, we reserved 1 extra statement. - body_node.statements_len += 1; - body_node.statements()[body_node.statements_len - 1] = &if_node.base; - if (new) - body_node.rbrace = try appendToken(rp.c, .RBrace, "}"); - while_node.body = &body_node.base; - return &while_node.base; + return Node.while_true.create(c.arena, body_node); } fn transForLoop( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.ForStmt, -) TransError!*ast.Node { +) TransError!Node { var loop_scope = Scope{ .parent = scope, .id = .Loop, @@ -2197,9 +2176,9 @@ fn transForLoop( defer if (block_scope) |*bs| bs.deinit(); if (stmt.getInit()) |init| { - block_scope = try Scope.Block.init(rp.c, scope, false); + block_scope = try Scope.Block.init(c, scope, false); loop_scope.parent = &block_scope.?.base; - const init_node = try transStmt(rp, &block_scope.?.base, init, .unused, .r_value); + const init_node = try transStmt(c, &block_scope.?.base, init, .unused, .r_value); try block_scope.?.statements.append(init_node); } var cond_scope = Scope.Condition{ @@ -2210,27 +2189,23 @@ fn transForLoop( }; defer cond_scope.deinit(); - const while_node = try transCreateNodeWhile(rp.c); - while_node.condition = if (stmt.getCond()) |cond| - try transBoolExpr(rp, &cond_scope.base, cond, .used, .r_value, false) + const cond = if (stmt.getCond()) |cond| + try transBoolExpr(c, &cond_scope.base, cond, .used, .r_value) else - try transCreateNodeBoolLiteral(rp.c, true); - _ = try appendToken(rp.c, .RParen, ")"); + Node.true_literal.init(); - if (stmt.getInc()) |incr| { - _ = try appendToken(rp.c, .Colon, ":"); - _ = try appendToken(rp.c, .LParen, "("); - while_node.continue_expr = try transExpr(rp, &cond_scope.base, incr, .unused, .r_value); - _ = try appendToken(rp.c, .RParen, ")"); - } + const cont_expr = if (stmt.getInc()) |incr| + try transExpr(c, &cond_scope.base, incr, .unused, .r_value) + else + null; - while_node.body = try transStmt(rp, &loop_scope, stmt.getBody(), .unused, .r_value); + const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); + const while_node = try Node.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); if (block_scope) |*bs| { - try bs.statements.append(&while_node.base); - return try bs.complete(rp.c); + try bs.statements.append(while_node); + return try bs.complete(c); } else { - _ = try appendToken(rp.c, .Semicolon, ";"); - return &while_node.base; + return while_node; } } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 8cf6cf74d5..c59bcd09e4 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -29,7 +29,11 @@ pub const Node = extern union { char_literal, identifier, @"if", + /// if (!operand) break; + if_not_break, @"while", + /// while (true) operand + while_true, @"switch", @"continue", @"break", @@ -211,6 +215,8 @@ pub const Node = extern union { .ptr_to_int, .enum_to_int, .empty_array, + .while_true, + .if_not_break, => Payload.UnOp, .add, @@ -399,6 +405,7 @@ pub const Payload = struct { data: struct { cond: Node, body: Node, + cont_expr: ?Node }, }; From cadd4483be80f73f192621f79eea41117183129c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 Feb 2021 23:02:38 +0200 Subject: [PATCH 12/36] translate-c: convert switch --- src/translate_c.zig | 280 ++++++++++++++-------------------------- src/translate_c/ast.zig | 31 +++-- 2 files changed, 117 insertions(+), 194 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index e256d9813f..e5c755c1a4 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -43,7 +43,7 @@ const Scope = struct { const Switch = struct { base: Scope, pending_block: Block, - cases: []Node, + cases: std.ArrayList(Node), case_index: usize, switch_label: ?[]const u8, default_label: ?[]const u8, @@ -1087,7 +1087,7 @@ fn transStmt( return maybeSuppressResult(c, scope, result_used, expr); }, else => { - return revertAndWarn( + return fail( rp, error.UnsupportedTranslation, stmt.getBeginLoc(), @@ -1348,7 +1348,7 @@ fn transDeclStmtOne( return error.UnsupportedTranslation; return node; }, - else => |kind| return revertAndWarn( + else => |kind| return fail( rp, error.UnsupportedTranslation, decl.getLocation(), @@ -1440,7 +1440,7 @@ fn transImplicitCastExpr( .BuiltinFnToFnPtr => { return transExpr(rp, scope, sub_expr, result_used, .r_value); }, - else => |kind| return revertAndWarn( + else => |kind| return fail( rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, expr).getBeginLoc(), @@ -1460,7 +1460,7 @@ fn transBoolExpr( if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) { var is_zero: bool = undefined; if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) { - return revertAndWarn(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); + return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); } return Node{ .tag = ([2]ast.Node.Tag{ .true_literal, .false_literal })[@boolToInt(is_zero)] }; } @@ -1605,7 +1605,7 @@ fn transIntegerLiteral( var eval_result: clang.ExprEvalResult = undefined; if (!expr.EvaluateAsInt(&eval_result, c.clang_context)) { const loc = expr.getBeginLoc(); - return revertAndWarn(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); + return fail(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{}); } if (suppress_as == .no_as) { @@ -2144,7 +2144,6 @@ fn transDoWhileLoop( block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete. block.data.stmts[block.data.stmts.len - 1] = if_not_break; break :blk node; - } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do @@ -2209,27 +2208,11 @@ fn transForLoop( } } -fn getSwitchCaseCount(stmt: *const clang.SwitchStmt) usize { - const body = stmt.getBody(); - assert(body.getStmtClass() == .CompoundStmtClass); - const comp = @ptrCast(*const clang.CompoundStmt, body); - // TODO https://github.com/ziglang/zig/issues/1738 - // return comp.body_end() - comp.body_begin(); - const start_addr = @ptrToInt(comp.body_begin()); - const end_addr = @ptrToInt(comp.body_end()); - return (end_addr - start_addr) / @sizeOf(*clang.Stmt); -} - fn transSwitch( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.SwitchStmt, -) TransError!*ast.Node { - const switch_tok = try appendToken(rp.c, .Keyword_switch, "switch"); - _ = try appendToken(rp.c, .LParen, "("); - - const cases_len = getSwitchCaseCount(stmt); - +) TransError!Node { var cond_scope = Scope.Condition{ .base = .{ .parent = scope, @@ -2237,16 +2220,13 @@ fn transSwitch( }, }; defer cond_scope.deinit(); - const switch_expr = try transExpr(rp, &cond_scope.base, stmt.getCond(), .used, .r_value); - _ = try appendToken(rp.c, .RParen, ")"); - _ = try appendToken(rp.c, .LBrace, "{"); - // reserve +1 case in case there is no default case - const switch_node = try ast.Node.Switch.alloc(rp.c.arena, cases_len + 1); + const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used, .r_value); + const switch_node = try c.arena.create(ast.Payload.Switch); switch_node.* = .{ - .switch_token = switch_tok, - .expr = switch_expr, - .cases_len = cases_len + 1, - .rbrace = try appendToken(rp.c, .RBrace, "}"), + .data = .{ + .cond = switch_expr, + .cases = undefined, // set later + }, }; var switch_scope = Scope.Switch{ @@ -2254,29 +2234,32 @@ fn transSwitch( .id = .Switch, .parent = scope, }, - .cases = switch_node.cases(), - .case_index = 0, + .cases = std.ArrayList(Node).init(c.gpa), .pending_block = undefined, .default_label = null, .switch_label = null, }; + defer { + switch_node.data.cases = try c.arena.dupe(Node, switch_scope.cases.items); + switch_node.data.default = switch_scope.switch_label; + switch_scope.cases.deinit(); + } // tmp block that all statements will go before being picked up by a case or default - var block_scope = try Scope.Block.init(rp.c, &switch_scope.base, false); + var block_scope = try Scope.Block.init(c, &switch_scope.base, false); defer block_scope.deinit(); // Note that we do not defer a deinit here; the switch_scope.pending_block field // has its own memory management. This resource is freed inside `transCase` and // then the final pending_block is freed at the bottom of this function with // pending_block.deinit(). - switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); - try switch_scope.pending_block.statements.append(&switch_node.base); + switch_scope.pending_block = try Scope.Block.init(c, scope, false); + try switch_scope.pending_block.statements.append(Node.initPayload(&switch_node.base)); - const last = try transStmt(rp, &block_scope.base, stmt.getBody(), .unused, .r_value); - _ = try appendToken(rp.c, .Semicolon, ";"); + const last = try transStmt(c, &block_scope.base, stmt.getBody(), .unused, .r_value); // take all pending statements - const last_block_stmts = last.cast(ast.Node.Block).?.statements(); + const last_block_stmts = last.castTag(.block).?.data.stmts; try switch_scope.pending_block.statements.ensureCapacity( switch_scope.pending_block.statements.items.len + last_block_stmts.len, ); @@ -2285,213 +2268,159 @@ fn transSwitch( } if (switch_scope.default_label == null) { - switch_scope.switch_label = try block_scope.makeMangledName(rp.c, "switch"); + switch_scope.switch_label = try block_scope.makeMangledName(c, "switch"); } if (switch_scope.switch_label) |l| { - switch_scope.pending_block.label = try appendIdentifier(rp.c, l); - _ = try appendToken(rp.c, .Colon, ":"); + switch_scope.pending_block.label = l; } if (switch_scope.default_label == null) { - const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); - else_prong.expr = blk: { - var br = try CtrlFlow.init(rp.c, .Break, switch_scope.switch_label.?); - break :blk &(try br.finish(null)).base; - }; - _ = try appendToken(rp.c, .Comma, ","); - - if (switch_scope.case_index >= switch_scope.cases.len) - return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{}); - switch_scope.cases[switch_scope.case_index] = &else_prong.base; - switch_scope.case_index += 1; + const else_prong = try Node.switch_else.create( + c.arena, + try Node.@"break".create(c.arena, switch_scope.switch_label.?), + ); + switch_scope.cases.append(else_prong); } - // We overallocated in case there was no default, so now we correct - // the number of cases in the AST node. - switch_node.cases_len = switch_scope.case_index; - const result_node = try switch_scope.pending_block.complete(rp.c); + const result_node = try switch_scope.pending_block.complete(c); switch_scope.pending_block.deinit(); return result_node; } fn transCase( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.CaseStmt, -) TransError!*ast.Node { - const block_scope = scope.findBlockScope(rp.c) catch unreachable; +) TransError!Node { + const block_scope = scope.findBlockScope(c) catch unreachable; const switch_scope = scope.getSwitch(); - const label = try block_scope.makeMangledName(rp.c, "case"); - _ = try appendToken(rp.c, .Semicolon, ";"); + const label = try block_scope.makeMangledName(c, "case"); const expr = if (stmt.getRHS()) |rhs| blk: { - const lhs_node = try transExpr(rp, scope, stmt.getLHS(), .used, .r_value); - const ellips = try appendToken(rp.c, .Ellipsis3, "..."); - const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + const lhs_node = try transExpr(c, scope, stmt.getLHS(), .used, .r_value); + const rhs_node = try transExpr(c, scope, rhs, .used, .r_value); - const node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - node.* = .{ - .base = .{ .tag = .Range }, - .op_token = ellips, - .lhs = lhs_node, - .rhs = rhs_node, - }; - break :blk &node.base; + break :blk Node.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); } else - try transExpr(rp, scope, stmt.getLHS(), .used, .r_value); + try transExpr(c, scope, stmt.getLHS(), .used, .r_value); - const switch_prong = try transCreateNodeSwitchCase(rp.c, expr); - switch_prong.expr = blk: { - var br = try CtrlFlow.init(rp.c, .Break, label); - break :blk &(try br.finish(null)).base; - }; - _ = try appendToken(rp.c, .Comma, ","); + const switch_prong = try Node.switch_prong.create( + c.arena, + try Node.@"break".create(c.arena, label), + ); + switch_scope.cases.append(switch_prong); - if (switch_scope.case_index >= switch_scope.cases.len) - return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{}); - switch_scope.cases[switch_scope.case_index] = &switch_prong.base; - switch_scope.case_index += 1; - - switch_scope.pending_block.label = try appendIdentifier(rp.c, label); - _ = try appendToken(rp.c, .Colon, ":"); + switch_scope.pending_block.label = label; // take all pending statements try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); block_scope.statements.shrinkAndFree(0); - const pending_node = try switch_scope.pending_block.complete(rp.c); + const pending_node = try switch_scope.pending_block.complete(c); switch_scope.pending_block.deinit(); - switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); + switch_scope.pending_block = try Scope.Block.init(c, scope, false); try switch_scope.pending_block.statements.append(pending_node); - return transStmt(rp, scope, stmt.getSubStmt(), .unused, .r_value); + return transStmt(c, scope, stmt.getSubStmt(), .unused, .r_value); } fn transDefault( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.DefaultStmt, -) TransError!*ast.Node { - const block_scope = scope.findBlockScope(rp.c) catch unreachable; +) TransError!Node { + const block_scope = scope.findBlockScope(c) catch unreachable; const switch_scope = scope.getSwitch(); - switch_scope.default_label = try block_scope.makeMangledName(rp.c, "default"); - _ = try appendToken(rp.c, .Semicolon, ";"); + switch_scope.default_label = try block_scope.makeMangledName(c, "default"); - const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c)); - else_prong.expr = blk: { - var br = try CtrlFlow.init(rp.c, .Break, switch_scope.default_label.?); - break :blk &(try br.finish(null)).base; - }; - _ = try appendToken(rp.c, .Comma, ","); - - if (switch_scope.case_index >= switch_scope.cases.len) - return revertAndWarn(rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), "TODO complex switch cases", .{}); - switch_scope.cases[switch_scope.case_index] = &else_prong.base; - switch_scope.case_index += 1; - - switch_scope.pending_block.label = try appendIdentifier(rp.c, switch_scope.default_label.?); - _ = try appendToken(rp.c, .Colon, ":"); + const else_prong = try Node.switch_else.create( + c.arena, + try Node.@"break".create(c.arena, switch_scope.default_label.?), + ); + switch_scope.cases.append(else_prong); + switch_scope.pending_block.label = try appendIdentifier(c, switch_scope.default_label.?); // take all pending statements try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); block_scope.statements.shrinkAndFree(0); - const pending_node = try switch_scope.pending_block.complete(rp.c); + const pending_node = try switch_scope.pending_block.complete(c); switch_scope.pending_block.deinit(); - switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); + switch_scope.pending_block = try Scope.Block.init(c, scope, false); try switch_scope.pending_block.statements.append(pending_node); - return transStmt(rp, scope, stmt.getSubStmt(), .unused, .r_value); + return transStmt(c, scope, stmt.getSubStmt(), .unused, .r_value); } -fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!*ast.Node { +fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { var result: clang.ExprEvalResult = undefined; - if (!expr.EvaluateAsConstantExpr(&result, .EvaluateForCodeGen, rp.c.clang_context)) - return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{}); + if (!expr.EvaluateAsConstantExpr(&result, .EvaluateForCodeGen, c.clang_context)) + return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{}); - var val_node: ?*ast.Node = null; switch (result.Val.getKind()) { .Int => { // See comment in `transIntegerLiteral` for why this code is here. // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, expr); - const as_node = try rp.c.createBuiltinCall("@as", 2); - const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc()); - as_node.params()[0] = ty_node; - _ = try appendToken(rp.c, .Comma, ","); - - const int_lit_node = try transCreateNodeAPInt(rp.c, result.Val.getInt()); - as_node.params()[1] = int_lit_node; - - as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - return maybeSuppressResult(rp, scope, used, &as_node.base); + const as_node = try Node.as.create(c.arena, .{ + .lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()), + .rhs = try transCreateNodeAPInt(c, result.Val.getInt()), + }); + return maybeSuppressResult(c, scope, used, as_node); }, else => { - return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{}); + return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind", .{}); }, } } -fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!*ast.Node { - return transStringLiteral(rp, scope, expr.getFunctionName(), used); +fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!Node { + return transStringLiteral(c, scope, expr.getFunctionName(), used); } -fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!*ast.Node { - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .CharLiteral }, - .token = undefined, - }; - if (narrow) { - const val_array = [_]u8{@intCast(u8, val)}; - node.token = try appendTokenFmt(c, .CharLiteral, "'{}'", .{std.zig.fmtEscapes(&val_array)}); - } else { - node.token = try appendTokenFmt(c, .CharLiteral, "'\\u{{{x}}}'", .{val}); - } - return &node.base; +fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { + return Node.char_literal.create(c.arena, if (narrow) + try std.fmt.bufPrint(c.arena, "'{}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})}) + else + try std.fmt.bufPrint(c.arena, "'\\u{{{x}}}'", .{val})); } fn transCharLiteral( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.CharacterLiteral, result_used: ResultUsed, suppress_as: SuppressCast, -) TransError!*ast.Node { +) TransError!Node { const kind = stmt.getKind(); const val = stmt.getValue(); const narrow = kind == .Ascii or kind == .UTF8; // C has a somewhat obscure feature called multi-character character constant // e.g. 'abcd' const int_lit_node = if (kind == .Ascii and val > 255) - try transCreateNodeInt(rp.c, val) + try transCreateNodeInt(c, val) else - try transCreateCharLitNode(rp.c, narrow, val); + try transCreateCharLitNode(c, narrow, val); if (suppress_as == .no_as) { - return maybeSuppressResult(rp, scope, result_used, int_lit_node); + return maybeSuppressResult(c, scope, result_used, int_lit_node); } // See comment in `transIntegerLiteral` for why this code is here. // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, stmt); - const as_node = try rp.c.createBuiltinCall("@as", 2); - const ty_node = try transQualType(rp, expr_base.getType(), expr_base.getBeginLoc()); - as_node.params()[0] = ty_node; - _ = try appendToken(rp.c, .Comma, ","); - as_node.params()[1] = int_lit_node; - - as_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return maybeSuppressResult(rp, scope, result_used, &as_node.base); + const as_node = Node.as.create(c.arena, .{ + .lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()), + .rhs = int_lit_node, + }); + return maybeSuppressResult(c, scope, result_used, as_node); } -fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!*ast.Node { +fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!Node { const comp = stmt.getSubStmt(); if (used == .unused) { - return transCompoundStmt(rp, scope, comp); + return transCompoundStmt(c, scope, comp); } - const lparen = try appendToken(rp.c, .LParen, "("); - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); var it = comp.body_begin(); @@ -2500,22 +2429,13 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.StmtExpr, u const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value); try block_scope.statements.append(result); } - const break_node = blk: { - var tmp = try CtrlFlow.init(rp.c, .Break, "blk"); - const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value); - break :blk try tmp.finish(rhs); - }; - _ = try appendToken(rp.c, .Semicolon, ";"); - 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, used, &grouped_expr.base); + const break_node = try Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = try transStmt(c, &block_scope.base, it[0], .used, .r_value), + }); + try block_scope.statements.append(break_node); + + return block_scope.complete(c); } fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!*ast.Node { @@ -4581,7 +4501,7 @@ fn fail( comptime format: []const u8, args: anytype, ) (@TypeOf(err) || error{OutOfMemory}) { - try emitWarning(c, source_loc, format, args); + try warn(c, source_loc, format, args); return err; } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index c59bcd09e4..a163950d2b 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -35,8 +35,13 @@ pub const Node = extern union { /// while (true) operand while_true, @"switch", + /// else => operand, + switch_else, + /// lhs => rhs, + switch_prong, @"continue", @"break", + break_val, @"return", field_access, field_access_arrow, @@ -109,6 +114,7 @@ pub const Node = extern union { bit_or, bit_xor, array_cat, + ellipsis3, log2_int_type, /// @import("std").math.Log2Int(operand) @@ -217,6 +223,7 @@ pub const Node = extern union { .empty_array, .while_true, .if_not_break, + .switch_else, => Payload.UnOp, .add, @@ -280,6 +287,8 @@ pub const Node = extern union { .int_to_enum, .int_to_ptr, .array_cat, + .ellipsis3, + .switch_prong, => Payload.BinOp, .int, @@ -300,6 +309,7 @@ pub const Node = extern union { .@"while" => Payload.While, .@"switch" => Payload.Switch, .@"break" => Payload.Break, + .break_val => Payload.BreakVal, .call => Payload.Call, .var_decl => Payload.VarDecl, .func => Payload.Func, @@ -413,22 +423,20 @@ pub const Payload = struct { base: Node = .{ .tag = .@"switch" }, data: struct { cond: Node, - cases: []Prong, - default: ?[]const u8, - - pub const Prong = struct { - lhs: Node, - rhs: ?Node, - label: []const u8, - }; + cases: []Node, }, }; pub const Break = struct { base: Node = .{ .tag = .@"break" }, + data: ?[]const u8, + }; + + pub const BreakVal = struct { + base: Node = .{ .tag = .break_val }, data: struct { label: ?[]const u8, - rhs: ?Node, + val: Node, }, }; @@ -525,11 +533,6 @@ pub const Payload = struct { }, }; - pub const Break = struct { - base: Node = .{ .tag = .@"break" }, - data: *Block - }; - pub const Array = struct { base: Node, data: struct { From 450b718b9ec21b328966469c851983ee8969e577 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 10 Feb 2021 23:38:45 +0200 Subject: [PATCH 13/36] translate-c: convert field/array access, call, pre/postcrement --- src/translate_c.zig | 479 ++++++++++------------------------------ src/translate_c/ast.zig | 22 +- 2 files changed, 129 insertions(+), 372 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index e5c755c1a4..1a98979317 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -2438,11 +2438,11 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: return block_scope.complete(c); } -fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!*ast.Node { - var container_node = try transExpr(rp, scope, stmt.getBase(), .used, .r_value); +fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node { + var container_node = try transExpr(c, scope, stmt.getBase(), .used, .r_value); if (stmt.isArrow()) { - container_node = try transCreateNodePtrDeref(rp.c, container_node); + container_node = try Node.deref.create(c.arena, container_node); } const member_decl = stmt.getMemberDecl(); @@ -2453,19 +2453,19 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp if (decl_kind == .Field) { const field_decl = @ptrCast(*const clang.FieldDecl, member_decl); if (field_decl.isAnonymousStructOrUnion()) { - const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; - break :blk try mem.dupe(rp.c.arena, u8, name); + const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?; + break :blk try mem.dupe(c.arena, u8, name); } } const decl = @ptrCast(*const clang.NamedDecl, member_decl); - break :blk try rp.c.str(decl.getName_bytes_begin()); + break :blk try c.str(decl.getName_bytes_begin()); }; - const node = try transCreateNodeFieldAccess(rp.c, container_node, name); - return maybeSuppressResult(rp, scope, result_used, node); + const node = try Node.field_access.create(c.arena, .{ .container = container_node, .name = name}); + return maybeSuppressResult(c, scope, result_used, node); } -fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node { +fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node { var base_stmt = stmt.getBase(); // Unwrap the base statement if it's an array decayed to a bare pointer type @@ -2478,30 +2478,23 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub } } - const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value); - const node = try transCreateNodeArrayAccess(rp.c, container_node); + const container_node = try transExpr(c, scope, base_stmt, .used, .r_value); // cast if the index is long long or signed const subscr_expr = stmt.getIdx(); - const qt = getExprQualType(rp.c, subscr_expr); + const qt = getExprQualType(c, subscr_expr); const is_longlong = cIsLongLongInteger(qt); const is_signed = cIsSignedInteger(qt); - if (is_longlong or is_signed) { - const cast_node = try rp.c.createBuiltinCall("@intCast", 2); + + const node = try Node.array_access.create(c.arena, .{ .lhs = container_node, .rhs = if (is_longlong or is_signed) blk: { + const cast_node = try c.createBuiltinCall("@intCast", 2); // check if long long first so that signed long long doesn't just become unsigned long long - var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false); - cast_node.params()[0] = typeid_node; - _ = try appendToken(rp.c, .Comma, ","); - cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value); - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - node.rtoken = try appendToken(rp.c, .RBrace, "]"); - node.index_expr = &cast_node.base; - } else { - node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value); - node.rtoken = try appendToken(rp.c, .RBrace, "]"); - } - return maybeSuppressResult(rp, scope, result_used, &node.base); + var typeid_node = if (is_longlong) try transCreateNodeIdentifier(c, "usize") else try transQualTypeIntWidthOf(c, qt, false); + break :blk try Node.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used, .r_value)}); + } else + try transExpr(c, scope, subscr_expr, .used, .r_value)}); + return maybeSuppressResult(c, scope, result_used, node); } /// Check if an expression is ultimately a reference to a function declaration @@ -2536,9 +2529,9 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { } } -fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!*ast.Node { +fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node { const callee = stmt.getCallee(); - var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value); + var raw_fn_expr = try transExpr(c, scope, callee, .used, .r_value); var is_ptr = false; const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr); @@ -2549,16 +2542,12 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r raw_fn_expr; const num_args = stmt.getNumArgs(); - const node = try rp.c.createCall(fn_expr, num_args); - const call_params = node.params(); + const call_params = try c.arena.alloc(Node, num_args); const args = stmt.getArgs(); var i: usize = 0; while (i < num_args) : (i += 1) { - if (i != 0) { - _ = try appendToken(rp.c, .Comma, ","); - } - var call_param = try transExpr(rp, scope, args[i], .used, .r_value); + var call_param = try transExpr(c, scope, args[i], .used, .r_value); // In C the result type of a boolean expression is int. If this result is passed as // an argument to a function whose parameter is also int, there is no cast. Therefore @@ -2570,10 +2559,7 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r if (i < param_count) { const param_qt = fn_proto.getParamType(@intCast(c_uint, i)); if (isBoolRes(call_param) and cIsNativeInt(param_qt)) { - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); - builtin_node.params()[0] = call_param; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - call_param = &builtin_node.base; + call_param = try Node.bool_to_int.create(c.arena, call_param); } } }, @@ -2582,18 +2568,16 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r } call_params[i] = call_param; } - node.rtoken = try appendToken(rp.c, .RParen, ")"); - + const node = try Node.call.create(c.arena, .{ .lhs = fn_expr, .args = call_params }); if (fn_ty) |ty| { const canon = ty.getReturnType().getCanonicalType(); const ret_ty = canon.getTypePtr(); if (ret_ty.isVoidType()) { - _ = try appendToken(rp.c, .Semicolon, ";"); - return &node.base; + return node; } } - return maybeSuppressResult(rp, scope, result_used, &node.base); + return maybeSuppressResult(c, scope, result_used, node); } const ClangFunctionType = union(enum) { @@ -2667,21 +2651,21 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat const op_expr = stmt.getSubExpr(); switch (stmt.getOpcode()) { .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePostCrement(c, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) + return transCreatePostCrement(c, scope, stmt, .assign_add_wrap, used) else - return transCreatePostCrement(c, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), + return transCreatePostCrement(c, scope, stmt, .assign_add, used), .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePostCrement(c, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) + return transCreatePostCrement(c, scope, stmt, .assign_sub_wrap, used) else - return transCreatePostCrement(c, scope, stmt, .AssignSub, .MinusEqual, "-=", used), + return transCreatePostCrement(c, scope, stmt, .assign_sub, used), .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePreCrement(c, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used) + return transCreatePreCrement(c, scope, stmt, .assign_add_wrap, used) else - return transCreatePreCrement(c, scope, stmt, .AssignAdd, .PlusEqual, "+=", used), + return transCreatePreCrement(c, scope, stmt, .assign_add, used), .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePreCrement(c, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used) + return transCreatePreCrement(c, scope, stmt, .assign_sub_wrap, used) else - return transCreatePreCrement(c, scope, stmt, .AssignSub, .MinusEqual, "-=", used), + return transCreatePreCrement(c, scope, stmt, .assign_sub, used), .AddrOf => { if (cIsFunctionDeclRef(op_expr)) { return transExpr(rp, scope, op_expr, used, .r_value); @@ -2704,7 +2688,7 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat // use -% x for unsigned integers return Node.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); } else - return revertAndWarn(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); + return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); }, .Not => { return Node.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); @@ -2715,31 +2699,32 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat .Extension => { return transExpr(c, scope, stmt.getSubExpr(), used, .l_value); }, - else => return revertAndWarn(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), + else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), } } fn transCreatePreCrement( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, - op: ast.Node.Tag, - op_tok_id: std.zig.Token.Id, - bytes: []const u8, + op: Node.Tag, used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const op_expr = stmt.getSubExpr(); if (used == .unused) { // common case // c: ++expr // zig: expr += 1 - const expr = try transExpr(rp, scope, op_expr, .used, .r_value); - const token = try appendToken(rp.c, op_tok_id, bytes); - const one = try transCreateNodeInt(rp.c, 1); - if (scope.id != .Condition) - _ = try appendToken(rp.c, .Semicolon, ";"); - return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false); + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ + .base = .{ .tag = op }, + .data = .{ + .lhs = try transExpr(c, scope, op_expr, .used, .r_value), + .rhs = Node.one_literal.init(), + } + }; + return Node.initPayload(&payload.base); } // worst case // c: ++expr @@ -2748,71 +2733,55 @@ fn transCreatePreCrement( // zig: _ref.* += 1; // zig: break :blk _ref.* // zig: }) - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); - const ref = try block_scope.makeMangledName(rp.c, "ref"); + const ref = try block_scope.makeMangledName(c, "ref"); - const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(rp.c, ref); - const eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); - rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); - const init_node = &rhs_node.base; - const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .init_node = init_node, - }); - try block_scope.statements.append(&node.base); + const expr = try transExpr(c, scope, op_expr, .used, .r_value); + const addr_of = try Node.address_of.create(c.arena, expr); + const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of}); + try block_scope.statements.append(ref_decl); - const lhs_node = try transCreateNodeIdentifier(rp.c, ref); - const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); - _ = try appendToken(rp.c, .Semicolon, ";"); - const token = try appendToken(rp.c, op_tok_id, bytes); - const one = try transCreateNodeInt(rp.c, 1); - _ = try appendToken(rp.c, .Semicolon, ";"); - const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); - try block_scope.statements.append(assign); - - const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node); - try block_scope.statements.append(&break_node.base); - const block_node = try block_scope.complete(rp.c); - // semicolon must immediately follow rbrace because it is the last token in a block - _ = try appendToken(rp.c, .Semicolon, ";"); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = try appendToken(rp.c, .LParen, "("), - .expr = block_node, - .rparen = try appendToken(rp.c, .RParen, ")"), + const lhs_node = try Node.identifier.create(c.arena, ref); + const ref_node = try Node.deref.create(c.arena, lhs_node); + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ + .base = .{ .tag = op }, + .data = .{ + .lhs = ref_node, + .rhs = Node.one_literal.init(), + } }; - return &grouped_expr.base; + try block_scope.statements.append(Node.initPayload(&payload.base)); + + return Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = ref_node, + }); } fn transCreatePostCrement( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, - op: ast.Node.Tag, - op_tok_id: std.zig.Token.Id, - bytes: []const u8, + op: Node.Tag, used: ResultUsed, -) TransError!*ast.Node { +) TransError!Node { const op_expr = stmt.getSubExpr(); if (used == .unused) { // common case // c: ++expr // zig: expr += 1 - const expr = try transExpr(rp, scope, op_expr, .used, .r_value); - const token = try appendToken(rp.c, op_tok_id, bytes); - const one = try transCreateNodeInt(rp.c, 1); - if (scope.id != .Condition) - _ = try appendToken(rp.c, .Semicolon, ";"); - return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false); + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ + .base = .{ .tag = op }, + .data = .{ + .lhs = try transExpr(c, scope, op_expr, .used, .r_value), + .rhs = Node.one_literal.init(), + } + }; + return Node.initPayload(&payload.base); } // worst case // c: expr++ @@ -2822,68 +2791,36 @@ fn transCreatePostCrement( // zig: _ref.* += 1; // zig: break :blk _tmp // zig: }) - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); - const ref = try block_scope.makeMangledName(rp.c, "ref"); + const ref = try block_scope.makeMangledName(c, "ref"); - const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(rp.c, ref); - const eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); - rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); - const init_node = &rhs_node.base; - const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .init_node = init_node, - }); - try block_scope.statements.append(&node.base); + const expr = try transExpr(c, scope, op_expr, .used, .r_value); + const addr_of = try Node.address_of.create(c.arena, expr); + const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of}); + try block_scope.statements.append(ref_decl); - const lhs_node = try transCreateNodeIdentifier(rp.c, ref); - const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); - _ = try appendToken(rp.c, .Semicolon, ";"); + const lhs_node = try Node.identifier.create(c.arena, ref); + const ref_node = try Node.deref.create(c.arena, lhs_node); - const tmp = try block_scope.makeMangledName(rp.c, "tmp"); - const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const tmp_name_tok = try appendIdentifier(rp.c, tmp); - const tmp_eq_token = try appendToken(rp.c, .Equal, "="); - const tmp_init_node = ref_node; - const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = tmp_name_tok, - .mut_token = tmp_mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = tmp_eq_token, - .init_node = tmp_init_node, - }); - try block_scope.statements.append(&tmp_node.base); + const tmp = try block_scope.makeMangledName(c, "tmp"); + const tmp_decl = try Node.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node}); + try block_scope.statements.append(tmp_decl); - const token = try appendToken(rp.c, op_tok_id, bytes); - const one = try transCreateNodeInt(rp.c, 1); - _ = try appendToken(rp.c, .Semicolon, ";"); - const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false); - try block_scope.statements.append(assign); - - const break_node = blk: { - var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label); - const rhs = try transCreateNodeIdentifier(rp.c, tmp); - break :blk try tmp_ctrl_flow.finish(rhs); + const payload = try c.arena.create(ast.Payload.BinOp); + payload.* = .{ + .base = .{ .tag = op }, + .data = .{ + .lhs = ref_node, + .rhs = Node.one_literal.init(), + } }; - try block_scope.statements.append(&break_node.base); - _ = try appendToken(rp.c, .Semicolon, ";"); - const block_node = try block_scope.complete(rp.c); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = try appendToken(rp.c, .LParen, "("), - .expr = block_node, - .rparen = try appendToken(rp.c, .RParen, ")"), - }; - return &grouped_expr.base; + try block_scope.statements.append(Node.initPayload(&payload.base)); + + return Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = try Node.identifier.create(c.arena, tmp), + }); } fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { @@ -3139,31 +3076,24 @@ fn transCPtrCast( } } -fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node { +fn transBreak(c: *Context, scope: *Scope) TransError!Node { const break_scope = scope.getBreakableScope(); const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: { const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope); - const block_scope = try scope.findBlockScope(rp.c); - swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch"); + const block_scope = try scope.findBlockScope(c); + swtch.switch_label = try block_scope.makeMangledName(c, "switch"); break :blk swtch.switch_label; } else null; - var cf = try CtrlFlow.init(rp.c, .Break, label_text); - const br = try cf.finish(null); - _ = try appendToken(rp.c, .Semicolon, ";"); - return &br.base; + return Node.@"break".create(c.arena, label_text); } -fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!*ast.Node { +fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { // TODO use something more accurate const dbl = stmt.getValueAsApproximateDouble(); - const node = try rp.c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .FloatLiteral }, - .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}), - }; - return maybeSuppressResult(rp, scope, used, &node.base); + const node = try Node.float_literal.create(c.arena, try std.fmt.allocPrint(c.arena, "{d}", .{dbl})); + return maybeSuppressResult(c, scope, used, &node.base); } fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node { @@ -3943,169 +3873,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a return &fn_proto.base; } -fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node { - _ = try appendToken(c, .Period, "."); - const qm = try appendToken(c, .QuestionMark, "?"); - const node = try c.arena.create(ast.Node.SimpleSuffixOp); - node.* = .{ - .base = .{ .tag = .UnwrapOptional }, - .lhs = wrapped, - .rtoken = qm, - }; - return &node.base; -} -fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node { - const node = try c.arena.create(ast.Node.EnumLiteral); - node.* = .{ - .dot = try appendToken(c, .Period, "."), - .name = try appendIdentifier(c, name), - }; - return &node.base; -} - -fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node { - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .StringLiteral }, - .token = try appendToken(c, .StringLiteral, str), - }; - return &node.base; -} - -fn transCreateNodeIf(c: *Context) !*ast.Node.If { - const if_tok = try appendToken(c, .Keyword_if, "if"); - _ = try appendToken(c, .LParen, "("); - const node = try c.arena.create(ast.Node.If); - node.* = .{ - .if_token = if_tok, - .condition = undefined, - .payload = null, - .body = undefined, - .@"else" = null, - }; - return node; -} - -fn transCreateNodeElse(c: *Context) !*ast.Node.Else { - const node = try c.arena.create(ast.Node.Else); - node.* = .{ - .else_token = try appendToken(c, .Keyword_else, "else"), - .payload = null, - .body = undefined, - }; - return node; -} - -fn transCreateNodeBreak( - c: *Context, - label: ?ast.TokenIndex, - rhs: ?*ast.Node, -) !*ast.Node.ControlFlowExpression { - var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null); - return ctrl_flow.finish(rhs); -} - -const CtrlFlow = struct { - c: *Context, - ltoken: ast.TokenIndex, - label_token: ?ast.TokenIndex, - tag: ast.Node.Tag, - - /// Does everything except the RHS. - fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow { - const kw: Token.Id = switch (tag) { - .Break => .Keyword_break, - .Continue => .Keyword_continue, - .Return => .Keyword_return, - else => unreachable, - }; - const kw_text = switch (tag) { - .Break => "break", - .Continue => "continue", - .Return => "return", - else => unreachable, - }; - const ltoken = try appendToken(c, kw, kw_text); - const label_token = if (label) |l| blk: { - _ = try appendToken(c, .Colon, ":"); - break :blk try appendIdentifier(c, l); - } else null; - return CtrlFlow{ - .c = c, - .ltoken = ltoken, - .label_token = label_token, - .tag = tag, - }; - } - - fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow { - const other_token = label orelse return init(c, tag, null); - const loc = c.token_locs.items[other_token]; - const label_name = c.source_buffer.items[loc.start..loc.end]; - return init(c, tag, label_name); - } - - fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression { - return ast.Node.ControlFlowExpression.create(self.c.arena, .{ - .ltoken = self.ltoken, - .tag = self.tag, - }, .{ - .label = self.label_token, - .rhs = rhs, - }); - } -}; - -fn transCreateNodeWhile(c: *Context) !*ast.Node.While { - const while_tok = try appendToken(c, .Keyword_while, "while"); - _ = try appendToken(c, .LParen, "("); - - const node = try c.arena.create(ast.Node.While); - node.* = .{ - .label = null, - .inline_token = null, - .while_token = while_tok, - .condition = undefined, - .payload = null, - .continue_expr = null, - .body = undefined, - .@"else" = null, - }; - return node; -} - -fn transCreateNodeContinue(c: *Context) !*ast.Node { - const ltoken = try appendToken(c, .Keyword_continue, "continue"); - const node = try ast.Node.ControlFlowExpression.create(c.arena, .{ - .ltoken = ltoken, - .tag = .Continue, - }, .{}); - _ = try appendToken(c, .Semicolon, ";"); - return &node.base; -} - -fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase { - const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>"); - - const node = try ast.Node.SwitchCase.alloc(c.arena, 1); - node.* = .{ - .items_len = 1, - .arrow_token = arrow_tok, - .payload = null, - .expr = undefined, - }; - node.items()[0] = lhs; - return node; -} - -fn transCreateNodeSwitchElse(c: *Context) !*ast.Node { - const node = try c.arena.create(ast.Node.SwitchElse); - node.* = .{ - .token = try appendToken(c, .Keyword_else, "else"), - }; - return &node.base; -} fn transCreateNodeShiftOp( c: *Context, @@ -4137,27 +3905,6 @@ fn transCreateNodeShiftOp( return Node.initPayload(&payload.base); } -fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node { - const node = try c.arena.create(ast.Node.SimpleSuffixOp); - node.* = .{ - .base = .{ .tag = .Deref }, - .lhs = lhs, - .rtoken = try appendToken(c, .PeriodAsterisk, ".*"), - }; - return &node.base; -} - -fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess { - _ = try appendToken(c, .LBrace, "["); - const node = try c.arena.create(ast.Node.ArrayAccess); - node.* = .{ - .lhs = lhs, - .index_expr = undefined, - .rtoken = undefined, - }; - return node; -} - fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { switch (ty.getTypeClass()) { .Builtin => { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index a163950d2b..3a7c02fd74 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -17,6 +17,7 @@ pub const Node = extern union { empty_block, return_void, zero_literal, + one_literal, void_type, noreturn_type, /// pub usingnamespace @import("std").c.builtins; @@ -44,7 +45,6 @@ pub const Node = extern union { break_val, @"return", field_access, - field_access_arrow, array_access, call, std_mem_zeroes, @@ -153,8 +153,10 @@ pub const Node = extern union { bit_not, not, address_of, - // operand.?.* + /// operand.?.* unwrap_deref, + /// .* + deref, block, @"break", @@ -202,11 +204,11 @@ pub const Node = extern union { .usingnamespace_builtins, .return_void, .zero_literal, + .one_literal, .void_type, .noreturn_type, => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), - .array_access, .std_mem_zeroes, .@"return", .discard, @@ -218,6 +220,7 @@ pub const Node = extern union { .optional_type, .address_of, .unwrap_deref, + .deref, .ptr_to_int, .enum_to_int, .empty_array, @@ -296,8 +299,6 @@ pub const Node = extern union { .string, .char, .identifier, - .field_access, - .field_access_arrow, .warning, .failed_decl, .sizeof, @@ -323,9 +324,10 @@ pub const Node = extern union { .array_type => Payload.Array, .arg_redecl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, - .typedef, .pub_typedef, .pub_var_simple => Payload.SimpleVarDecl, + .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, .array_filler => Payload.ArrayFiller, + .field_access => Payload.FieldAccess, }; } @@ -588,6 +590,14 @@ pub const Payload = struct { count: usize, }, }; + + pub const FieldAccess = struct { + base: Node, + data: struct { + container: Node, + name: []const u8, + }, + }; }; /// Converts the nodes into a Zig ast. From c30c2f7c133861a8749a9ffbc2bf6a49d9c6283e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 11 Feb 2021 12:31:48 +0200 Subject: [PATCH 14/36] translate-c: convert assignment and conditional exprs --- src/translate_c.zig | 728 ++++++++++++---------------------------- src/translate_c/ast.zig | 30 +- 2 files changed, 235 insertions(+), 523 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 1a98979317..0a20d5e6d5 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1144,18 +1144,16 @@ fn transBinaryOperator( } }, .Shl => { - const node = try transCreateNodeShiftOp(c, scope, stmt, .shl); - return maybeSuppressResult(c, scope, result_used, node); + return transCreateNodeShiftOp(c, scope, stmt, .shl, result_used); }, .Shr => { - const node = try transCreateNodeShiftOp(c, scope, stmt, .shr); - return maybeSuppressResult(c, scope, result_used, node); + return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used); }, .LAnd => { - return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_and, result_used, true); + return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_and, result_used); }, .LOr => { - return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_or, result_used, true); + return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_or, result_used); }, else => {}, } @@ -1233,15 +1231,7 @@ fn transBinaryOperator( else rhs_uncasted; - const payload = try c.arena.create(ast.Payload.BinOp); - payload.* = .{ - .base = .{ .tag = op_id }, - .data = .{ - .lhs = lhs, - .rhs = rhs, - }, - }; - return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base)); + return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, used); } fn transCompoundStmtInline( @@ -1716,7 +1706,7 @@ fn transStringLiteralAsArray( init_list[i] = try transCreateCharLitNode(c, narrow, code_unit); } while (i < array_size) : (i += 1) { - init_list[i] = try transCreateNodeInt(c, 0); + init_list[i] = try transCreateNodeNumber(c, 0); } return Node.array_init.create(c.arena, init_list); @@ -2398,7 +2388,7 @@ fn transCharLiteral( // C has a somewhat obscure feature called multi-character character constant // e.g. 'abcd' const int_lit_node = if (kind == .Ascii and val > 255) - try transCreateNodeInt(c, val) + try transCreateNodeNumber(c, val) else try transCreateCharLitNode(c, narrow, val); @@ -2434,8 +2424,8 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: .val = try transStmt(c, &block_scope.base, it[0], .used, .r_value), }); try block_scope.statements.append(break_node); - - return block_scope.complete(c); + const res = try block_scope.complete(c); + return maybeSuppressResult(c, scope, used, res); } fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node { @@ -2460,8 +2450,9 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re const decl = @ptrCast(*const clang.NamedDecl, member_decl); break :blk try c.str(decl.getName_bytes_begin()); }; + const ident = try Node.identifier.create(c.arena, name); - const node = try Node.field_access.create(c.arena, .{ .container = container_node, .name = name}); + const node = try Node.field_access.create(c.arena, .{ .lhs = container_node, .rhs = ident}); return maybeSuppressResult(c, scope, result_used, node); } @@ -2716,15 +2707,9 @@ fn transCreatePreCrement( // common case // c: ++expr // zig: expr += 1 - const payload = try c.arena.create(ast.Payload.BinOp); - payload.* = .{ - .base = .{ .tag = op }, - .data = .{ - .lhs = try transExpr(c, scope, op_expr, .used, .r_value), - .rhs = Node.one_literal.init(), - } - }; - return Node.initPayload(&payload.base); + const lhs = try transExpr(c, scope, op_expr, .used, .r_value); + const rhs = Node.one_literal.init(); + return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used); } // worst case // c: ++expr @@ -2744,20 +2729,15 @@ fn transCreatePreCrement( const lhs_node = try Node.identifier.create(c.arena, ref); const ref_node = try Node.deref.create(c.arena, lhs_node); - const payload = try c.arena.create(ast.Payload.BinOp); - payload.* = .{ - .base = .{ .tag = op }, - .data = .{ - .lhs = ref_node, - .rhs = Node.one_literal.init(), - } - }; - try block_scope.statements.append(Node.initPayload(&payload.base)); + const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Node.one_literal.init(), .used); + try block_scope.statements.append(node); - return Node.break_val.create(c.arena, .{ + const break_node = try Node.break_val.create(c.arena, .{ .label = block_scope.label, .val = ref_node, }); + try block_scope.statements.append(break_node); + return block_scope.complete(c); } fn transCreatePostCrement( @@ -2771,17 +2751,11 @@ fn transCreatePostCrement( if (used == .unused) { // common case - // c: ++expr + // c: expr++ // zig: expr += 1 - const payload = try c.arena.create(ast.Payload.BinOp); - payload.* = .{ - .base = .{ .tag = op }, - .data = .{ - .lhs = try transExpr(c, scope, op_expr, .used, .r_value), - .rhs = Node.one_literal.init(), - } - }; - return Node.initPayload(&payload.base); + const lhs = try transExpr(c, scope, op_expr, .used, .r_value); + const rhs = Node.one_literal.init(); + return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used); } // worst case // c: expr++ @@ -2807,44 +2781,39 @@ fn transCreatePostCrement( const tmp_decl = try Node.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node}); try block_scope.statements.append(tmp_decl); - const payload = try c.arena.create(ast.Payload.BinOp); - payload.* = .{ - .base = .{ .tag = op }, - .data = .{ - .lhs = ref_node, - .rhs = Node.one_literal.init(), - } - }; - try block_scope.statements.append(Node.initPayload(&payload.base)); + const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Node.one_literal.init(), .used); + try block_scope.statements.append(node); - return Node.break_val.create(c.arena, .{ + const break_node = try Node.break_val.create(c.arena, .{ .label = block_scope.label, .val = try Node.identifier.create(c.arena, tmp), }); + try block_scope.statements.append(break_node); + return block_scope.complete(c); } -fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!*ast.Node { +fn transCompoundAssignOperator(c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!Node { switch (stmt.getOpcode()) { .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreateCompoundAssign(rp, scope, stmt, .AssignMulWrap, .AsteriskPercentEqual, "*%=", .MulWrap, .AsteriskPercent, "*%", used) + return transCreateCompoundAssign(c, scope, stmt, .assign_mul_wrap, used) else - return transCreateCompoundAssign(rp, scope, stmt, .AssignMul, .AsteriskEqual, "*=", .Mul, .Asterisk, "*", used), + return transCreateCompoundAssign(c, scope, stmt, .assign_mul, used), .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreateCompoundAssign(rp, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", .AddWrap, .PlusPercent, "+%", used) + return transCreateCompoundAssign(c, scope, stmt, .assign_add_wrap, used) else - return transCreateCompoundAssign(rp, scope, stmt, .AssignAdd, .PlusEqual, "+=", .Add, .Plus, "+", used), + return transCreateCompoundAssign(c, scope, stmt, .assign_add, used), .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreateCompoundAssign(rp, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", .SubWrap, .MinusPercent, "-%", used) + return transCreateCompoundAssign(c, scope, stmt, .assign_sub_wrap, used) else - return transCreateCompoundAssign(rp, scope, stmt, .AssignSub, .MinusPercentEqual, "-=", .Sub, .Minus, "-", used), - .DivAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignDiv, .SlashEqual, "/=", .Div, .Slash, "/", used), - .RemAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignMod, .PercentEqual, "%=", .Mod, .Percent, "%", used), - .ShlAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftLeft, .AngleBracketAngleBracketLeftEqual, "<<=", .BitShiftLeft, .AngleBracketAngleBracketLeft, "<<", used), - .ShrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitShiftRight, .AngleBracketAngleBracketRightEqual, ">>=", .BitShiftRight, .AngleBracketAngleBracketRight, ">>", used), - .AndAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitAnd, .AmpersandEqual, "&=", .BitAnd, .Ampersand, "&", used), - .XorAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitXor, .CaretEqual, "^=", .BitXor, .Caret, "^", used), - .OrAssign => return transCreateCompoundAssign(rp, scope, stmt, .AssignBitOr, .PipeEqual, "|=", .BitOr, .Pipe, "|", used), - else => return revertAndWarn( + return transCreateCompoundAssign(c, scope, stmt, .assign_sub, used), + .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_div, used), + .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_mod, used), + .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_shl, used), + .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_shr, used), + .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_bit_and, used), + .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_bit_xor, used), + .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_bit_or, used), + else => return fail( rp, error.UnsupportedTranslation, stmt.getBeginLoc(), @@ -2855,25 +2824,20 @@ fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const cla } fn transCreateCompoundAssign( - rp: RestorePoint, + c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, - assign_op: ast.Node.Tag, - assign_tok_id: std.zig.Token.Id, - assign_bytes: []const u8, - bin_op: ast.Node.Tag, - bin_tok_id: std.zig.Token.Id, - bin_bytes: []const u8, + op: Node.Tag, used: ResultUsed, -) TransError!*ast.Node { - const is_shift = bin_op == .BitShiftLeft or bin_op == .BitShiftRight; - const is_div = bin_op == .Div; - const is_mod = bin_op == .Mod; +) TransError!Node { + const is_shift = op == .assign_shl or op == .assign_shr; + const is_div = op == .assign_div; + const is_mod = op == .assign_mod; const lhs = stmt.getLHS(); const rhs = stmt.getRHS(); const loc = stmt.getBeginLoc(); - const lhs_qt = getExprQualType(rp.c, lhs); - const rhs_qt = getExprQualType(rp.c, rhs); + const lhs_qt = getExprQualType(c, lhs); + const rhs_qt = getExprQualType(c, rhs); const is_signed = cIsSignedInteger(lhs_qt); const requires_int_cast = blk: { const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt); @@ -2885,146 +2849,99 @@ fn transCreateCompoundAssign( // c: lhs += rhs // zig: lhs += rhs if ((is_mod or is_div) and is_signed) { - const op_token = try appendToken(rp.c, .Equal, "="); - const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - const builtin = if (is_mod) "@rem" else "@divTrunc"; - const builtin_node = try rp.c.createBuiltinCall(builtin, 2); - const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); - builtin_node.params()[0] = lhs_node; - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value); - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - op_node.* = .{ - .base = .{ .tag = .Assign }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = &builtin_node.base, - }; - _ = try appendToken(rp.c, .Semicolon, ";"); - return &op_node.base; + const lhs_node = try transExpr(c, scope, lhs, .used, .l_value); + const rhs_node = try transExpr(c, scope, rhs, .used, .r_value); + const builtin = if (is_mod) + try Node.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) + else + try Node.divTrunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + + return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); } - const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); - const eq_token = try appendToken(rp.c, assign_tok_id, assign_bytes); + const lhs_node = try transExpr(c, scope, lhs, .used, .l_value); var rhs_node = if (is_shift or requires_int_cast) - try transExprCoercing(rp, scope, rhs, .used, .r_value) + try transExprCoercing(c, scope, rhs, .used, .r_value) else - try transExpr(rp, scope, rhs, .used, .r_value); + try transExpr(c, scope, rhs, .used, .r_value); if (is_shift or requires_int_cast) { - const cast_node = try rp.c.createBuiltinCall("@intCast", 2); + // @intCast(rhs) const cast_to_type = if (is_shift) - try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc) + try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc) else - try transQualType(rp, getExprQualType(rp.c, lhs), loc); - cast_node.params()[0] = cast_to_type; - _ = try appendToken(rp.c, .Comma, ","); - cast_node.params()[1] = rhs_node; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - rhs_node = &cast_node.base; + try transQualType(c, getExprQualType(c, lhs), loc); + + rhs_node = try Node.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } - if (scope.id != .Condition) - _ = try appendToken(rp.c, .Semicolon, ";"); - return transCreateNodeInfixOp(rp, scope, lhs_node, assign_op, eq_token, rhs_node, .used, false); + + return transCreateNodeInfixOp(c, scope, assign_op, lhs_node, rhs_node, .used); } // worst case // c: lhs += rhs // zig: (blk: { // zig: const _ref = &lhs; - // zig: _ref.* = _ref.* + rhs; + // zig: _ref.* += rhs; // zig: break :blk _ref.* // zig: }) - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); - const ref = try block_scope.makeMangledName(rp.c, "ref"); + const ref = try block_scope.makeMangledName(c, "ref"); - const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(rp.c, ref); - const eq_token = try appendToken(rp.c, .Equal, "="); - const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); - addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value); - const init_node = &addr_node.base; - const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .init_node = init_node, - }); - try block_scope.statements.append(&node.base); + const expr = try transExpr(c, scope, op_expr, .used, .r_value); + const addr_of = try Node.address_of.create(c.arena, expr); + const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of}); + try block_scope.statements.append(ref_decl); - const lhs_node = try transCreateNodeIdentifier(rp.c, ref); - const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node); - _ = try appendToken(rp.c, .Semicolon, ";"); + const lhs_node = try Node.identifier.create(c.arena, ref); + const ref_node = try Node.deref.create(c.arena, lhs_node); if ((is_mod or is_div) and is_signed) { - const op_token = try appendToken(rp.c, .Equal, "="); - const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - const builtin = if (is_mod) "@rem" else "@divTrunc"; - const builtin_node = try rp.c.createBuiltinCall(builtin, 2); - builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node); - _ = try appendToken(rp.c, .Comma, ","); - builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value); - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - _ = try appendToken(rp.c, .Semicolon, ";"); - op_node.* = .{ - .base = .{ .tag = .Assign }, - .op_token = op_token, - .lhs = ref_node, - .rhs = &builtin_node.base, - }; - _ = try appendToken(rp.c, .Semicolon, ";"); - try block_scope.statements.append(&op_node.base); + const rhs_node = try transExpr(c, scope, rhs, .used, .r_value); + const builtin = if (is_mod) + try Node.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) + else + try Node.divTrunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + + const assign = try transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); + try block_scope.statements.append(assign); } else { - const bin_token = try appendToken(rp.c, bin_tok_id, bin_bytes); - var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + var rhs_node = try transExpr(c, scope, rhs, .used, .r_value); if (is_shift or requires_int_cast) { - const cast_node = try rp.c.createBuiltinCall("@intCast", 2); + // @intCast(rhs) const cast_to_type = if (is_shift) - try qualTypeToLog2IntRef(rp, getExprQualType(rp.c, rhs), loc) + try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc) else - try transQualType(rp, getExprQualType(rp.c, lhs), loc); - cast_node.params()[0] = cast_to_type; - _ = try appendToken(rp.c, .Comma, ","); - cast_node.params()[1] = rhs_node; - cast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - rhs_node = &cast_node.base; + try transQualType(c, getExprQualType(c, lhs), loc); + + rhs_node = try Node.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } - const rhs_bin = try transCreateNodeInfixOp(rp, scope, ref_node, bin_op, bin_token, rhs_node, .used, false); - _ = try appendToken(rp.c, .Semicolon, ";"); - - const ass_eq_token = try appendToken(rp.c, .Equal, "="); - const assign = try transCreateNodeInfixOp(rp, scope, ref_node, .Assign, ass_eq_token, rhs_bin, .used, false); + const assign = try transCreateNodeInfixOp(c, scope, op, ref_node, rhs_node, .used); try block_scope.statements.append(assign); } - const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node); - try block_scope.statements.append(&break_node.base); - const block_node = try block_scope.complete(rp.c); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = try appendToken(rp.c, .LParen, "("), - .expr = block_node, - .rparen = try appendToken(rp.c, .RParen, ")"), - }; - return &grouped_expr.base; + const break_node = try Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = ref_node, + }); + try block_scope.statements.append(break_node); + return block_scope.complete(c); } fn transCPtrCast( - rp: RestorePoint, + c: *Context, loc: clang.SourceLocation, dst_type: clang.QualType, src_type: clang.QualType, - expr: *ast.Node, -) !*ast.Node { + expr: Node, +) !Node { const ty = dst_type.getTypePtr(); const child_type = ty.getPointeeType(); const src_ty = src_type.getTypePtr(); const src_child_type = src_ty.getPointeeType(); + const dst_type = try transType(c, ty, loc); if ((src_child_type.isConstQualified() and !child_type.isConstQualified()) or @@ -3032,47 +2949,25 @@ fn transCPtrCast( !child_type.isVolatileQualified())) { // Casting away const or volatile requires us to use @intToPtr - const inttoptr_node = try rp.c.createBuiltinCall("@intToPtr", 2); - const dst_type_node = try transType(rp, ty, loc); - inttoptr_node.params()[0] = dst_type_node; - _ = try appendToken(rp.c, .Comma, ","); - - const ptrtoint_node = try rp.c.createBuiltinCall("@ptrToInt", 1); - ptrtoint_node.params()[0] = expr; - ptrtoint_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - inttoptr_node.params()[1] = &ptrtoint_node.base; - inttoptr_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - return &inttoptr_node.base; + const ptr_to_int = try Node.ptr_to_int.create(c.arena, expr); + const int_to_ptr = try Node.int_to_ptr.create(c.arena, .{ .lhs = dst_type, .rhs = ptr_to_int }); + return int_to_ptr; } else { // Implicit downcasting from higher to lower alignment values is forbidden, // use @alignCast to side-step this problem - const ptrcast_node = try rp.c.createBuiltinCall("@ptrCast", 2); - const dst_type_node = try transType(rp, ty, loc); - ptrcast_node.params()[0] = dst_type_node; - _ = try appendToken(rp.c, .Comma, ","); - - if (qualTypeCanon(child_type).isVoidType()) { + const rhs = if (qualTypeCanon(child_type).isVoidType()) // void has 1-byte alignment, so @alignCast is not needed - ptrcast_node.params()[1] = expr; - } else if (typeIsOpaque(rp.c, qualTypeCanon(child_type), loc)) { + expr + else if (typeIsOpaque(c, qualTypeCanon(child_type), loc)) // For opaque types a ptrCast is enough - ptrcast_node.params()[1] = expr; - } else { - const aligncast_node = try rp.c.createBuiltinCall("@alignCast", 2); - const alignof_node = try rp.c.createBuiltinCall("@alignOf", 1); - const child_type_node = try transQualType(rp, child_type, loc); - alignof_node.params()[0] = child_type_node; - alignof_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - aligncast_node.params()[0] = &alignof_node.base; - _ = try appendToken(rp.c, .Comma, ","); - aligncast_node.params()[1] = expr; - aligncast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - ptrcast_node.params()[1] = &aligncast_node.base; - } - ptrcast_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - - return &ptrcast_node.base; + expr + else blk: { + const child_type_node = try transQualType(c, child_type, loc); + const alignof = try Node.alignof.create(c.arena, child_type_node); + const align_cast = try Node.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); + break :blk align_cast; + }; + return Node.ptr_cast.create(c.arena, .{ .lhs = dst_type, .rhs = rhs }); } } @@ -3092,13 +2987,15 @@ fn transBreak(c: *Context, scope: *Scope) TransError!Node { fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { // TODO use something more accurate const dbl = stmt.getValueAsApproximateDouble(); - const node = try Node.float_literal.create(c.arena, try std.fmt.allocPrint(c.arena, "{d}", .{dbl})); + const node = try transCreateNodeNumber(c, dbl); return maybeSuppressResult(c, scope, used, &node.base); } -fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node { +fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!Node { // GNU extension of the ternary operator where the middle expression is // omitted, the conditition itself is returned if it evaluates to true + const qt = @ptrCast(*const clang.Stmt, stmt).getType(); + const res_is_bool = qualTypeIsBoolean(qt); const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); const cond_expr = casted_stmt.getCond(); const true_expr = casted_stmt.getTrueExpr(); @@ -3109,67 +3006,39 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const // const _cond_temp = (cond_expr); // break :blk if (_cond_temp) _cond_temp else (false_expr); // }) - const lparen = try appendToken(rp.c, .LParen, "("); - - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); - const mangled_name = try block_scope.makeMangledName(rp.c, "cond_temp"); - const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(rp.c, mangled_name); - const eq_token = try appendToken(rp.c, .Equal, "="); - const init_node = try transExpr(rp, &block_scope.base, cond_expr, .used, .r_value); - const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const tmp_var = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .init_node = init_node, + const mangled_name = try block_scope.makeMangledName(c, "cond_temp"); + const init_node = try transExpr(c, &block_scope.base, cond_expr, .used, .r_value); + const ref_decl = try Node.var_simple.create(c.arena, .{ .name = mangled_name, .init = init_node}); + try block_scope.statements.append(ref_decl); + + const cond_node = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); + var then_body = try Node.identifier.create(c.arena, mangled_name); + if (!res_is_bool and isBoolRes(init_node)) { + then_body = try Node.bool_to_int.create(c.arena, then_body); + } + + var else_body = try transExpr(c, &block_scope.base, false_expr, .used, .r_value); + if (!res_is_bool and isBoolRes(else_body)) { + else_body = try Node.bool_to_int.create(c.arena, else_body); + } + const if_node = try Node.@"if".create(c.arena, .{ + .cond = cond, + .then = then_body, + .@"else" = else_body, }); - try block_scope.statements.append(&tmp_var.base); - - var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label); - - const if_node = try transCreateNodeIf(rp.c); - var cond_scope = Scope.Condition{ - .base = .{ - .parent = &block_scope.base, - .id = .Condition, - }, - }; - defer cond_scope.deinit(); - const tmp_var_node = try transCreateNodeIdentifier(rp.c, mangled_name); - - const ty = getExprQualType(rp.c, cond_expr).getTypePtr(); - const cond_node = try finishBoolExpr(rp, &cond_scope.base, cond_expr.getBeginLoc(), ty, tmp_var_node, used); - if_node.condition = cond_node; - _ = try appendToken(rp.c, .RParen, ")"); - - if_node.body = try transCreateNodeIdentifier(rp.c, mangled_name); - if_node.@"else" = try transCreateNodeElse(rp.c); - if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value); - _ = try appendToken(rp.c, .Semicolon, ";"); - - const break_node = try break_node_tmp.finish(&if_node.base); - _ = try appendToken(rp.c, .Semicolon, ";"); - try block_scope.statements.append(&break_node.base); - const block_node = try block_scope.complete(rp.c); - - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = lparen, - .expr = block_node, - .rparen = try appendToken(rp.c, .RParen, ")"), - }; - return maybeSuppressResult(rp, scope, used, &grouped_expr.base); + const break_node = try Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = if_node, + }); + try block_scope.statements.append(break_node); + const res = try block_scope.complete(c); + return maybeSuppressResult(c, scope, used, res); } -fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!*ast.Node { - const grouped = scope.id == .Condition; - const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined; - const if_node = try transCreateNodeIf(rp.c); +fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!Node { var cond_scope = Scope.Condition{ .base = .{ .parent = scope, @@ -3178,60 +3047,41 @@ fn transConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang. }; defer cond_scope.deinit(); + const qt = @ptrCast(*const clang.Stmt, stmt).getType(); + const res_is_bool = qualTypeIsBoolean(qt); const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); const cond_expr = casted_stmt.getCond(); const true_expr = casted_stmt.getTrueExpr(); const false_expr = casted_stmt.getFalseExpr(); - if_node.condition = try transBoolExpr(rp, &cond_scope.base, cond_expr, .used, .r_value, false); - _ = try appendToken(rp.c, .RParen, ")"); + const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used, .r_value); - if_node.body = try transExpr(rp, scope, true_expr, .used, .r_value); - - if_node.@"else" = try transCreateNodeElse(rp.c); - if_node.@"else".?.body = try transExpr(rp, scope, false_expr, .used, .r_value); - - if (grouped) { - const rparen = try appendToken(rp.c, .RParen, ")"); - const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); - grouped_expr.* = .{ - .lparen = lparen, - .expr = &if_node.base, - .rparen = rparen, - }; - return maybeSuppressResult(rp, scope, used, &grouped_expr.base); - } else { - return maybeSuppressResult(rp, scope, used, &if_node.base); + var then_body = try transExpr(c, scope, true_expr, .used, .r_value); + if (!res_is_bool and isBoolRes(then_body)) { + then_body = try Node.bool_to_int.create(c.arena, then_body); } + + var else_body = try transExpr(c, scope, false_expr, .used, .r_value); + if (!res_is_bool and isBoolRes(else_body)) { + else_body = try Node.bool_to_int.create(c.arena, else_body); + } + + const if_node = try Node.@"if".create(c.arena, .{ + .cond = cond, + .then = then_body, + .@"else" = else_body, + }); + return maybeSuppressResult(c, scope, used, if_node); } fn maybeSuppressResult( - rp: RestorePoint, + c: *Context, scope: *Scope, used: ResultUsed, - result: *ast.Node, -) TransError!*ast.Node { + result: Node, +) TransError!Node { if (used == .used) return result; - if (scope.id != .Condition) { - // NOTE: This is backwards, but the semicolon must immediately follow the node. - _ = try appendToken(rp.c, .Semicolon, ";"); - } else { // TODO is there a way to avoid this hack? - // this parenthesis must come immediately following the node - _ = try appendToken(rp.c, .RParen, ")"); - // these need to come before _ - _ = try appendToken(rp.c, .Colon, ":"); - _ = try appendToken(rp.c, .LParen, "("); - } - const lhs = try transCreateNodeIdentifier(rp.c, "_"); - const op_token = try appendToken(rp.c, .Equal, "="); - const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .Assign }, - .op_token = op_token, - .lhs = lhs, - .rhs = result, - }; - return &op_node.base; + return Node.ignore.create(c.arena, result); } fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { @@ -3242,11 +3092,11 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { /// only matters for incomplete arrays, since the size of the array is determined /// by the size of the initializer fn transQualTypeInitialized( - rp: RestorePoint, + c: *Context, qt: clang.QualType, decl_init: *const clang.Expr, source_loc: clang.SourceLocation, -) TypeError!*ast.Node { +) TypeError!Node { const ty = qt.getTypePtr(); if (ty.getTypeClass() == .IncompleteArray) { const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); @@ -3257,17 +3107,17 @@ fn transQualTypeInitialized( const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator const array_size = @intCast(usize, string_lit_size); - return transCreateNodeArrayType(rp, source_loc, elem_ty, array_size); + return Node.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); }, .InitListExprClass => { const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); const size = init_expr.getNumInits(); - return transCreateNodeArrayType(rp, source_loc, elem_ty, size); + return Node.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty }); }, else => {}, } } - return transQualType(rp, qt, source_loc); + return transQualType(c, qt, source_loc); } fn transQualType(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node { @@ -3276,16 +3126,16 @@ fn transQualType(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocati /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. /// Asserts the type is an integer. -fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!*ast.Node { +fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!Node { return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed); } /// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness. /// Asserts the type is an integer. -fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!*ast.Node { +fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!Node { assert(ty.getTypeClass() == .Builtin); const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); - return transCreateNodeIdentifier(c, switch (builtin_ty.getKind()) { + return Node.type.create(c.arena, switch (builtin_ty.getKind()) { .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8", .UShort, .Short => if (is_signed) "c_short" else "c_ushort", .UInt, .Int => if (is_signed) "c_int" else "c_uint", @@ -3532,28 +3382,22 @@ fn cIsLongLongInteger(qt: clang.QualType) bool { }; } fn transCreateNodeAssign( - rp: RestorePoint, + c: *Context, scope: *Scope, result_used: ResultUsed, lhs: *const clang.Expr, rhs: *const clang.Expr, -) !*ast.Node { +) !Node { // common case // c: lhs = rhs // zig: lhs = rhs if (result_used == .unused) { - const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); - const eq_token = try appendToken(rp.c, .Equal, "="); - var rhs_node = try transExprCoercing(rp, scope, rhs, .used, .r_value); + const lhs_node = try transExpr(c, scope, lhs, .used, .l_value); + var rhs_node = try transExprCoercing(c, scope, rhs, .used, .r_value); if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); - builtin_node.params()[0] = rhs_node; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - rhs_node = &builtin_node.base; + rhs_node = try Node.bool_to_int.create(c.arena, rhs_node); } - if (scope.id != .Condition) - _ = try appendToken(rp.c, .Semicolon, ";"); - return transCreateNodeInfixOp(rp, scope, lhs_node, .Assign, eq_token, rhs_node, .used, false); + return transCreateNodeInfixOp(c, scope, .assign, lhs_node, rhs_node, .used); } // worst case @@ -3563,76 +3407,36 @@ fn transCreateNodeAssign( // zig: lhs = _tmp; // zig: break :blk _tmp // zig: }) - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); - const tmp = try block_scope.makeMangledName(rp.c, "tmp"); - const mut_tok = try appendToken(rp.c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(rp.c, tmp); - const eq_token = try appendToken(rp.c, .Equal, "="); - var rhs_node = try transExpr(rp, &block_scope.base, rhs, .used, .r_value); - if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); - builtin_node.params()[0] = rhs_node; - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - rhs_node = &builtin_node.base; - } - const init_node = rhs_node; - const semicolon_token = try appendToken(rp.c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(rp.c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .eq_token = eq_token, - .init_node = init_node, - }); - try block_scope.statements.append(&node.base); + const tmp = try block_scope.makeMangledName(c, "tmp"); + const rhs = try transExpr(c, scope, op_expr, .used, .r_value); + const tmp_decl = try Node.var_simple.create(c.arena, .{ .name = tmp, .init = rhs}); + try block_scope.statements.append(tmp_decl); - const lhs_node = try transExpr(rp, &block_scope.base, lhs, .used, .l_value); - const lhs_eq_token = try appendToken(rp.c, .Equal, "="); - const ident = try transCreateNodeIdentifier(rp.c, tmp); - _ = try appendToken(rp.c, .Semicolon, ";"); - const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false); + const lhs = try transExpr(c, &block_scope.base, lhs, .used, .l_value); + const tmp_ident = try Node.identifier.create(c.arena, tmp); + const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs, tmp_iden, .used); try block_scope.statements.append(assign); - const break_node = blk: { - var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, tokenSlice(rp.c, block_scope.label.?)); - const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp); - break :blk try tmp_ctrl_flow.finish(rhs_expr); - }; - _ = try appendToken(rp.c, .Semicolon, ";"); - try block_scope.statements.append(&break_node.base); - const block_node = try block_scope.complete(rp.c); - // semicolon must immediately follow rbrace because it is the last token in a block - _ = try appendToken(rp.c, .Semicolon, ";"); - return block_node; + const break_node = try Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = tmp_ident, + }); + try block_scope.statements.append(break_node); + return block_scope.complete(c); } -fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node { - const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp); - field_access_node.* = .{ - .base = .{ .tag = .Period }, - .op_token = try appendToken(c, .Period, "."), - .lhs = container, - .rhs = try transCreateNodeIdentifier(c, field_name), - }; - return &field_access_node.base; -} - -fn transCreateNodeBoolInfixOp( +fn transCreateNodeInfixOp( c: *Context, scope: *Scope, - stmt: *const clang.BinaryOperator, op: ast.Node.Tag, + lhs: Node, + rhs: Node, used: ResultUsed, ) !Node { - std.debug.assert(op == .bool_and or op == .bool_or); - - const lhs = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value, true); - const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value, true); - const payload = try c.arena.create(ast.Payload.BinOp); payload.* = .{ .base = .{ .tag = op }, @@ -3644,39 +3448,19 @@ fn transCreateNodeBoolInfixOp( return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base)); } -fn transCreateNodePtrType( +fn transCreateNodeBoolInfixOp( c: *Context, - is_const: bool, - is_volatile: bool, - op_tok_id: std.zig.Token.Id, -) !*ast.Node.PtrType { - const node = try c.arena.create(ast.Node.PtrType); - const op_token = switch (op_tok_id) { - .LBracket => blk: { - const lbracket = try appendToken(c, .LBracket, "["); - _ = try appendToken(c, .Asterisk, "*"); - _ = try appendToken(c, .RBracket, "]"); - break :blk lbracket; - }, - .Identifier => blk: { - const lbracket = try appendToken(c, .LBracket, "["); // Rendering checks if this token + 2 == .Identifier, so needs to return this token - _ = try appendToken(c, .Asterisk, "*"); - _ = try appendIdentifier(c, "c"); - _ = try appendToken(c, .RBracket, "]"); - break :blk lbracket; - }, - .Asterisk => try appendToken(c, .Asterisk, "*"), - else => unreachable, - }; - node.* = .{ - .op_token = op_token, - .ptr_info = .{ - .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, - .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, - }, - .rhs = undefined, // translate and set afterward - }; - return node; + scope: *Scope, + stmt: *const clang.BinaryOperator, + op: ast.Node.Tag, + used: ResultUsed, +) !Node { + std.debug.assert(op == .bool_and or op == .bool_or); + + const lhs = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value); + const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value); + + return transCreateNodeInfixOp(c, scope, op, lhs, rhs, used); } fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { @@ -3722,73 +3506,10 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { return Node.int_literal.create(c.arena, str); } -fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node { - const token = try appendToken(c, .Keyword_undefined, "undefined"); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .UndefinedLiteral }, - .token = token, - }; - return &node.base; -} - -fn transCreateNodeNullLiteral(c: *Context) !*ast.Node { - const token = try appendToken(c, .Keyword_null, "null"); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .NullLiteral }, - .token = token, - }; - return &node.base; -} - -fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { - const token = if (value) - try appendToken(c, .Keyword_true, "true") - else - try appendToken(c, .Keyword_false, "false"); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .BoolLiteral }, - .token = token, - }; - return &node.base; -} - -fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { - const fmt_s = if (comptime std.meta.trait.isIntegerNumber(@TypeOf(int))) "{d}" else "{s}"; - const token = try appendTokenFmt(c, .IntegerLiteral, fmt_s, .{int}); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .IntegerLiteral }, - .token = token, - }; - return &node.base; -} - -fn transCreateNodeFloat(c: *Context, str: []const u8) !*ast.Node { - const token = try appendTokenFmt(c, .FloatLiteral, "{s}", .{str}); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .FloatLiteral }, - .token = token, - }; - return &node.base; -} - -fn transCreateNodeOpaqueType(c: *Context) !*ast.Node { - const container_tok = try appendToken(c, .Keyword_opaque, "opaque"); - const lbrace_token = try appendToken(c, .LBrace, "{"); - const container_node = try ast.Node.ContainerDecl.alloc(c.arena, 0); - container_node.* = .{ - .kind_token = container_tok, - .layout_token = null, - .lbrace_token = lbrace_token, - .rbrace_token = try appendToken(c, .RBrace, "}"), - .fields_and_decls_len = 0, - .init_arg_expr = .None, - }; - return &container_node.base; +fn transCreateNodeNumber(c: *Context, int: anytype) !Node { + const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(int))) "{d}" else "{s}"; + const str = try std.fmt.allocPrint(c.arena, fmt_s, .{int}); + return Node.int_literal.create(c.arena, str); } fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node { @@ -3873,13 +3594,12 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a return &fn_proto.base; } - - fn transCreateNodeShiftOp( c: *Context, scope: *Scope, stmt: *const clang.BinaryOperator, op: Node.Tag, + used: ResultUsed, ) !Node { std.debug.assert(op == .shl or op == .shr); @@ -3894,15 +3614,7 @@ fn transCreateNodeShiftOp( const rhs = try transExprCoercing(c, scope, rhs_expr, .used, .r_value); const rhs_casted = try Node.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type }); - const payload = try c.arena.create(ast.Payload.BinOp); - payload.* = .{ - .base = .{ .tag = op }, - .data = .{ - .lhs = lhs, - .rhs = rhs_casted, - }, - }; - return Node.initPayload(&payload.base); + return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used); } fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 3a7c02fd74..d62e83ea6a 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -24,8 +24,8 @@ pub const Node = extern union { usingnamespace_builtins, // After this, the tag requires a payload. - int_literal, - float_literal, + // int or float, doesn't really matter + number_literal, string_literal, char_literal, identifier, @@ -115,6 +115,7 @@ pub const Node = extern union { bit_xor, array_cat, ellipsis3, + assign, log2_int_type, /// @import("std").math.Log2Int(operand) @@ -147,6 +148,8 @@ pub const Node = extern union { int_to_ptr, /// @ptrToInt(operand) ptr_to_int, + /// @alignCast(lhs, rhs) + align_cast, negate, negate_wrap, @@ -190,6 +193,9 @@ pub const Node = extern union { /// [1]type{val} ** count array_filler, + /// _ = operand; + ignore, + pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -227,6 +233,7 @@ pub const Node = extern union { .while_true, .if_not_break, .switch_else, + .ignore, => Payload.UnOp, .add, @@ -292,12 +299,14 @@ pub const Node = extern union { .array_cat, .ellipsis3, .switch_prong, + .field_access, + .assign, + .align_cast, => Payload.BinOp, - .int, - .float, - .string, - .char, + .number_literal, + .string_literal, + .char_literal, .identifier, .warning, .failed_decl, @@ -327,7 +336,6 @@ pub const Node = extern union { .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, .array_filler => Payload.ArrayFiller, - .field_access => Payload.FieldAccess, }; } @@ -590,14 +598,6 @@ pub const Payload = struct { count: usize, }, }; - - pub const FieldAccess = struct { - base: Node, - data: struct { - container: Node, - name: []const u8, - }, - }; }; /// Converts the nodes into a Zig ast. From 66bbd75a8346b8a292f0d95d4b60a5d7d11b73b2 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 11 Feb 2021 17:44:04 +0200 Subject: [PATCH 15/36] translate-c: convert macro translation --- src/translate_c.zig | 960 ++++++++++++---------------------------- src/translate_c/ast.zig | 41 +- 2 files changed, 307 insertions(+), 694 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 0a20d5e6d5..9ef4788ba7 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3512,86 +3512,48 @@ fn transCreateNodeNumber(c: *Context, int: anytype) !Node { return Node.int_literal.create(c.arena, str); } -fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_alias: *ast.Node.FnProto) !*ast.Node { +fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node { const scope = &c.global_scope.base; - const pub_tok = try appendToken(c, .Keyword_pub, "pub"); - const fn_tok = try appendToken(c, .Keyword_fn, "fn"); - const name_tok = try appendIdentifier(c, name); - _ = try appendToken(c, .LParen, "("); - - var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa); + var fn_params = std.ArrayList(Node).init(c.gpa); defer fn_params.deinit(); for (proto_alias.params()) |param, i| { - if (i != 0) { - _ = try appendToken(c, .Comma, ","); - } - const param_name_tok = param.name_token orelse - try appendTokenFmt(c, .Identifier, "arg_{d}", .{c.getMangle()}); + const param_name = param.name orelse + try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()}); - _ = try appendToken(c, .Colon, ":"); - - (try fn_params.addOne()).* = .{ - .doc_comments = null, - .comptime_token = null, - .noalias_token = param.noalias_token, - .name_token = param_name_tok, - .param_type = param.param_type, - }; + try fn_params.append(.{ + .name = param_name, + .type = param.type, + .is_noalias = param.is_noalias, + }); } - _ = try appendToken(c, .RParen, ")"); - - _ = try appendToken(c, .Keyword_callconv, "callconv"); - _ = try appendToken(c, .LParen, "("); - const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline"); - _ = try appendToken(c, .RParen, ")"); - - const block_lbrace = try appendToken(c, .LBrace, "{"); - - const return_kw = try appendToken(c, .Keyword_return, "return"); - const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getInitNode().?); - - const call_expr = try c.createCall(unwrap_expr, fn_params.items.len); - const call_params = call_expr.params(); + const init = if (value.castTag(.var_decl)) |v| + v.data.init + else if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |v| + v.data.init + else + unreachable; + const unwrap_expr = try Node.unwrap.create(c.arena, init); + const call_params = try c.arena.alloc(Node, fn_params.items.len); for (fn_params.items) |param, i| { - if (i != 0) { - _ = try appendToken(c, .Comma, ","); - } - call_params[i] = try transCreateNodeIdentifier(c, tokenSlice(c, param.name_token.?)); + call_params[i] = try Node.identifier.create(c.arena, param.name); } - call_expr.rtoken = try appendToken(c, .RParen, ")"); - - const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{ - .ltoken = return_kw, - .tag = .Return, - }, .{ - .rhs = &call_expr.base, + const call_expr = try Node.call.create(c.arean, .{ + .lhs = unwrap_expr, + .args = call_params, }); - _ = try appendToken(c, .Semicolon, ";"); + const return_expr = try Node.@"return".create(c.arean, call_expr); + const block = try Node.block_single.create(c.arean, return_expr); - const block = try ast.Node.Block.alloc(c.arena, 1); - block.* = .{ - .lbrace = block_lbrace, - .statements_len = 1, - .rbrace = try appendToken(c, .RBrace, "}"), - }; - block.statements()[0] = &return_expr.base; - - const fn_proto = try ast.Node.FnProto.create(c.arena, .{ - .params_len = fn_params.items.len, - .fn_token = fn_tok, + return Node.pub_inline_fn.create(c.arena, .{ + .name = name, + .params = try c.arena.dupe(ast.Node.Param, fn_params.items), .return_type = proto_alias.return_type, - }, .{ - .visib_token = pub_tok, - .name_token = name_tok, - .body_node = &block.base, - .callconv_expr = callconv_expr, + .body = block, }); - mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); - return &fn_proto.base; } fn transCreateNodeShiftOp( @@ -4108,27 +4070,13 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { const scope = &c.global_scope.base; - const visib_tok = try appendToken(c, .Keyword_pub, "pub"); - const mut_tok = try appendToken(c, .Keyword_const, "const"); - const name_tok = try appendIdentifier(c, m.name); - const eq_token = try appendToken(c, .Equal, "="); - const init_node = try parseCExpr(c, m, scope); const last = m.next().?; if (last != .Eof and last != .Nl) return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); - const semicolon_token = try appendToken(c, .Semicolon, ";"); - const node = try ast.Node.VarDecl.create(c.arena, .{ - .name_token = name_tok, - .mut_token = mut_tok, - .semicolon_token = semicolon_token, - }, .{ - .visib_token = visib_tok, - .eq_token = eq_token, - .init_node = init_node, - }); - _ = try c.global_scope.macro_table.put(m.name, &node.base); + const var_decl = try Node.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); + _ = try c.global_scope.macro_table.put(m.name, var_decl); } fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { @@ -4136,16 +4084,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { defer block_scope.deinit(); const scope = &block_scope.base; - const pub_tok = try appendToken(c, .Keyword_pub, "pub"); - const fn_tok = try appendToken(c, .Keyword_fn, "fn"); - const name_tok = try appendIdentifier(c, m.name); - _ = try appendToken(c, .LParen, "("); - if (m.next().? != .LParen) { return m.fail(c, "unable to translate C expr: expected '('", .{}); } - var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(c.gpa); + var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); defer fn_params.deinit(); while (true) { @@ -4153,120 +4096,78 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { _ = m.next(); const mangled_name = try block_scope.makeMangledName(c, m.slice()); - const param_name_tok = try appendIdentifier(c, mangled_name); - _ = try appendToken(c, .Colon, ":"); - - const any_type = try c.arena.create(ast.Node.OneToken); - any_type.* = .{ - .base = .{ .tag = .AnyType }, - .token = try appendToken(c, .Keyword_anytype, "anytype"), - }; - - (try fn_params.addOne()).* = .{ - .doc_comments = null, - .comptime_token = null, - .noalias_token = null, - .name_token = param_name_tok, - .param_type = .{ .any_type = &any_type.base }, - }; + try fn_params.append(.{ + .is_noalias = false, + .name = mangled_name, + .type = Node.@"anytype".init(), + }); if (m.peek().? != .Comma) break; _ = m.next(); - _ = try appendToken(c, .Comma, ","); } if (m.next().? != .RParen) { return m.fail(c, "unable to translate C expr: expected ')'", .{}); } - _ = try appendToken(c, .RParen, ")"); - - _ = try appendToken(c, .Keyword_callconv, "callconv"); - _ = try appendToken(c, .LParen, "("); - const callconv_expr = try transCreateNodeEnumLiteral(c, "Inline"); - _ = try appendToken(c, .RParen, ")"); - - const type_of = try c.createBuiltinCall("@TypeOf", 1); - - const return_kw = try appendToken(c, .Keyword_return, "return"); const expr = try parseCExpr(c, m, scope); const last = m.next().?; if (last != .Eof and last != .Nl) return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); - _ = try appendToken(c, .Semicolon, ";"); - const type_of_arg = if (!expr.tag.isBlock()) expr else blk: { - const stmts = expr.blockStatements(); + + const typeof_arg = if (expr.castTag(.block)) |some| blk: { + const stmts = some.data.stmts; const blk_last = stmts[stmts.len - 1]; - const br = blk_last.cast(ast.Node.ControlFlowExpression).?; - break :blk br.getRHS().?; - }; - type_of.params()[0] = type_of_arg; - type_of.rparen_token = try appendToken(c, .RParen, ")"); - const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{ - .ltoken = return_kw, - .tag = .Return, - }, .{ - .rhs = expr, - }); - + const br = blk_last.castTag(.break_val).?; + break :blk br.data; + } else expr; + const typeof = try Node.typeof.create(c.arean, typeof_arg); + const return_expr = try Node.@"return".create(c.arena, expr); try block_scope.statements.append(&return_expr.base); - const block_node = try block_scope.complete(c); - const fn_proto = try ast.Node.FnProto.create(c.arena, .{ - .fn_token = fn_tok, - .params_len = fn_params.items.len, - .return_type = .{ .Explicit = &type_of.base }, - }, .{ - .visib_token = pub_tok, - .name_token = name_tok, - .body_node = block_node, - .callconv_expr = callconv_expr, + + const fn_decl = try Node.pub_inline_fn.create(c.arena, .{ + .name = m.name, + .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), + .return_type = typeof, + .body = try block_scope.complete(c), }); - mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); - _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base); } const ParseError = Error || error{ParseError}; -fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { // TODO parseCAssignExpr here const node = try parseCCondExpr(c, m, scope); if (m.next().? != .Comma) { m.i -= 1; return node; } - _ = try appendToken(c, .Semicolon, ";"); var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); var last = node; while (true) { // suppress result - const lhs = try transCreateNodeIdentifier(c, "_"); - const op_token = try appendToken(c, .Equal, "="); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .Assign }, - .op_token = op_token, - .lhs = lhs, - .rhs = last, - }; - try block_scope.statements.append(&op_node.base); + const ignore = try Node.ignore.create(c.arena, last); + try block_scope.statements.append(ignore); last = try parseCCondExpr(c, m, scope); - _ = try appendToken(c, .Semicolon, ";"); if (m.next().? != .Comma) { m.i -= 1; break; } } - const break_node = try transCreateNodeBreak(c, block_scope.label, last); - try block_scope.statements.append(&break_node.base); + const break_node = try Node.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = last, + }); + try block_scope.statements.append(break_node); return try block_scope.complete(c); } -fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { +fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { var lit_bytes = m.slice(); switch (m.list[m.i].id) { @@ -4286,11 +4187,10 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { } if (suffix == .none) { - return transCreateNodeInt(c, lit_bytes); + return transCreateNodeNumber(c, lit_bytes); } - const cast_node = try c.createBuiltinCall("@as", 2); - cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) { + const type_node = try Node.type.create(c.arena, switch (suffix) { .u => "c_uint", .l => "c_long", .lu => "c_ulong", @@ -4304,27 +4204,22 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { .llu => 3, else => unreachable, }]; - _ = try appendToken(c, .Comma, ","); - cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes); - cast_node.rparen_token = try appendToken(c, .RParen, ")"); - return &cast_node.base; + const rhs = try transCreateNodeNumber(c, lit_bytes); + return Node.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); }, .FloatLiteral => |suffix| { if (lit_bytes[0] == '.') lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes}); if (suffix == .none) { - return transCreateNodeFloat(c, lit_bytes); + return transCreateNodeNumber(c, lit_bytes); } - const cast_node = try c.createBuiltinCall("@as", 2); - cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) { + const type_node = try Node.type.create(c.arena, switch (suffix) { .f => "f32", .l => "c_longdouble", else => unreachable, }); - _ = try appendToken(c, .Comma, ","); - cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]); - cast_node.rparen_token = try appendToken(c, .RParen, ")"); - return &cast_node.base; + const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1]); + return Node.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); }, else => unreachable, } @@ -4490,79 +4385,62 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { return bytes[0..i]; } -fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { const tok = m.next().?; const slice = m.slice(); switch (tok) { .CharLiteral => { if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { - const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, m)); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .CharLiteral }, - .token = token, - }; - return &node.base; + return Node.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); } else { - const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]}); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .IntegerLiteral }, - .token = token, - }; - return &node.base; + const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]}); + return Node.int_literal.create(c.arena, str); } }, .StringLiteral => { - const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, m)); - const node = try c.arena.create(ast.Node.OneToken); - node.* = .{ - .base = .{ .tag = .StringLiteral }, - .token = token, - }; - return &node.base; + return Node.string_literal.create(c.arena, try zigifyEscapeSequences(c, m)); }, .IntegerLiteral, .FloatLiteral => { return parseCNumLit(c, m); }, // eventually this will be replaced by std.c.parse which will handle these correctly - .Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"), - .Keyword_bool => return transCreateNodeIdentifierUnchecked(c, "bool"), - .Keyword_double => return transCreateNodeIdentifierUnchecked(c, "f64"), - .Keyword_long => return transCreateNodeIdentifierUnchecked(c, "c_long"), - .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"), - .Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"), - .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"), - .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"), + .Keyword_void => return Node.type.create(c.arena, "c_void"), + .Keyword_bool => return Node.type.create(c.arena, "bool"), + .Keyword_double => return Node.type.create(c.arena, "f64"), + .Keyword_long => return Node.type.create(c.arena, "c_long"), + .Keyword_int => return Node.type.create(c.arena, "c_int"), + .Keyword_float => return Node.type.create(c.arena, "f32"), + .Keyword_short => return Node.type.create(c.arena, "c_short"), + .Keyword_char => return Node.type.create(c.arena, "u8"), .Keyword_unsigned => if (m.next()) |t| switch (t) { - .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"), - .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"), - .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"), + .Keyword_char => return Node.type.create(c.arena, "u8"), + .Keyword_short => return Node.type.create(c.arena, "c_ushort"), + .Keyword_int => return Node.type.create(c.arena, "c_uint"), .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { _ = m.next(); - return transCreateNodeIdentifierUnchecked(c, "c_ulonglong"); - } else return transCreateNodeIdentifierUnchecked(c, "c_ulong"), + return Node.type.create(c.arena, "c_ulonglong"); + } else return Node.type.create(c.arena, "c_ulong"), else => { m.i -= 1; - return transCreateNodeIdentifierUnchecked(c, "c_uint"); + return Node.type.create(c.arena, "c_uint"); }, } else { - return transCreateNodeIdentifierUnchecked(c, "c_uint"); + return Node.type.create(c.arena, "c_uint"); }, .Keyword_signed => if (m.next()) |t| switch (t) { - .Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"), - .Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"), - .Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"), + .Keyword_char => return Node.type.create(c.arena, "i8"), + .Keyword_short => return Node.type.create(c.arena, "c_short"), + .Keyword_int => return Node.type.create(c.arena, "c_int"), .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { _ = m.next(); - return transCreateNodeIdentifierUnchecked(c, "c_longlong"); - } else return transCreateNodeIdentifierUnchecked(c, "c_long"), + return Node.type.create(c.arena, "c_longlong"); + } else return Node.type.create(c.arena, "c_long"), else => { m.i -= 1; - return transCreateNodeIdentifierUnchecked(c, "c_int"); + return Node.type.create(c.arena, "c_int"); }, } else { - return transCreateNodeIdentifierUnchecked(c, "c_int"); + return Node.type.create(c.arena, "c_int"); }, .Keyword_enum, .Keyword_struct, .Keyword_union => { // struct Foo will be declared as struct_Foo by transRecordDecl @@ -4572,17 +4450,12 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* return error.ParseError; } - const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() }); - const identifier = try c.arena.create(ast.Node.OneToken); - identifier.* = .{ - .base = .{ .tag = .Identifier }, - .token = ident_token, - }; - return &identifier.base; + const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() }); + return Node.identifier.create(c.arena, name); }, .Identifier => { const mangled_name = scope.getAlias(slice); - return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name); + return Node.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); }, .LParen => { const inner_node = try parseCExpr(c, m, scope); @@ -4612,10 +4485,6 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* }, else => return inner_node, } - - // hack to get zig fmt to render a comma in builtin calls - _ = try appendToken(c, .Comma, ","); - const node_to_cast = try parseCExpr(c, m, scope); if (saw_l_paren and m.next().? != .RParen) { @@ -4623,28 +4492,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* return error.ParseError; } - const lparen = try appendToken(c, .LParen, "("); - - //(@import("std").meta.cast(dest, x)) - const import_fn_call = try c.createBuiltinCall("@import", 1); - const std_node = try transCreateNodeStringLiteral(c, "\"std\""); - import_fn_call.params()[0] = std_node; - import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); - const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta"); - const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast"); - - const cast_fn_call = try c.createCall(outer_field_access, 2); - cast_fn_call.params()[0] = inner_node; - cast_fn_call.params()[1] = node_to_cast; - cast_fn_call.rtoken = try appendToken(c, .RParen, ")"); - - const group_node = try c.arena.create(ast.Node.GroupedExpression); - group_node.* = .{ - .lparen = lparen, - .expr = &cast_fn_call.base, - .rparen = try appendToken(c, .RParen, ")"), - }; - return &group_node.base; + return Node.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast }); }, else => { try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); @@ -4653,447 +4501,255 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* } } -fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCPrimaryExprInner(c, m, scope); // In C the preprocessor would handle concatting strings while expanding macros. // This should do approximately the same by concatting any strings and identifiers // after a primary expression. while (true) { - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (m.peek().?) { .StringLiteral, .Identifier => {}, else => break, } - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .ArrayCat }, - .op_token = try appendToken(c, .PlusPlus, "++"), - .lhs = node, - .rhs = try parseCPrimaryExprInner(c, m, scope), - }; - node = &op_node.base; + node = try Node.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) }); } return node; } -fn nodeIsInfixOp(tag: ast.Node.Tag) bool { - return switch (tag) { - .Add, - .AddWrap, - .ArrayCat, - .ArrayMult, - .Assign, - .AssignBitAnd, - .AssignBitOr, - .AssignBitShiftLeft, - .AssignBitShiftRight, - .AssignBitXor, - .AssignDiv, - .AssignSub, - .AssignSubWrap, - .AssignMod, - .AssignAdd, - .AssignAddWrap, - .AssignMul, - .AssignMulWrap, - .BangEqual, - .BitAnd, - .BitOr, - .BitShiftLeft, - .BitShiftRight, - .BitXor, - .BoolAnd, - .BoolOr, - .Div, - .EqualEqual, - .ErrorUnion, - .GreaterOrEqual, - .GreaterThan, - .LessOrEqual, - .LessThan, - .MergeErrorSets, - .Mod, - .Mul, - .MulWrap, - .Period, - .Range, - .Sub, - .SubWrap, - .UnwrapOptional, - .Catch, - => true, - - else => false, - }; -} - -fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node { +fn macroBoolToInt(c: *Context, node: Node) !Node { if (!isBoolRes(node)) { - if (!nodeIsInfixOp(node.tag)) return node; - - const group_node = try c.arena.create(ast.Node.GroupedExpression); - group_node.* = .{ - .lparen = try appendToken(c, .LParen, "("), - .expr = node, - .rparen = try appendToken(c, .RParen, ")"), - }; - return &group_node.base; + return node; } - const builtin_node = try c.createBuiltinCall("@boolToInt", 1); - builtin_node.params()[0] = node; - builtin_node.rparen_token = try appendToken(c, .RParen, ")"); - return &builtin_node.base; + return Node.bool_to_int.create(c.arena, node); } -fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node { +fn macroIntToBool(c: *Context, node: Node) !Node { if (isBoolRes(node)) { - if (!nodeIsInfixOp(node.tag)) return node; - - const group_node = try c.arena.create(ast.Node.GroupedExpression); - group_node.* = .{ - .lparen = try appendToken(c, .LParen, "("), - .expr = node, - .rparen = try appendToken(c, .RParen, ")"), - }; - return &group_node.base; + return node; } - const op_token = try appendToken(c, .BangEqual, "!="); - const zero = try transCreateNodeInt(c, 0); - const res = try c.arena.create(ast.Node.SimpleInfixOp); - res.* = .{ - .base = .{ .tag = .BangEqual }, - .op_token = op_token, - .lhs = node, - .rhs = zero, - }; - const group_node = try c.arena.create(ast.Node.GroupedExpression); - group_node.* = .{ - .lparen = try appendToken(c, .LParen, "("), - .expr = &res.base, - .rparen = try appendToken(c, .RParen, ")"), - }; - return &group_node.base; + return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() }); } -fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node { - if (!nodeIsInfixOp(node.tag)) return node; - - const group_node = try c.arena.create(ast.Node.GroupedExpression); - group_node.* = .{ - .lparen = try appendToken(c, .LParen, "("), - .expr = node, - .rparen = try appendToken(c, .RParen, ")"), - }; - return &group_node.base; -} - -fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { const node = try parseCOrExpr(c, m, scope); if (m.peek().? != .QuestionMark) { return node; } _ = m.next(); - // must come immediately after expr - _ = try appendToken(c, .RParen, ")"); - const if_node = try transCreateNodeIf(c); - if_node.condition = node; - if_node.body = try parseCOrExpr(c, m, scope); + const then_body = try parseCOrExpr(c, m, scope); if (m.next().? != .Colon) { try m.fail(c, "unable to translate C expr: expected ':'", .{}); return error.ParseError; } - if_node.@"else" = try transCreateNodeElse(c); - if_node.@"else".?.body = try parseCCondExpr(c, m, scope); - return &if_node.base; + const else_body = try parseCCondExpr(c, m, scope); + return Node.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body }); } -fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCAndExpr(c, m, scope); while (m.next().? == .PipePipe) { - const lhs_node = try macroIntToBool(c, node); - const op_token = try appendToken(c, .Keyword_or, "or"); - const rhs_node = try parseCAndExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .BoolOr }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroIntToBool(c, rhs_node), - }; - node = &op_node.base; + const lhs = try macroIntToBool(c, node); + const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope)); + node = try Node.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; } -fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCBitOrExpr(c, m, scope); while (m.next().? == .AmpersandAmpersand) { - const lhs_node = try macroIntToBool(c, node); - const op_token = try appendToken(c, .Keyword_and, "and"); - const rhs_node = try parseCBitOrExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .BoolAnd }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroIntToBool(c, rhs_node), - }; - node = &op_node.base; + const lhs = try macroIntToBool(c, node); + const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope)); + node = try Node.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; } -fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCBitXorExpr(c, m, scope); while (m.next().? == .Pipe) { - const lhs_node = try macroBoolToInt(c, node); - const op_token = try appendToken(c, .Pipe, "|"); - const rhs_node = try parseCBitXorExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .BitOr }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCBitXorExpr(c, m, scope)); + node = try Node.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; } -fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCBitAndExpr(c, m, scope); while (m.next().? == .Caret) { - const lhs_node = try macroBoolToInt(c, node); - const op_token = try appendToken(c, .Caret, "^"); - const rhs_node = try parseCBitAndExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .BitXor }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCBitAndExpr(c, m, scope)); + node = try Node.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; } -fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCEqExpr(c, m, scope); while (m.next().? == .Ampersand) { - const lhs_node = try macroBoolToInt(c, node); - const op_token = try appendToken(c, .Ampersand, "&"); - const rhs_node = try parseCEqExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = .BitAnd }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCEqExpr(c, m, scope)); + node = try Node.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; } -fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCRelExpr(c, m, scope); while (true) { - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (m.peek().?) { .BangEqual => { - op_token = try appendToken(c, .BangEqual, "!="); - op_id = .BangEqual; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope)); + node = try Node.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .EqualEqual => { - op_token = try appendToken(c, .EqualEqual, "=="); - op_id = .EqualEqual; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope)); + node = try Node.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } - _ = m.next(); - const lhs_node = try macroBoolToInt(c, node); - const rhs_node = try parseCRelExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = op_id }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; } } -fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCShiftExpr(c, m, scope); while (true) { - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (m.peek().?) { .AngleBracketRight => { - op_token = try appendToken(c, .AngleBracketRight, ">"); - op_id = .GreaterThan; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); + node = try Node.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketRightEqual => { - op_token = try appendToken(c, .AngleBracketRightEqual, ">="); - op_id = .GreaterOrEqual; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); + node = try Node.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketLeft => { - op_token = try appendToken(c, .AngleBracketLeft, "<"); - op_id = .LessThan; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); + node = try Node.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketLeftEqual => { - op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); - op_id = .LessOrEqual; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); + node = try Node.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } - _ = m.next(); - const lhs_node = try macroBoolToInt(c, node); - const rhs_node = try parseCShiftExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = op_id }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; } } -fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCAddSubExpr(c, m, scope); while (true) { - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (m.peek().?) { .AngleBracketAngleBracketLeft => { - op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); - op_id = .BitShiftLeft; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope)); + node = try Node.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketAngleBracketRight => { - op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); - op_id = .BitShiftRight; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope)); + node = try Node.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } - _ = m.next(); - const lhs_node = try macroBoolToInt(c, node); - const rhs_node = try parseCAddSubExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = op_id }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; } } -fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCMulExpr(c, m, scope); while (true) { - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (m.peek().?) { .Plus => { - op_token = try appendToken(c, .Plus, "+"); - op_id = .Add; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope)); + node = try Node.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .Minus => { - op_token = try appendToken(c, .Minus, "-"); - op_id = .Sub; + _ = m.next(); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope)); + node = try Node.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } - _ = m.next(); - const lhs_node = try macroBoolToInt(c, node); - const rhs_node = try parseCMulExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = op_id }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; } } -fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCUnaryExpr(c, m, scope); while (true) { - var op_token: ast.TokenIndex = undefined; - var op_id: ast.Node.Tag = undefined; switch (m.next().?) { .Asterisk => { if (m.peek().? == .RParen) { // type *) - // hack to get zig fmt to render a comma in builtin calls - _ = try appendToken(c, .Comma, ","); - // last token of `node` const prev_id = m.list[m.i - 1].id; if (prev_id == .Keyword_void) { - const ptr = try transCreateNodePtrType(c, false, false, .Asterisk); - ptr.rhs = node; - const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?"); - optional_node.rhs = &ptr.base; - return &optional_node.base; + const ptr = try Node.single_pointer.create(c.arena, .{ + .is_const = false, + .is_volatile = false, + .elem_type = node, + }); + return Node.optional_type.create(c.arena, ptr); } else { - const ptr = try transCreateNodePtrType(c, false, false, Token.Id.Identifier); - ptr.rhs = node; - return &ptr.base; + return Node.c_pointer.create(c.arena, .{ + .is_const = false, + .is_volatile = false, + .elem_type = node, + }); } } else { // expr * expr - op_token = try appendToken(c, .Asterisk, "*"); - op_id = .BitShiftLeft; + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + node = try Node.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } }, .Slash => { - op_id = .Div; - op_token = try appendToken(c, .Slash, "/"); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + node = try Node.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .Percent => { - op_id = .Mod; - op_token = try appendToken(c, .Percent, "%"); + const lhs = try macroBoolToInt(c, node); + const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + node = try Node.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => { m.i -= 1; return node; }, } - const lhs_node = try macroBoolToInt(c, node); - const rhs_node = try parseCUnaryExpr(c, m, scope); - const op_node = try c.arena.create(ast.Node.SimpleInfixOp); - op_node.* = .{ - .base = .{ .tag = op_id }, - .op_token = op_token, - .lhs = lhs_node, - .rhs = try macroBoolToInt(c, rhs_node), - }; - node = &op_node.base; } } -fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var node = try parseCPrimaryExpr(c, m, scope); while (true) { switch (m.next().?) { @@ -5103,38 +4759,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N return error.ParseError; } - node = try transCreateNodeFieldAccess(c, node, m.slice()); - continue; + const ident = try Node.identifier.create(c.arena, m.slice()); + node = try Node.field_access.create(c.arena, .{ .lhs = node, .rhs = ident }); }, .Arrow => { if (m.next().? != .Identifier) { try m.fail(c, "unable to translate C expr: expected identifier", .{}); return error.ParseError; } - const deref = try transCreateNodePtrDeref(c, node); - node = try transCreateNodeFieldAccess(c, deref, m.slice()); - continue; + + const deref = try Node.deref.create(c.arena, node); + const ident = try Node.identifier.create(c.arena, m.slice()); + node = try Node.field_access.create(c.arena, .{ .lhs = deref, .rhs = ident }); }, .LBracket => { - const arr_node = try transCreateNodeArrayAccess(c, node); - arr_node.index_expr = try parseCExpr(c, m, scope); - arr_node.rtoken = try appendToken(c, .RBracket, "]"); - node = &arr_node.base; - if (m.next().? != .RBracket) { - try m.fail(c, "unable to translate C expr: expected ']'", .{}); - return error.ParseError; - } - continue; + const index = try macroBoolToInt(c, try parseCExpr(c, m, scope)); + node = try Node.array_access.create(c.arena, .{ .lhs = node, .rhs = index }); }, .LParen => { - _ = try appendToken(c, .LParen, "("); - var call_params = std.ArrayList(*ast.Node).init(c.gpa); + var call_params = std.ArrayList(Node).init(c.gpa); defer call_params.deinit(); while (true) { const arg = try parseCCondExpr(c, m, scope); try call_params.append(arg); switch (m.next().?) { - .Comma => _ = try appendToken(c, .Comma, ","), + .Comma => {}, .RParen => break, else => { try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{}); @@ -5142,32 +4791,17 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N }, } } - const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len); - call_node.* = .{ - .lhs = node, - .params_len = call_params.items.len, - .async_token = null, - .rtoken = try appendToken(c, .RParen, ")"), - }; - mem.copy(*ast.Node, call_node.params(), call_params.items); - node = &call_node.base; - continue; + node = try Node.call.create(c.arena, .{ .lhs = node, .rhs = try c.arena.dupe(Node, call_params.items) }); }, .LBrace => { - // must come immediately after `node` - _ = try appendToken(c, .Comma, ","); - - const dot = try appendToken(c, .Period, "."); - _ = try appendToken(c, .LBrace, "{"); - - var init_vals = std.ArrayList(*ast.Node).init(c.gpa); + var init_vals = std.ArrayList(Node).init(c.gpa); defer init_vals.deinit(); while (true) { const val = try parseCCondExpr(c, m, scope); try init_vals.append(val); switch (m.next().?) { - .Comma => _ = try appendToken(c, .Comma, ","), + .Comma => {}, .RBrace => break, else => { try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{}); @@ -5175,29 +4809,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N }, } } - const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len); - tuple_node.* = .{ - .dot = dot, - .list_len = init_vals.items.len, - .rtoken = try appendToken(c, .RBrace, "}"), - }; - mem.copy(*ast.Node, tuple_node.list(), init_vals.items); - - //(@import("std").mem.zeroInit(T, .{x})) - const import_fn_call = try c.createBuiltinCall("@import", 1); - const std_node = try transCreateNodeStringLiteral(c, "\"std\""); - import_fn_call.params()[0] = std_node; - import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); - const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); - const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit"); - - const zero_init_call = try c.createCall(outer_field_access, 2); - zero_init_call.params()[0] = node; - zero_init_call.params()[1] = &tuple_node.base; - zero_init_call.rtoken = try appendToken(c, .RParen, ")"); - - node = &zero_init_call.base; - continue; + const tuple_node = try Node.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items)); + node = try Node.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node }); }, .PlusPlus, .MinusMinus => { try m.fail(c, "TODO postfix inc/dec expr", .{}); @@ -5211,35 +4824,31 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N } } -fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { +fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { switch (m.next().?) { .Bang => { - const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); - node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); - return &node.base; + const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); + return Node.not.create(c.arena, operand); }, .Minus => { - const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); - node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - return &node.base; + const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + return Node.negate.create(c.arena, operand); }, .Plus => return try parseCUnaryExpr(c, m, scope), .Tilde => { - const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); - node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - return &node.base; + const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); + return Node.bit_not.create(c.arena, operand); }, .Asterisk => { - const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); - return try transCreateNodePtrDeref(c, node); + const operand = try parseCUnaryExpr(c, m, scope); + return Node.deref.create(c.arena, operand); }, .Ampersand => { - const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); - node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); - return &node.base; + const operand = try parseCUnaryExpr(c, m, scope); + return Node.address_of.create(c.arena, operand); }, .Keyword_sizeof => { - const inner = if (m.peek().? == .LParen) blk: { + const operand = if (m.peek().? == .LParen) blk: { _ = m.next(); // C grammar says this should be 'type-name' but we have to // use parseCMulExpr to correctly handle pointer types. @@ -5251,18 +4860,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod break :blk inner; } else try parseCUnaryExpr(c, m, scope); - //(@import("std").meta.sizeof(dest, x)) - const import_fn_call = try c.createBuiltinCall("@import", 1); - const std_node = try transCreateNodeStringLiteral(c, "\"std\""); - import_fn_call.params()[0] = std_node; - import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); - const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta"); - const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof"); - - const sizeof_call = try c.createCall(outer_field_access, 1); - sizeof_call.params()[0] = inner; - sizeof_call.rtoken = try appendToken(c, .RParen, ")"); - return &sizeof_call.base; + return Node.std_meta_sizeof.create(c.arena, operand); }, .Keyword_alignof => { // TODO this won't work if using 's @@ -5273,16 +4871,13 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod } // C grammar says this should be 'type-name' but we have to // use parseCMulExpr to correctly handle pointer types. - const inner = try parseCMulExpr(c, m, scope); + const operand = try parseCMulExpr(c, m, scope); if (m.next().? != .RParen) { try m.fail(c, "unable to translate C expr: expected ')'", .{}); return error.ParseError; } - const builtin_call = try c.createBuiltinCall("@alignOf", 1); - builtin_call.params()[0] = inner; - builtin_call.rparen_token = try appendToken(c, .RParen, ")"); - return &builtin_call.base; + return Node.alignof.create(c.arena, operand); }, .PlusPlus, .MinusMinus => { try m.fail(c, "TODO unary inc/dec expr", .{}); @@ -5295,50 +4890,40 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Nod } } -fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 { - const tok = c.token_locs.items[token]; - const slice = c.source_buffer.items[tok.start..tok.end]; - return if (mem.startsWith(u8, slice, "@\"")) - slice[2 .. slice.len - 1] - else - slice; -} - -fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { - switch (node.tag) { - .ContainerDecl, - .AddressOf, - .Await, - .BitNot, - .BoolNot, - .OptionalType, - .Negation, - .NegationWrap, - .Resume, - .Try, - .ArrayType, - .ArrayTypeSentinel, - .PtrType, - .SliceType, +fn getContainer(c: *Context, node: Node) ?Node { + switch (node.tag()) { + .@"union", + .@"struct", + .@"enum", + .address_of, + .bit_not, + .not, + .optional_type, + .negate, + .negate_wrap, + .array_type, + .c_pointer, + .single_pointer, => return node, - .Identifier => { - const ident = node.castTag(.Identifier).?; - if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { - if (value.cast(ast.Node.VarDecl)) |var_decl| - return getContainer(c, var_decl.getInitNode().?); + .identifier => { + const ident = node.castTag(.identifier).?; + if (c.global_scope.sym_table.get(ident.data)) |value| { + if (value.castTag(.var_decl)) |var_decl| + return getContainer(c, var_decl.data.init); + if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl| + return getContainer(c, var_decl.data.init); } }, - .Period => { - const infix = node.castTag(.Period).?; + .field_access => { + const infix = node.castTag(.field_access).?; - if (getContainerTypeOf(c, infix.lhs)) |ty_node| { - if (ty_node.cast(ast.Node.ContainerDecl)) |container| { - for (container.fieldsAndDecls()) |field_ref| { - const field = field_ref.cast(ast.Node.ContainerField).?; - const ident = infix.rhs.castTag(.Identifier).?; - if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { + if (getContainerTypeOf(c, infix.data.lhs)) |ty_node| { + if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { + for (container.data.fields) |field| { + const ident = infix.data.rhs.castTag(.identifier).?; + if (mem.eql(u8, field.data.name, field.data)) { return getContainer(c, field.type_expr.?); } } @@ -5351,22 +4936,20 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { return null; } -fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { - if (ref.castTag(.Identifier)) |ident| { - if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { - if (value.cast(ast.Node.VarDecl)) |var_decl| { - if (var_decl.getTypeNode()) |ty| - return getContainer(c, ty); +fn getContainerTypeOf(c: *Context, ref: Node) ?Node { + if (ref.castTag(.identifier)) |ident| { + if (c.global_scope.sym_table.get(ident.data)) |value| { + if (value.castTag(.var_decl)) |var_decl| { + return getContainer(c, var_decl.data.type); } } - } else if (ref.castTag(.Period)) |infix| { - if (getContainerTypeOf(c, infix.lhs)) |ty_node| { - if (ty_node.cast(ast.Node.ContainerDecl)) |container| { - for (container.fieldsAndDecls()) |field_ref| { - const field = field_ref.cast(ast.Node.ContainerField).?; - const ident = infix.rhs.castTag(.Identifier).?; - if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { - return getContainer(c, field.type_expr.?); + } else if (ref.castTag(.field_access)) |infix| { + if (getContainerTypeOf(c, infix.data.lhs)) |ty_node| { + if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { + for (container.data.fields) |field| { + const ident = infix.data.rhs.castTag(.identifier).?; + if (mem.eql(u8, field.name, ident.data)) { + return getContainer(c, field.type); } } } else @@ -5376,11 +4959,16 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { return null; } -fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { - const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getInitNode().? else return null; +fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func { + const init = if (value.castTag(.var_decl)) |v| + v.data.init + else if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |v| + v.data.init + else + return null; if (getContainerTypeOf(c, init)) |ty_node| { - if (ty_node.castTag(.OptionalType)) |prefix| { - if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { + if (ty_node.castTag(.optional_type)) |prefix| { + if (prefix.data.castTag(.func)) |fn_proto| { return fn_proto; } } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index d62e83ea6a..1cf014e5dc 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -47,7 +47,6 @@ pub const Node = extern union { field_access, array_access, call, - std_mem_zeroes, var_decl, func, warning, @@ -57,6 +56,7 @@ pub const Node = extern union { @"struct", @"union", array_init, + tuple, container_init, std_meta_cast, discard, @@ -162,6 +162,8 @@ pub const Node = extern union { deref, block, + /// { operand } + block_single, @"break", sizeof, @@ -173,8 +175,12 @@ pub const Node = extern union { single_pointer, array_type, - /// @import("std").mem.zeroes(T) + /// @import("std").meta.sizeof(operand) + std_meta_sizeof, + /// @import("std").mem.zeroes(operand) std_mem_zeroes, + /// @import("std").mem.zeroInit(lhs, rhs) + std_mem_zeroinit, // pub const name = @compileError(msg); fail_decl, // var actual = mangled; @@ -188,6 +194,9 @@ pub const Node = extern union { /// pub const enum_field_name = @enumToInt(enum_name.field_name); enum_redecl, + /// pub inline fn name(params) return_type body + pub_inline_fn, + /// [0]type{} empty_array, /// [1]type{val} ** count @@ -195,6 +204,7 @@ pub const Node = extern union { /// _ = operand; ignore, + @"anytype", pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -213,6 +223,7 @@ pub const Node = extern union { .one_literal, .void_type, .noreturn_type, + .@"anytype", => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .std_mem_zeroes, @@ -234,6 +245,7 @@ pub const Node = extern union { .if_not_break, .switch_else, .ignore, + .block_single, => Payload.UnOp, .add, @@ -302,6 +314,8 @@ pub const Node = extern union { .field_access, .assign, .align_cast, + .array_access, + .std_mem_zeroinit, => Payload.BinOp, .number_literal, @@ -325,7 +339,7 @@ pub const Node = extern union { .func => Payload.Func, .@"enum" => Payload.Enum, .@"struct", .@"union" => Payload.Record, - .array_init => Payload.ArrayInit, + .array_init, .tuple => Payload.ArrayInit, .container_init => Payload.ContainerInit, .std_meta_cast => Payload.Infix, .block => Payload.Block, @@ -336,6 +350,7 @@ pub const Node = extern union { .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, .array_filler => Payload.ArrayFiller, + .pub_inline_fn => Payload.PubInlineFn, }; } @@ -488,12 +503,12 @@ pub const Payload = struct { body: ?Node, alignment: ?c_uint, }, + }; - pub const Param = struct { - is_noalias: bool, - name: ?[]const u8, - type: Node, - }; + pub const Param = struct { + is_noalias: bool, + name: ?[]const u8, + type: Node, }; pub const Enum = struct { @@ -598,6 +613,16 @@ pub const Payload = struct { count: usize, }, }; + + pub const PubInlineFn = struct { + base: Node, + data: struct { + name: []const u8, + params: []Param, + return_type: Node, + body: Node, + }, + }; }; /// Converts the nodes into a Zig ast. From 2a74a1ebaace8b5de1796b1756f65e421eb479a4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 12 Feb 2021 11:23:15 +0200 Subject: [PATCH 16/36] translate-c: bunch of small fixes to get it compiling --- src/translate_c.zig | 1190 +++++++++++++++++++-------------------- src/translate_c/ast.zig | 108 ++-- src/type.zig | 4 + 3 files changed, 647 insertions(+), 655 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 9ef4788ba7..c7a30ff919 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -10,12 +10,13 @@ const mem = std.mem; const math = std.math; const ast = @import("translate_c/ast.zig"); const Node = ast.Node; +const Tag = Node.Tag; const CallingConvention = std.builtin.CallingConvention; pub const ClangErrMsg = clang.Stage2ErrorMsg; -pub const Error = error{OutOfMemory}; +pub const Error = std.mem.Allocator.Error; const TypeError = Error || error{UnsupportedType}; const TransError = TypeError || error{UnsupportedTranslation}; @@ -30,11 +31,11 @@ const Scope = struct { parent: ?*Scope, const Id = enum { - Switch, - Block, - Root, - Condition, - Loop, + @"switch", + block, + root, + condition, + loop, }; /// Represents an in-progress Node.Switch. This struct is stack-allocated. @@ -44,7 +45,6 @@ const Scope = struct { base: Scope, pending_block: Block, cases: std.ArrayList(Node), - case_index: usize, switch_label: ?[]const u8, default_label: ?[]const u8, }; @@ -84,7 +84,7 @@ const Scope = struct { fn init(c: *Context, parent: *Scope, labeled: bool) !Block { var blk = Block{ .base = .{ - .id = .Block, + .id = .block, .parent = parent, }, .statements = std.ArrayList(Node).init(c.gpa), @@ -105,12 +105,12 @@ const Scope = struct { fn complete(self: *Block, c: *Context) !Node { // We reserve 1 extra statement if the parent is a Loop. This is in case of // do while, we want to put `if (cond) break;` at the end. - const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop); - const stmts = try c.arena.alloc(Node, alloc_len); + const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .loop); + var stmts = try c.arena.alloc(Node, alloc_len); stmts.len -= 1; mem.copy(Node, stmts, self.statements.items); - return Node.block.create(c.arena, .{ - .lable = self.label, + return Tag.block.create(c.arena, .{ + .label = self.label, .stmts = stmts, }); } @@ -161,7 +161,7 @@ const Scope = struct { fn init(c: *Context) Root { return .{ .base = .{ - .id = .Root, + .id = .root, .parent = null, }, .sym_table = SymbolTable.init(c.gpa), @@ -195,9 +195,9 @@ const Scope = struct { var scope = inner; while (true) { switch (scope.id) { - .Root => unreachable, - .Block => return @fieldParentPtr(Block, "base", scope), - .Condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), + .root => unreachable, + .block => return @fieldParentPtr(Block, "base", scope), + .condition => return @fieldParentPtr(Condition, "base", scope).getBlockScope(c), else => scope = scope.parent.?, } } @@ -207,8 +207,8 @@ const Scope = struct { var scope = inner; while (true) { switch (scope.id) { - .Root => unreachable, - .Block => { + .root => unreachable, + .block => { const block = @fieldParentPtr(Block, "base", scope); if (block.return_type) |qt| return qt; scope = scope.parent.?; @@ -220,17 +220,17 @@ const Scope = struct { fn getAlias(scope: *Scope, name: []const u8) []const u8 { return switch (scope.id) { - .Root => return name, - .Block => @fieldParentPtr(Block, "base", scope).getAlias(name), - .Switch, .Loop, .Condition => scope.parent.?.getAlias(name), + .root => return name, + .block => @fieldParentPtr(Block, "base", scope).getAlias(name), + .@"switch", .loop, .condition => scope.parent.?.getAlias(name), }; } fn contains(scope: *Scope, name: []const u8) bool { return switch (scope.id) { - .Root => @fieldParentPtr(Root, "base", scope).contains(name), - .Block => @fieldParentPtr(Block, "base", scope).contains(name), - .Switch, .Loop, .Condition => scope.parent.?.contains(name), + .root => @fieldParentPtr(Root, "base", scope).contains(name), + .block => @fieldParentPtr(Block, "base", scope).contains(name), + .@"switch", .loop, .condition => scope.parent.?.contains(name), }; } @@ -238,9 +238,9 @@ const Scope = struct { var scope = inner; while (true) { switch (scope.id) { - .Root => unreachable, - .Switch => return scope, - .Loop => return scope, + .root => unreachable, + .@"switch" => return scope, + .loop => return scope, else => scope = scope.parent.?, } } @@ -250,24 +250,24 @@ const Scope = struct { var scope = inner; while (true) { switch (scope.id) { - .Root => unreachable, - .Switch => return @fieldParentPtr(Switch, "base", scope), + .root => unreachable, + .@"switch" => return @fieldParentPtr(Switch, "base", scope), else => scope = scope.parent.?, } } } /// Appends a node to the first block scope if inside a function, or to the root tree if not. - fn appendNode(scope: *Scope, node: Node) !void { + fn appendNode(inner: *Scope, node: Node) !void { var scope = inner; while (true) { switch (scope.id) { - .Root => { - const root = @fieldParentPtr(Root, "base", scope).contains(name); + .root => { + const root = @fieldParentPtr(Root, "base", scope); return root.nodes.append(node); }, - .Block => { - const block = @fieldParentPtr(Block, "base", scope).contains(name); + .block => { + const block = @fieldParentPtr(Block, "base", scope); return block.statements.append(node); }, else => scope = scope.parent.?, @@ -321,7 +321,7 @@ pub fn translate( args_end: [*]?[*]const u8, errors: *[]ClangErrMsg, resources_path: [*:0]const u8, -) !ast.Tree { +) !std.zig.ast.Tree { const ast_unit = clang.LoadFromCommandLine( args_begin, args_end, @@ -339,14 +339,6 @@ pub fn translate( var arena = std.heap.ArenaAllocator.init(gpa); errdefer arena.deinit(); - if (true) { - var x = false; - if (x) { - return error.OutOfMemory; - } - @panic("TODO update translate-c"); - } - var context = Context{ .gpa = gpa, .arena = &arena.allocator, @@ -361,15 +353,15 @@ pub fn translate( context.alias_list.deinit(); context.global_names.deinit(gpa); context.opaque_demotes.deinit(gpa); - context.global_scope.deini(); + context.global_scope.deinit(); } - try context.global_scope.nodes.append(try Node.usingnamespace_builtins.init()); + try context.global_scope.nodes.append(Tag.usingnamespace_builtins.init()); try prepopulateGlobalNameTable(ast_unit, &context); if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) { - return context.err; + return error.OutOfMemory; } try transPreprocessorEntities(&context, ast_unit); @@ -377,16 +369,17 @@ pub fn translate( try addMacros(&context); for (context.alias_list.items) |alias| { if (!context.global_scope.sym_table.contains(alias.alias)) { - try createAlias(&context, alias); + const node = try Tag.alias.create(context.arena, .{ .actual = alias.alias, .mangled = alias.name }); + try addTopLevelDecl(&context, alias.alias, node); } } - return ast.render(context.global_scope.nodes.items); + return ast.render(gpa, context.global_scope.nodes.items); } fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) { - return c.err; + return error.OutOfMemory; } // TODO if we see #undef, delete it from the table @@ -409,19 +402,13 @@ fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void { fn declVisitorNamesOnlyC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool { const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); - declVisitorNamesOnly(c, decl) catch |err| { - c.err = err; - return false; - }; + declVisitorNamesOnly(c, decl) catch return false; return true; } fn declVisitorC(context: ?*c_void, decl: *const clang.Decl) callconv(.C) bool { const c = @ptrCast(*Context, @alignCast(@alignOf(Context), context)); - declVisitor(c, decl) catch |err| { - c.err = err; - return false; - }; + declVisitor(c, decl) catch return false; return true; } @@ -454,7 +441,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { }, else => { const decl_name = try c.str(decl.getDeclKindName()); - try warn(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); + try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); }, } } @@ -513,7 +500,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { decl_ctx.has_body = false; decl_ctx.storage_class = .Extern; decl_ctx.is_export = false; - try warn(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{}); + try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{}); } break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { error.UnsupportedType => { @@ -535,7 +522,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }; if (!decl_ctx.has_body) { - return addTopLevelDecl(c, fn_name, &proto_node.base); + return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); } // actual function definition with body @@ -547,10 +534,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { var scope = &block_scope.base; var param_id: c_uint = 0; - for (proto_node.params()) |*param, i| { - const param_name = if (param.name_token) |name_tok| - tokenSlice(c, name_tok) - else + for (proto_node.data.params) |*param, i| { + const param_name = param.name orelse return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name}); const c_param = fn_decl.getParamDecl(param_id); @@ -565,7 +550,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const arg_name = try block_scope.makeMangledName(c, bare_arg_name); param.name = arg_name; - const redecl_node = try Node.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); + const redecl_node = try Tag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name }); try block_scope.statements.append(redecl_node); } @@ -607,12 +592,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { error.UnsupportedType, => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}), }; - const ret = try Node.@"return".create(c.arena, rhs); + const ret = try Tag.@"return".create(c.arena, rhs); try block_scope.statements.append(ret); } - proto_node.body = try block_scope.complete(c); - return addTopLevelDecl(c, fn_name, &proto_node.base); + proto_node.data.body = try block_scope.complete(c); + return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); } fn transQualTypeMaybeInitialized(c: *Context, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node { @@ -668,7 +653,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0) else - transExprCoercing(c, scope, expr, .used, .r_value); + transExprCoercing(c, scope, expr, .used); init_node = node_or_error catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType, @@ -677,18 +662,18 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co }, error.OutOfMemory => |e| return e, }; - if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { - init_node = try Node.bool_to_int.create(c.arena, init_node); + if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) { + init_node = try Tag.bool_to_int.create(c.arena, init_node.?); } } else { - init_node = Node.undefined_literal.init(); + init_node = Tag.undefined_literal.init(); } } else if (storage_class != .Extern) { // The C language specification states that variables with static or threadlocal // storage without an initializer are initialized to a zero value. // @import("std").mem.zeroes(T) - init_node = try Node.std_mem_zeroes.create(c.arena, type_node); + init_node = try Tag.std_mem_zeroes.create(c.arena, type_node); } const linksection_string = blk: { @@ -708,7 +693,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co break :blk null; }; - const node = try Node.var_decl.create(c.arena, .{ + const node = try Tag.var_decl.create(c.arena, .{ .is_pub = is_pub, .is_const = is_const, .is_extern = is_extern, @@ -719,12 +704,12 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co .type = type_node, .init = init_node, }); - return addTopLevelDecl(c, checked_name, &node.base); + return addTopLevelDecl(c, checked_name, node); } fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !Node { _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name); - return Node.identifier.create(c.arena, builtin_name); + return Tag.identifier.create(c.arena, builtin_name); } const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ @@ -744,7 +729,7 @@ const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?Node { if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name| - return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice + return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); @@ -753,17 +738,17 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name; if (builtin_typedef_map.get(checked_name)) |builtin| { _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin); - return Node.identifier.create(c.arena, builtin); + return try Tag.identifier.create(c.arena, builtin); } if (!top_level_visit) { - return transCreateNodeIdentifier(c, checked_name); + return try Tag.identifier.create(c.arena, checked_name); } _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name); const node = (try transCreateNodeTypedef(c, typedef_decl, true, checked_name)) orelse return null; try addTopLevelDecl(c, checked_name, node); - return transCreateNodeIdentifier(c, checked_name); + return try Tag.identifier.create(c.arena, checked_name); } fn transCreateNodeTypedef( @@ -782,9 +767,9 @@ fn transCreateNodeTypedef( error.OutOfMemory => |e| return e, }; - const payload = try c.arena.create(ast.Payload.Typedef); + const payload = try c.arena.create(ast.Payload.SimpleVarDecl); payload.* = .{ - .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[@boolToInt(toplevel)] }, + .base = .{ .tag = ([2]Tag{ .typedef, .pub_typedef })[@boolToInt(toplevel)] }, .data = .{ .name = checked_name, .init = init_node, @@ -795,7 +780,7 @@ fn transCreateNodeTypedef( fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Node { if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| - return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice + return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice const record_loc = record_decl.getLocation(); var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); @@ -815,7 +800,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod } else if (record_decl.isStruct()) { container_kind_name = "struct"; } else { - try warn(c, record_loc, "record {s} is not a struct or union", .{bare_name}); + try warn(c, &c.global_scope.base, record_loc, "record {s} is not a struct or union", .{bare_name}); return null; } @@ -826,7 +811,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod const init_node = blk: { const record_def = record_decl.getDefinition() orelse { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - break :blk Node.opaque_literal.init(); + break :blk Tag.opaque_literal.init(); }; const is_packed = record_decl.getPackedAttribute(); @@ -843,14 +828,14 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod if (field_decl.isBitField()) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); - break :blk Node.opaque_literal.init(); + try warn(c, &c.global_scope.base, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); + break :blk Tag.opaque_literal.init(); } if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); - break :blk Node.opaque_literal.init(); + try warn(c, &c.global_scope.base, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); + break :blk Tag.opaque_literal.init(); } var is_anon = false; @@ -864,8 +849,8 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod const field_type = transQualType(c, field_qt, field_loc) catch |err| switch (err) { error.UnsupportedType => { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); - break :blk Node.opaque_literal.init(); + try warn(c, &c.global_scope.base, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name }); + break :blk Tag.opaque_literal.init(); }, else => |e| return e, }; @@ -890,20 +875,20 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod }); } - const payload = try c.arena.create(ast.Payload.Record); - container_node.* = .{ - .base = .{ .tag = ([2]ast.Node.Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, + const record_payload = try c.arena.create(ast.Payload.Record); + record_payload.* = .{ + .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] }, .data = .{ .is_packed = is_packed, .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items), }, }; - break :blk Node.initPayload(&container_node.base); + break :blk Node.initPayload(&record_payload.base); }; const payload = try c.arena.create(ast.Payload.SimpleVarDecl); payload.* = .{ - .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, + .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, .data = .{ .name = name, .init = init_node, @@ -913,12 +898,12 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); if (!is_unnamed) try c.alias_list.append(.{ .alias = bare_name, .name = name }); - return Node.identifier.create(c.arena, name); + return try Tag.identifier.create(c.arena, name); } fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name| - return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice + return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice const enum_loc = enum_decl.getLocation(); var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); @@ -965,7 +950,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { else => |e| return e, } else - try Node.type.create(c.arena, "c_int"); + try Tag.type.create(c.arena, "c_int"); it = enum_def.enumerator_begin(); end_it = enum_def.enumerator_end(); @@ -983,29 +968,29 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { else null; - try fields_and_decls.append(.{ + try fields.append(.{ .name = field_name, .value = int_node, }); // In C each enum value is in the global namespace. So we put them there too. // At this point we can rely on the enum emitting successfully. - try addTopLevelDecl(c, field_name, try Node.enum_redecl.create(c.arena, .{ + try addTopLevelDecl(c, field_name, try Tag.enum_redecl.create(c.arena, .{ .enum_val_name = enum_val_name, .field_name = field_name, .enum_name = name, })); } - break :blk try Node.@"enum".create(c.arena, try c.arena.dupe(ast.Payload.Enum.Field, fields.items)); + break :blk try Tag.@"enum".create(c.arena, try c.arena.dupe(ast.Payload.Enum.Field, fields.items)); } else blk: { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {}); - break :blk Node.opaque_literal.init(); + break :blk Tag.opaque_literal.init(); }; const payload = try c.arena.create(ast.Payload.SimpleVarDecl); payload.* = .{ - .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, + .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] }, .data = .{ .name = name, .init = init_node, @@ -1015,7 +1000,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); if (!is_unnamed) try c.alias_list.append(.{ .alias = bare_name, .name = name }); - return transCreateNodeIdentifier(c, name); + return try Tag.identifier.create(c.arena, name); } const ResultUsed = enum { @@ -1023,31 +1008,25 @@ const ResultUsed = enum { unused, }; -const LRValue = enum { - l_value, - r_value, -}; - fn transStmt( c: *Context, scope: *Scope, stmt: *const clang.Stmt, result_used: ResultUsed, - lrvalue: LRValue, ) TransError!Node { const sc = stmt.getStmtClass(); switch (sc) { .BinaryOperatorClass => return transBinaryOperator(c, scope, @ptrCast(*const clang.BinaryOperator, stmt), result_used), .CompoundStmtClass => return transCompoundStmt(c, scope, @ptrCast(*const clang.CompoundStmt, stmt)), - .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used, lrvalue), + .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @ptrCast(*const clang.CStyleCastExpr, stmt), result_used), .DeclStmtClass => return transDeclStmt(c, scope, @ptrCast(*const clang.DeclStmt, stmt)), - .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt), lrvalue), + .DeclRefExprClass => return transDeclRefExpr(c, scope, @ptrCast(*const clang.DeclRefExpr, stmt)), .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @ptrCast(*const clang.ImplicitCastExpr, stmt), result_used), .IntegerLiteralClass => return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, stmt), result_used, .with_as), .ReturnStmtClass => return transReturnStmt(c, scope, @ptrCast(*const clang.ReturnStmt, stmt)), .StringLiteralClass => return transStringLiteral(c, scope, @ptrCast(*const clang.StringLiteral, stmt), result_used), .ParenExprClass => { - const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used, lrvalue); + const expr = try transExpr(c, scope, @ptrCast(*const clang.ParenExpr, stmt).getSubExpr(), .used); return maybeSuppressResult(c, scope, result_used, expr); }, .InitListExprClass => return transInitListExpr(c, scope, @ptrCast(*const clang.InitListExpr, stmt), result_used), @@ -1056,9 +1035,9 @@ fn transStmt( .WhileStmtClass => return transWhileLoop(c, scope, @ptrCast(*const clang.WhileStmt, stmt)), .DoStmtClass => return transDoWhileLoop(c, scope, @ptrCast(*const clang.DoStmt, stmt)), .NullStmtClass => { - return Node.empty_block.init(); + return Tag.empty_block.init(); }, - .ContinueStmtClass => return try transCreateNodeContinue(c), + .ContinueStmtClass => return Tag.@"continue".init(), .BreakStmtClass => return transBreak(c, scope), .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), @@ -1083,12 +1062,12 @@ fn transStmt( .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @ptrCast(*const clang.CompoundAssignOperator, stmt), result_used), .OpaqueValueExprClass => { const source_expr = @ptrCast(*const clang.OpaqueValueExpr, stmt).getSourceExpr().?; - const expr = try transExpr(c, scope, source_expr, .used, lrvalue); + const expr = try transExpr(c, scope, source_expr, .used); return maybeSuppressResult(c, scope, result_used, expr); }, else => { return fail( - rp, + c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", @@ -1109,37 +1088,36 @@ fn transBinaryOperator( switch (op) { .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()), .Comma => { - var block_scope = try Scope.Block.init(rp.c, scope, true); + var block_scope = try Scope.Block.init(c, scope, true); defer block_scope.deinit(); - - const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused, .r_value); + const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused); try block_scope.statements.append(lhs); - const rhs = try transExpr(rp, &block_scope.base, stmt.getRHS(), .used, .r_value); - const break_node = try Node.break_val.create(c.arena, .{ + const rhs = try transExpr(c, &block_scope.base, stmt.getRHS(), .used); + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, .val = rhs, }); try block_scope.statements.append(break_node); - const block_node = try block_scope.complete(rp.c); - return maybeSuppressResult(rp, scope, result_used, block_node); + const block_node = try block_scope.complete(c); + return maybeSuppressResult(c, scope, result_used, block_node); }, .Div => { if (cIsSignedInteger(qt)) { // signed integer division uses @divTrunc - const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); - const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); - const div_trunc = try Node.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + const lhs = try transExpr(c, scope, stmt.getLHS(), .used); + const rhs = try transExpr(c, scope, stmt.getRHS(), .used); + const div_trunc = try Tag.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); return maybeSuppressResult(c, scope, result_used, div_trunc); } }, .Rem => { if (cIsSignedInteger(qt)) { // signed integer division uses @rem - const lhs = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); - const rhs = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); - const rem = try Node.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + const lhs = try transExpr(c, scope, stmt.getLHS(), .used); + const rhs = try transExpr(c, scope, stmt.getRHS(), .used); + const rem = try Tag.rem.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); return maybeSuppressResult(c, scope, result_used, rem); } }, @@ -1150,14 +1128,14 @@ fn transBinaryOperator( return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used); }, .LAnd => { - return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_and, result_used); + return transCreateNodeBoolInfixOp(c, scope, stmt, .@"and", result_used); }, .LOr => { - return transCreateNodeBoolInfixOp(c, scope, stmt, .bool_or, result_used); + return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used); }, else => {}, } - var op_id: Node.Tag = undefined; + var op_id: Tag = undefined; switch (op) { .Add => { if (cIsUnsignedInteger(qt)) { @@ -1218,20 +1196,20 @@ fn transBinaryOperator( else => unreachable, } - const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used, .l_value); - const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used, .r_value); + const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used); + const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used); const lhs = if (isBoolRes(lhs_uncasted)) - try Node.bool_to_int.create(c.arena, lhs_uncasted) + try Tag.bool_to_int.create(c.arena, lhs_uncasted) else lhs_uncasted; const rhs = if (isBoolRes(rhs_uncasted)) - try Node.bool_to_int.create(c.arena, rhs_uncasted) + try Tag.bool_to_int.create(c.arena, rhs_uncasted) else rhs_uncasted; - return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, used); + return transCreateNodeInfixOp(c, scope, op_id, lhs, rhs, result_used); } fn transCompoundStmtInline( @@ -1243,7 +1221,7 @@ fn transCompoundStmtInline( var it = stmt.body_begin(); const end_it = stmt.body_end(); while (it != end_it) : (it += 1) { - const result = try transStmt(c, parent_scope, it[0], .unused, .r_value); + const result = try transStmt(c, parent_scope, it[0], .unused); try block.statements.append(result); } } @@ -1260,7 +1238,6 @@ fn transCStyleCastExprClass( scope: *Scope, stmt: *const clang.CStyleCastExpr, result_used: ResultUsed, - lrvalue: LRValue, ) TransError!Node { const sub_expr = stmt.getSubExpr(); const cast_node = (try transCCast( @@ -1269,7 +1246,7 @@ fn transCStyleCastExprClass( stmt.getBeginLoc(), stmt.getType(), sub_expr.getType(), - try transExpr(c, scope, sub_expr, .used, lrvalue), + try transExpr(c, scope, sub_expr, .used), )); return maybeSuppressResult(c, scope, result_used, cast_node); } @@ -1294,7 +1271,7 @@ fn transDeclStmtOne( // This is actually a global variable, put it in the global scope and reference it. // `_ = mangled_name;` try visitVarDecl(c, var_decl, mangled_name); - return try maybeSuppressResult(c, scope, .unused, try Node.identifier.create(c.arena, mangled_name)); + return try maybeSuppressResult(c, scope, .unused, try Tag.identifier.create(c.arena, mangled_name)); }, else => {}, } @@ -1308,13 +1285,13 @@ fn transDeclStmtOne( if (expr.getStmtClass() == .StringLiteralClass) try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node)) else - try transExprCoercing(c, scope, expr, .used, .r_value) + try transExprCoercing(c, scope, expr, .used) else - try transCreateNodeUndefinedLiteral(c); + Tag.undefined_literal.init(); if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { - init_node = try Node.bool_to_int.create(c.arena, init_node); + init_node = try Tag.bool_to_int.create(c.arena, init_node); } - return Node.var_decl.create(c.arena, .{ + return Tag.var_decl.create(c.arena, .{ .is_pub = false, .is_const = is_const, .is_extern = false, @@ -1339,7 +1316,7 @@ fn transDeclStmtOne( return node; }, else => |kind| return fail( - rp, + c, error.UnsupportedTranslation, decl.getLocation(), "TODO implement translation of DeclStmt kind {s}", @@ -1370,12 +1347,11 @@ fn transDeclRefExpr( c: *Context, scope: *Scope, expr: *const clang.DeclRefExpr, - lrvalue: LRValue, ) TransError!Node { const value_decl = expr.getDecl(); const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin()); const mangled_name = scope.getAlias(name); - return Node.identifier.create(c.arena, mangled_name); + return Tag.identifier.create(c.arena, mangled_name); } fn transImplicitCastExpr( @@ -1389,49 +1365,49 @@ fn transImplicitCastExpr( const src_type = getExprQualType(c, sub_expr); switch (expr.getCastKind()) { .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => { - const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used); const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node); return maybeSuppressResult(c, scope, result_used, casted); }, .LValueToRValue, .NoOp, .FunctionToPointerDecay => { - const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used); return maybeSuppressResult(c, scope, result_used, sub_expr_node); }, .ArrayToPointerDecay => { if (exprIsNarrowStringLiteral(sub_expr)) { - const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used); return maybeSuppressResult(c, scope, result_used, sub_expr_node); } - const addr = try Node.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used, .r_value)); + const addr = try Tag.address_of.create(c.arena, try transExpr(c, scope, sub_expr, .used)); return maybeSuppressResult(c, scope, result_used, addr); }, .NullToPointer => { - return Node.null_literal.init(); + return Tag.null_literal.init(); }, .PointerToBoolean => { // @ptrToInt(val) != 0 - const ptr_to_int = try Node.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used, .r_value)); + const ptr_to_int = try Tag.ptr_to_int.create(c.arena, try transExpr(c, scope, sub_expr, .used)); - const ne = try Node.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Node.zero_literal.init() }); + const ne = try Tag.not_equal.create(c.arena, .{ .lhs = ptr_to_int, .rhs = Tag.zero_literal.init() }); return maybeSuppressResult(c, scope, result_used, ne); }, .IntegralToBoolean => { - const sub_expr_node = try transExpr(c, scope, sub_expr, .used, .r_value); + const sub_expr_node = try transExpr(c, scope, sub_expr, .used); // The expression is already a boolean one, return it as-is if (isBoolRes(sub_expr_node)) return maybeSuppressResult(c, scope, result_used, sub_expr_node); // val != 0 - const ne = try Node.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Node.zero_literal.init() }); + const ne = try Tag.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Tag.zero_literal.init() }); return maybeSuppressResult(c, scope, result_used, ne); }, .BuiltinFnToFnPtr => { - return transExpr(rp, scope, sub_expr, result_used, .r_value); + return transExpr(c, scope, sub_expr, result_used); }, else => |kind| return fail( - rp, + c, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, expr).getBeginLoc(), "TODO implement translation of CastKind {s}", @@ -1445,17 +1421,16 @@ fn transBoolExpr( scope: *Scope, expr: *const clang.Expr, used: ResultUsed, - lrvalue: LRValue, ) TransError!Node { if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) { var is_zero: bool = undefined; if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) { return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{}); } - return Node{ .tag = ([2]ast.Node.Tag{ .true_literal, .false_literal })[@boolToInt(is_zero)] }; + return Node{ .tag_if_small_enough = @enumToInt(([2]Tag{ .true_literal, .false_literal })[@boolToInt(is_zero)]) }; } - var res = try transExpr(c, scope, expr, used, lrvalue); + var res = try transExpr(c, scope, expr, used); if (isBoolRes(res)) { return maybeSuppressResult(c, scope, used, res); } @@ -1494,7 +1469,7 @@ fn isBoolRes(res: Node) bool { .@"or", .@"and", .equal, - .note_equal, + .not_equal, .less_than, .less_than_equal, .greater_than, @@ -1547,18 +1522,18 @@ fn finishBoolExpr( .Float16, => { // node != 0 - return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() }); + return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); }, .NullPtr => { // node == null - return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init() }); + return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); }, else => {}, } }, .Pointer => { // node == null - return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init() }); + return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); }, .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); @@ -1568,8 +1543,7 @@ fn finishBoolExpr( }, .Enum => { // node != 0 - return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() }); - const op_token = try appendToken(c, .BangEqual, "!="); + return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); }, .Elaborated => { const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); @@ -1614,9 +1588,10 @@ fn transIntegerLiteral( // But the first step is to be correct, and the next step is to make the output more elegant. // @as(T, x) + const expr_base = @ptrCast(*const clang.Expr, expr); const ty_node = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()); const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt()); - const as = try Node.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs }); + const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs }); return maybeSuppressResult(c, scope, result_used, as); } @@ -1624,16 +1599,16 @@ fn transReturnStmt( c: *Context, scope: *Scope, expr: *const clang.ReturnStmt, -) TransError!*ast.Node { +) TransError!Node { const val_expr = expr.getRetValue() orelse - return Node.return_void.init(); + return Tag.return_void.init(); - var rhs = try transExprCoercing(c, scope, val_expr, .used, .r_value); + var rhs = try transExprCoercing(c, scope, val_expr, .used); const return_qt = scope.findBlockReturnType(c); if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) { - rhs = try Node.bool_to_int.create(c.arena, rhs); + rhs = try Tag.bool_to_int.create(c.arena, rhs); } - return Node.@"return".create(c.arena, rhs); + return Tag.@"return".create(c.arena, rhs); } fn transStringLiteral( @@ -1647,10 +1622,9 @@ fn transStringLiteral( .Ascii, .UTF8 => { var len: usize = undefined; const bytes_ptr = stmt.getString_bytes_begin_size(&len); - const str = bytes_ptr[0..len]; - const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(str)}); - const node = try Node.string_literal.create(c.arena, str); + const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])}); + const node = try Tag.string_literal.create(c.arena, str); return maybeSuppressResult(c, scope, result_used, node); }, .UTF16, .UTF32, .Wide => { @@ -1658,9 +1632,9 @@ fn transStringLiteral( const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() }); const lit_array = try transStringLiteralAsArray(c, scope, stmt, stmt.getLength() + 1); - const decl = try Node.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); - try scope.appendNode(name, decl); - const node = try Node.identifier.create(c.arena, name); + const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array }); + try scope.appendNode(decl); + const node = try Tag.identifier.create(c.arena, name); return maybeSuppressResult(c, scope, result_used, node); }, } @@ -1669,9 +1643,7 @@ fn transStringLiteral( /// Parse the size of an array back out from an ast Node. fn zigArraySize(c: *Context, node: Node) TransError!usize { if (node.castTag(.array_type)) |array| { - if (array.data.len.castTag(.int_literal)) |int_lit| { - return std.fmt.parseUnsigned(usize, int_lit.data, 10) catch error.UnsupportedTranslation; - } + return array.data.len; } return error.UnsupportedTranslation; } @@ -1709,7 +1681,7 @@ fn transStringLiteralAsArray( init_list[i] = try transCreateNodeNumber(c, 0); } - return Node.array_init.create(c.arena, init_list); + return Tag.array_init.create(c.arena, init_list); } fn cIsEnum(qt: clang.QualType) bool { @@ -1747,89 +1719,77 @@ fn transCCast( // 3. Bit-cast to correct signed-ness const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type); const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type); - var src_int_expr = if (cIsInteger(src_type)) expr else Node.enum_to_int.create(c.arena, expr); + var src_int_expr = if (cIsInteger(src_type)) expr else try Tag.enum_to_int.create(c.arena, expr); if (isBoolRes(src_int_expr)) { - src_int_expr = try Node.bool_to_int.create(c.arena, src_int_expr); + src_int_expr = try Tag.bool_to_int.create(c.arena, src_int_expr); } switch (cIntTypeCmp(dst_type, src_int_type)) { .lt => { // @truncate(SameSignSmallerInt, src_int_expr) const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); - src_int_expr = try Node.truncate.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); + src_int_expr = try Tag.truncate.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); }, .gt => { // @as(SameSignBiggerInt, src_int_expr) const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed); - src_int_expr = try Node.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); + src_int_expr = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr }); }, .eq => { // src_int_expr = src_int_expr }, } // @bitCast(dest_type, intermediate_value) - return Node.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr }); + return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr }); } if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { // @intCast(dest_type, @ptrToInt(val)) - const ptr_to_int = try Node.ptr_to_int.create(c.arena, expr); - return Node.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int }); + const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); + return Tag.int_cast.create(c.arena, .{ .lhs = dst_node, .rhs = ptr_to_int }); } if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) { // @intToPtr(dest_type, val) - return Node.int_to_ptr.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + return Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsFloating(src_type) and cIsFloating(dst_type)) { // @floatCast(dest_type, val) - return Node.float_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + return Tag.float_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsFloating(src_type) and !cIsFloating(dst_type)) { // @floatToInt(dest_type, val) - return Node.float_to_int.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + return Tag.float_to_int.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (!cIsFloating(src_type) and cIsFloating(dst_type)) { // @intToFloat(dest_type, val) - return Node.int_to_float.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + return Tag.int_to_float.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) { // @boolToInt returns either a comptime_int or a u1 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast // instead of @as - const bool_to_int = Node.bool_to_int.create(c.arena, expr); - return Node.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); + const bool_to_int = try Tag.bool_to_int.create(c.arena, expr); + return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); } if (cIsEnum(dst_type)) { // @intToEnum(dest_type, val) - return Node.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsEnum(src_type) and !cIsEnum(dst_type)) { // @enumToInt(val) - return Node.enum_to_int.create(c.arena, expr); + return Tag.enum_to_int.create(c.arena, expr); } // @as(dest_type, val) - return Node.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } -fn transExpr( - c: *Context, - scope: *Scope, - expr: *const clang.Expr, - used: ResultUsed, - lrvalue: LRValue, -) TransError!Node { - return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used, lrvalue); +fn transExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { + return transStmt(c, scope, @ptrCast(*const clang.Stmt, expr), used); } /// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore /// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals. -fn transExprCoercing( - c: *Context, - scope: *Scope, - expr: *const clang.Expr, - used: ResultUsed, - lrvalue: LRValue, -) TransError!Node { +fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { .IntegerLiteralClass => { return transIntegerLiteral(c, scope, @ptrCast(*const clang.IntegerLiteral, expr), .used, .no_as); @@ -1840,12 +1800,12 @@ fn transExprCoercing( .UnaryOperatorClass => { const un_expr = @ptrCast(*const clang.UnaryOperator, expr); if (un_expr.getOpcode() == .Extension) { - return transExprCoercing(c, scope, un_expr.getSubExpr(), used, lrvalue); + return transExprCoercing(c, scope, un_expr.getSubExpr(), used); } }, else => {}, } - return transExpr(c, scope, expr, .used, .r_value); + return transExpr(c, scope, expr, .used); } fn transInitListExprRecord( @@ -1896,11 +1856,11 @@ fn transInitListExprRecord( try field_inits.append(.{ .name = raw_name, - .value = try transExpr(c, scope, elem_expr, .used, .r_value), + .value = try transExpr(c, scope, elem_expr, .used), }); } - return Node.container_init.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items)); + return Tag.container_init.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items)); } fn transInitListExprArray( @@ -1920,18 +1880,18 @@ fn transInitListExprArray( const leftover_count = all_count - init_count; if (all_count == 0) { - return Node.empty_array.create(c.arena, try transQualType(c, child_qt, source_loc)); + return Tag.empty_array.create(c.arena, try transQualType(c, child_qt, loc)); } - const ty_node = try transType(ty); + const ty_node = try transType(c, ty, loc); const init_node = if (init_count != 0) blk: { const init_list = try c.arena.alloc(Node, init_count); for (init_list) |*init, i| { - const elem_expr = expr.getInit(i); - init.* = try transExpr(c, scope, elem_expr, .used, .r_value); + const elem_expr = expr.getInit(@intCast(c_uint, i)); + init.* = try transExpr(c, scope, elem_expr, .used); } - const init_node = try Node.array_init.create(c.arena, init_list); + const init_node = try Tag.array_init.create(c.arena, init_list); if (leftover_count == 0) { return init_node; } @@ -1939,14 +1899,14 @@ fn transInitListExprArray( } else null; const filler_val_expr = expr.getArrayFiller(); - const filler_node = try Node.array_filler.create(c.arena, .{ + const filler_node = try Tag.array_filler.create(c.arena, .{ .type = ty_node, - .filler = try transExpr(c, scope, filler_val_expr, .used, .r_value), + .filler = try transExpr(c, scope, filler_val_expr, .used), .count = leftover_count, }); if (init_node) |some| { - return Node.array_cat.create(c.arena, some, filler_node); + return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node }); } else { return filler_node; } @@ -1964,7 +1924,7 @@ fn transInitListExpr( if (qual_type.isRecordType()) { return maybeSuppressResult(c, scope, used, try transInitListExprRecord( - rp, + c, scope, source_loc, expr, @@ -1972,7 +1932,7 @@ fn transInitListExpr( )); } else if (qual_type.isArrayType()) { return maybeSuppressResult(c, scope, used, try transInitListExprArray( - rp, + c, scope, source_loc, expr, @@ -1994,7 +1954,7 @@ fn transZeroInitExpr( .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); switch (builtin_ty.getKind()) { - .Bool => return Node.false_literal.init(), + .Bool => return Tag.false_literal.init(), .Char_U, .UChar, .Char_S, @@ -2015,11 +1975,11 @@ fn transZeroInitExpr( .Float128, .Float16, .LongDouble, - => return Node.zero_literal.init(), + => return Tag.zero_literal.init(), else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}), } }, - .Pointer => return Node.null_literal.init(), + .Pointer => return Tag.null_literal.init(), .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); const typedef_decl = typedef_ty.getDecl(); @@ -2058,19 +2018,19 @@ fn transIfStmt( var cond_scope = Scope.Condition{ .base = .{ .parent = scope, - .id = .Condition, + .id = .condition, }, }; defer cond_scope.deinit(); const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); - const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used, .r_value); + const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); - const then_body = try transStmt(c, scope, stmt.getThen(), .unused, .r_value); + const then_body = try transStmt(c, scope, stmt.getThen(), .unused); const else_body = if (stmt.getElse()) |expr| - try transStmt(c, scope, expr, .unused, .r_value) + try transStmt(c, scope, expr, .unused) else null; - return Node.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); + return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body }); } fn transWhileLoop( @@ -2081,19 +2041,19 @@ fn transWhileLoop( var cond_scope = Scope.Condition{ .base = .{ .parent = scope, - .id = .Condition, + .id = .condition, }, }; defer cond_scope.deinit(); const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond()); - const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used, .r_value); + const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); var loop_scope = Scope{ .parent = scope, - .id = .Loop, + .id = .loop, }; - const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); - return Node.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); + const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused); + return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null }); } fn transDoWhileLoop( @@ -2103,20 +2063,19 @@ fn transDoWhileLoop( ) TransError!Node { var loop_scope = Scope{ .parent = scope, - .id = .Loop, + .id = .loop, }; // if (!cond) break; - const if_node = try transCreateNodeIf(c); var cond_scope = Scope.Condition{ .base = .{ .parent = scope, - .id = .Condition, + .id = .condition, }, }; defer cond_scope.deinit(); - const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used, .r_value); - const if_not_break = try Node.if_not_break.create(c.arena, cond); + const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); + const if_not_break = try Tag.if_not_break.create(c.arena, cond); const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. @@ -2129,8 +2088,8 @@ fn transDoWhileLoop( // zig: b; // zig: if (!cond) break; // zig: } - const node = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); - const block = node.castTag(.block); + const node = try transStmt(c, &loop_scope, stmt.getBody(), .unused); + const block = node.castTag(.block).?; block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete. block.data.stmts[block.data.stmts.len - 1] = if_not_break; break :blk node; @@ -2143,12 +2102,12 @@ fn transDoWhileLoop( // zig: a; // zig: if (!cond) break; // zig: } - const statements = try c.arena.create(Node, 2); - statements[0] = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); + const statements = try c.arena.alloc(Node, 2); + statements[0] = try transStmt(c, &loop_scope, stmt.getBody(), .unused); statements[1] = if_not_break; - break :blk try Node.block.create(c.arena, .{ .label = null, .stmts = statements }); + break :blk try Tag.block.create(c.arena, .{ .label = null, .stmts = statements }); }; - return Node.while_true.create(c.arena, body_node); + return Tag.while_true.create(c.arena, body_node); } fn transForLoop( @@ -2158,7 +2117,7 @@ fn transForLoop( ) TransError!Node { var loop_scope = Scope{ .parent = scope, - .id = .Loop, + .id = .loop, }; var block_scope: ?Scope.Block = null; @@ -2167,29 +2126,29 @@ fn transForLoop( if (stmt.getInit()) |init| { block_scope = try Scope.Block.init(c, scope, false); loop_scope.parent = &block_scope.?.base; - const init_node = try transStmt(c, &block_scope.?.base, init, .unused, .r_value); + const init_node = try transStmt(c, &block_scope.?.base, init, .unused); try block_scope.?.statements.append(init_node); } var cond_scope = Scope.Condition{ .base = .{ .parent = &loop_scope, - .id = .Condition, + .id = .condition, }, }; defer cond_scope.deinit(); const cond = if (stmt.getCond()) |cond| - try transBoolExpr(c, &cond_scope.base, cond, .used, .r_value) + try transBoolExpr(c, &cond_scope.base, cond, .used) else - Node.true_literal.init(); + Tag.true_literal.init(); const cont_expr = if (stmt.getInc()) |incr| - try transExpr(c, &cond_scope.base, incr, .unused, .r_value) + try transExpr(c, &cond_scope.base, incr, .unused) else null; - const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused, .r_value); - const while_node = try Node.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); + const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused); + const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr }); if (block_scope) |*bs| { try bs.statements.append(while_node); return try bs.complete(c); @@ -2206,13 +2165,14 @@ fn transSwitch( var cond_scope = Scope.Condition{ .base = .{ .parent = scope, - .id = .Condition, + .id = .condition, }, }; defer cond_scope.deinit(); - const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used, .r_value); + const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used); const switch_node = try c.arena.create(ast.Payload.Switch); switch_node.* = .{ + .base = .{ .tag = .@"switch" }, .data = .{ .cond = switch_expr, .cases = undefined, // set later @@ -2221,7 +2181,7 @@ fn transSwitch( var switch_scope = Scope.Switch{ .base = .{ - .id = .Switch, + .id = .@"switch", .parent = scope, }, .cases = std.ArrayList(Node).init(c.gpa), @@ -2229,11 +2189,7 @@ fn transSwitch( .default_label = null, .switch_label = null, }; - defer { - switch_node.data.cases = try c.arena.dupe(Node, switch_scope.cases.items); - switch_node.data.default = switch_scope.switch_label; - switch_scope.cases.deinit(); - } + defer switch_scope.cases.deinit(); // tmp block that all statements will go before being picked up by a case or default var block_scope = try Scope.Block.init(c, &switch_scope.base, false); @@ -2246,7 +2202,7 @@ fn transSwitch( switch_scope.pending_block = try Scope.Block.init(c, scope, false); try switch_scope.pending_block.statements.append(Node.initPayload(&switch_node.base)); - const last = try transStmt(c, &block_scope.base, stmt.getBody(), .unused, .r_value); + const last = try transStmt(c, &block_scope.base, stmt.getBody(), .unused); // take all pending statements const last_block_stmts = last.castTag(.block).?.data.stmts; @@ -2264,13 +2220,14 @@ fn transSwitch( switch_scope.pending_block.label = l; } if (switch_scope.default_label == null) { - const else_prong = try Node.switch_else.create( + const else_prong = try Tag.switch_else.create( c.arena, - try Node.@"break".create(c.arena, switch_scope.switch_label.?), + try Tag.@"break".create(c.arena, switch_scope.switch_label.?), ); - switch_scope.cases.append(else_prong); + try switch_scope.cases.append(else_prong); } + switch_node.data.cases = try c.arena.dupe(Node, switch_scope.cases.items); const result_node = try switch_scope.pending_block.complete(c); switch_scope.pending_block.deinit(); return result_node; @@ -2286,18 +2243,18 @@ fn transCase( const label = try block_scope.makeMangledName(c, "case"); const expr = if (stmt.getRHS()) |rhs| blk: { - const lhs_node = try transExpr(c, scope, stmt.getLHS(), .used, .r_value); - const rhs_node = try transExpr(c, scope, rhs, .used, .r_value); + const lhs_node = try transExpr(c, scope, stmt.getLHS(), .used); + const rhs_node = try transExpr(c, scope, rhs, .used); - break :blk Node.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); } else - try transExpr(c, scope, stmt.getLHS(), .used, .r_value); + try transExpr(c, scope, stmt.getLHS(), .used); - const switch_prong = try Node.switch_prong.create( - c.arena, - try Node.@"break".create(c.arena, label), - ); - switch_scope.cases.append(switch_prong); + const switch_prong = try Tag.switch_prong.create(c.arena, .{ + .lhs = expr, + .rhs = try Tag.@"break".create(c.arena, label), + }); + try switch_scope.cases.append(switch_prong); switch_scope.pending_block.label = label; @@ -2311,7 +2268,7 @@ fn transCase( try switch_scope.pending_block.statements.append(pending_node); - return transStmt(c, scope, stmt.getSubStmt(), .unused, .r_value); + return transStmt(c, scope, stmt.getSubStmt(), .unused); } fn transDefault( @@ -2323,12 +2280,12 @@ fn transDefault( const switch_scope = scope.getSwitch(); switch_scope.default_label = try block_scope.makeMangledName(c, "default"); - const else_prong = try Node.switch_else.create( + const else_prong = try Tag.switch_else.create( c.arena, - try Node.@"break".create(c.arena, switch_scope.default_label.?), + try Tag.@"break".create(c.arena, switch_scope.default_label.?), ); - switch_scope.cases.append(else_prong); - switch_scope.pending_block.label = try appendIdentifier(c, switch_scope.default_label.?); + try switch_scope.cases.append(else_prong); + switch_scope.pending_block.label = switch_scope.default_label.?; // take all pending statements try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); @@ -2339,7 +2296,7 @@ fn transDefault( switch_scope.pending_block = try Scope.Block.init(c, scope, false); try switch_scope.pending_block.statements.append(pending_node); - return transStmt(c, scope, stmt.getSubStmt(), .unused, .r_value); + return transStmt(c, scope, stmt.getSubStmt(), .unused); } fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { @@ -2352,7 +2309,7 @@ fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: // See comment in `transIntegerLiteral` for why this code is here. // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, expr); - const as_node = try Node.as.create(c.arena, .{ + const as_node = try Tag.as.create(c.arena, .{ .lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()), .rhs = try transCreateNodeAPInt(c, result.Val.getInt()), }); @@ -2369,10 +2326,10 @@ fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.Predefined } fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node { - return Node.char_literal.create(c.arena, if (narrow) - try std.fmt.bufPrint(c.arena, "'{}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})}) + return Tag.char_literal.create(c.arena, if (narrow) + try std.fmt.allocPrint(c.arena, "'{s}'", .{std.zig.fmtEscapes(&.{@intCast(u8, val)})}) else - try std.fmt.bufPrint(c.arena, "'\\u{{{x}}}'", .{val})); + try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val})); } fn transCharLiteral( @@ -2398,7 +2355,7 @@ fn transCharLiteral( // See comment in `transIntegerLiteral` for why this code is here. // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, stmt); - const as_node = Node.as.create(c.arena, .{ + const as_node = try Tag.as.create(c.arena, .{ .lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()), .rhs = int_lit_node, }); @@ -2416,12 +2373,12 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: var it = comp.body_begin(); const end_it = comp.body_end(); while (it != end_it - 1) : (it += 1) { - const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value); + const result = try transStmt(c, &block_scope.base, it[0], .unused); try block_scope.statements.append(result); } - const break_node = try Node.break_val.create(c.arena, .{ - .label = block_scope.label, - .val = try transStmt(c, &block_scope.base, it[0], .used, .r_value), + const break_node = try Tag.break_val.create(c.arena, .{ + .label = block_scope.label, + .val = try transStmt(c, &block_scope.base, it[0], .used), }); try block_scope.statements.append(break_node); const res = try block_scope.complete(c); @@ -2429,10 +2386,10 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: } fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node { - var container_node = try transExpr(c, scope, stmt.getBase(), .used, .r_value); + var container_node = try transExpr(c, scope, stmt.getBase(), .used); if (stmt.isArrow()) { - container_node = try Node.deref.create(c.arena, container_node); + container_node = try Tag.deref.create(c.arena, container_node); } const member_decl = stmt.getMemberDecl(); @@ -2450,9 +2407,9 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re const decl = @ptrCast(*const clang.NamedDecl, member_decl); break :blk try c.str(decl.getName_bytes_begin()); }; - const ident = try Node.identifier.create(c.arena, name); + const ident = try Tag.identifier.create(c.arena, name); - const node = try Node.field_access.create(c.arena, .{ .lhs = container_node, .rhs = ident}); + const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .rhs = ident }); return maybeSuppressResult(c, scope, result_used, node); } @@ -2469,7 +2426,7 @@ fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscrip } } - const container_node = try transExpr(c, scope, base_stmt, .used, .r_value); + const container_node = try transExpr(c, scope, base_stmt, .used); // cast if the index is long long or signed const subscr_expr = stmt.getIdx(); @@ -2477,14 +2434,17 @@ fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscrip const is_longlong = cIsLongLongInteger(qt); const is_signed = cIsSignedInteger(qt); - - const node = try Node.array_access.create(c.arena, .{ .lhs = container_node, .rhs = if (is_longlong or is_signed) blk: { - const cast_node = try c.createBuiltinCall("@intCast", 2); + const rhs = if (is_longlong or is_signed) blk: { // check if long long first so that signed long long doesn't just become unsigned long long - var typeid_node = if (is_longlong) try transCreateNodeIdentifier(c, "usize") else try transQualTypeIntWidthOf(c, qt, false); - break :blk try Node.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used, .r_value)}); + var typeid_node = if (is_longlong) try Tag.identifier.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, qt, false); + break :blk try Tag.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used) }); } else - try transExpr(c, scope, subscr_expr, .used, .r_value)}); + try transExpr(c, scope, subscr_expr, .used); + + const node = try Tag.array_access.create(c.arena, .{ + .lhs = container_node, + .rhs = rhs, + }); return maybeSuppressResult(c, scope, result_used, node); } @@ -2522,23 +2482,23 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool { fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node { const callee = stmt.getCallee(); - var raw_fn_expr = try transExpr(c, scope, callee, .used, .r_value); + var raw_fn_expr = try transExpr(c, scope, callee, .used); var is_ptr = false; const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr); const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee)) - try transCreateNodeUnwrapNull(rp.c, raw_fn_expr) + try Tag.unwrap.create(c.arena, raw_fn_expr) else raw_fn_expr; const num_args = stmt.getNumArgs(); - const call_params = try c.arena.alloc(Node, num_args); + const args = try c.arena.alloc(Node, num_args); - const args = stmt.getArgs(); + const c_args = stmt.getArgs(); var i: usize = 0; while (i < num_args) : (i += 1) { - var call_param = try transExpr(c, scope, args[i], .used, .r_value); + var arg = try transExpr(c, scope, c_args[i], .used); // In C the result type of a boolean expression is int. If this result is passed as // an argument to a function whose parameter is also int, there is no cast. Therefore @@ -2549,17 +2509,17 @@ fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result const param_count = fn_proto.getNumParams(); if (i < param_count) { const param_qt = fn_proto.getParamType(@intCast(c_uint, i)); - if (isBoolRes(call_param) and cIsNativeInt(param_qt)) { - call_param = try Node.bool_to_int.create(c.arena, call_param); + if (isBoolRes(arg) and cIsNativeInt(param_qt)) { + arg = try Tag.bool_to_int.create(c.arena, arg); } } }, else => {}, } } - call_params[i] = call_param; + args[i] = arg; } - const node = try Node.call.create(c.arena, .{ .lhs = fn_expr, .args = call_params }); + const node = try Tag.call.create(c.arena, .{ .lhs = fn_expr, .args = args }); if (fn_ty) |ty| { const canon = ty.getReturnType().getCanonicalType(); const ret_ty = canon.getTypePtr(); @@ -2609,17 +2569,17 @@ fn transUnaryExprOrTypeTraitExpr( result_used: ResultUsed, ) TransError!Node { const loc = stmt.getBeginLoc(); - const type_node = try transQualType(rp, stmt.getTypeOfArgument(), loc); + const type_node = try transQualType(c, stmt.getTypeOfArgument(), loc); const kind = stmt.getKind(); switch (kind) { - .SizeOf => return Node.sizeof.create(c.arena, type_node), - .AlignOf => return Node.alignof.create(c.arena, type_node), + .SizeOf => return Tag.sizeof.create(c.arena, type_node), + .AlignOf => return Tag.alignof.create(c.arena, type_node), .PreferredAlignOf, .VecStep, .OpenMPRequiredSimdAlign, - => return revertAndWarn( - rp, + => return fail( + c, error.UnsupportedTranslation, loc, "Unsupported type trait kind {}", @@ -2642,53 +2602,54 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat const op_expr = stmt.getSubExpr(); switch (stmt.getOpcode()) { .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePostCrement(c, scope, stmt, .assign_add_wrap, used) + return transCreatePostCrement(c, scope, stmt, .add_wrap_assign, used) else - return transCreatePostCrement(c, scope, stmt, .assign_add, used), + return transCreatePostCrement(c, scope, stmt, .add_assign, used), .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePostCrement(c, scope, stmt, .assign_sub_wrap, used) + return transCreatePostCrement(c, scope, stmt, .sub_wrap_assign, used) else - return transCreatePostCrement(c, scope, stmt, .assign_sub, used), + return transCreatePostCrement(c, scope, stmt, .sub_assign, used), .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePreCrement(c, scope, stmt, .assign_add_wrap, used) + return transCreatePreCrement(c, scope, stmt, .add_wrap_assign, used) else - return transCreatePreCrement(c, scope, stmt, .assign_add, used), + return transCreatePreCrement(c, scope, stmt, .add_assign, used), .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreatePreCrement(c, scope, stmt, .assign_sub_wrap, used) + return transCreatePreCrement(c, scope, stmt, .sub_wrap_assign, used) else - return transCreatePreCrement(c, scope, stmt, .assign_sub, used), + return transCreatePreCrement(c, scope, stmt, .sub_assign, used), .AddrOf => { if (cIsFunctionDeclRef(op_expr)) { - return transExpr(rp, scope, op_expr, used, .r_value); + return transExpr(c, scope, op_expr, used); } - return Node.address_of.create(c.arena, try transExpr(c, scope, op_expr, used, .r_value)); + return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used)); }, .Deref => { - const node = try transExpr(c, scope, op_expr, used, .r_value); + const node = try transExpr(c, scope, op_expr, used); var is_ptr = false; const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr); if (fn_ty != null and is_ptr) return node; - return Node.unwrap_deref.create(c.arena, node); + const unwrapped = try Tag.unwrap.create(c.arena, node); + return Tag.deref.create(c.arena, unwrapped); }, - .Plus => return transExpr(c, scope, op_expr, used, .r_value), + .Plus => return transExpr(c, scope, op_expr, used), .Minus => { if (!qualTypeHasWrappingOverflow(op_expr.getType())) { - return Node.negate.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); + return Tag.negate.create(c.arena, try transExpr(c, scope, op_expr, .used)); } else if (cIsUnsignedInteger(op_expr.getType())) { // use -% x for unsigned integers - return Node.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); + return Tag.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used)); } else return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{}); }, .Not => { - return Node.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); + return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used)); }, .LNot => { - return Node.not.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value)); + return Tag.not.create(c.arena, try transExpr(c, scope, op_expr, .used)); }, .Extension => { - return transExpr(c, scope, stmt.getSubExpr(), used, .l_value); + return transExpr(c, scope, stmt.getSubExpr(), used); }, else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}), } @@ -2698,7 +2659,7 @@ fn transCreatePreCrement( c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, - op: Node.Tag, + op: Tag, used: ResultUsed, ) TransError!Node { const op_expr = stmt.getSubExpr(); @@ -2707,8 +2668,8 @@ fn transCreatePreCrement( // common case // c: ++expr // zig: expr += 1 - const lhs = try transExpr(c, scope, op_expr, .used, .r_value); - const rhs = Node.one_literal.init(); + const lhs = try transExpr(c, scope, op_expr, .used); + const rhs = Tag.one_literal.init(); return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used); } // worst case @@ -2722,17 +2683,17 @@ fn transCreatePreCrement( defer block_scope.deinit(); const ref = try block_scope.makeMangledName(c, "ref"); - const expr = try transExpr(c, scope, op_expr, .used, .r_value); - const addr_of = try Node.address_of.create(c.arena, expr); - const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of}); + const expr = try transExpr(c, scope, op_expr, .used); + const addr_of = try Tag.address_of.create(c.arena, expr); + const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); try block_scope.statements.append(ref_decl); - const lhs_node = try Node.identifier.create(c.arena, ref); - const ref_node = try Node.deref.create(c.arena, lhs_node); - const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Node.one_literal.init(), .used); + const lhs_node = try Tag.identifier.create(c.arena, ref); + const ref_node = try Tag.deref.create(c.arena, lhs_node); + const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Tag.one_literal.init(), .used); try block_scope.statements.append(node); - const break_node = try Node.break_val.create(c.arena, .{ + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, .val = ref_node, }); @@ -2744,7 +2705,7 @@ fn transCreatePostCrement( c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, - op: Node.Tag, + op: Tag, used: ResultUsed, ) TransError!Node { const op_expr = stmt.getSubExpr(); @@ -2753,8 +2714,8 @@ fn transCreatePostCrement( // common case // c: expr++ // zig: expr += 1 - const lhs = try transExpr(c, scope, op_expr, .used, .r_value); - const rhs = Node.one_literal.init(); + const lhs = try transExpr(c, scope, op_expr, .used); + const rhs = Tag.one_literal.init(); return transCreateNodeInfixOp(c, scope, op, lhs, rhs, .used); } // worst case @@ -2769,24 +2730,24 @@ fn transCreatePostCrement( defer block_scope.deinit(); const ref = try block_scope.makeMangledName(c, "ref"); - const expr = try transExpr(c, scope, op_expr, .used, .r_value); - const addr_of = try Node.address_of.create(c.arena, expr); - const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of}); + const expr = try transExpr(c, scope, op_expr, .used); + const addr_of = try Tag.address_of.create(c.arena, expr); + const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); try block_scope.statements.append(ref_decl); - const lhs_node = try Node.identifier.create(c.arena, ref); - const ref_node = try Node.deref.create(c.arena, lhs_node); + const lhs_node = try Tag.identifier.create(c.arena, ref); + const ref_node = try Tag.deref.create(c.arena, lhs_node); const tmp = try block_scope.makeMangledName(c, "tmp"); - const tmp_decl = try Node.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node}); + const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node }); try block_scope.statements.append(tmp_decl); - const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Node.one_literal.init(), .used); + const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Tag.one_literal.init(), .used); try block_scope.statements.append(node); - const break_node = try Node.break_val.create(c.arena, .{ + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, - .val = try Node.identifier.create(c.arena, tmp), + .val = try Tag.identifier.create(c.arena, tmp), }); try block_scope.statements.append(break_node); return block_scope.complete(c); @@ -2795,26 +2756,26 @@ fn transCreatePostCrement( fn transCompoundAssignOperator(c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!Node { switch (stmt.getOpcode()) { .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreateCompoundAssign(c, scope, stmt, .assign_mul_wrap, used) + return transCreateCompoundAssign(c, scope, stmt, .mul_wrap_assign, used) else - return transCreateCompoundAssign(c, scope, stmt, .assign_mul, used), + return transCreateCompoundAssign(c, scope, stmt, .mul_assign, used), .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreateCompoundAssign(c, scope, stmt, .assign_add_wrap, used) + return transCreateCompoundAssign(c, scope, stmt, .add_wrap_assign, used) else - return transCreateCompoundAssign(c, scope, stmt, .assign_add, used), + return transCreateCompoundAssign(c, scope, stmt, .add_assign, used), .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType())) - return transCreateCompoundAssign(c, scope, stmt, .assign_sub_wrap, used) + return transCreateCompoundAssign(c, scope, stmt, .sub_wrap_assign, used) else - return transCreateCompoundAssign(c, scope, stmt, .assign_sub, used), - .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_div, used), - .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_mod, used), - .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_shl, used), - .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_shr, used), - .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_bit_and, used), - .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_bit_xor, used), - .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .assign_bit_or, used), + return transCreateCompoundAssign(c, scope, stmt, .sub_assign, used), + .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .div_assign, used), + .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .mod_assign, used), + .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .shl_assign, used), + .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .shr_assign, used), + .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_and_assign, used), + .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_xor_assign, used), + .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_or_assign, used), else => return fail( - rp, + c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", @@ -2827,12 +2788,12 @@ fn transCreateCompoundAssign( c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, - op: Node.Tag, + op: Tag, used: ResultUsed, ) TransError!Node { - const is_shift = op == .assign_shl or op == .assign_shr; - const is_div = op == .assign_div; - const is_mod = op == .assign_mod; + const is_shift = op == .shl_assign or op == .shr_assign; + const is_div = op == .div_assign; + const is_mod = op == .mod_assign; const lhs = stmt.getLHS(); const rhs = stmt.getRHS(); const loc = stmt.getBeginLoc(); @@ -2849,21 +2810,21 @@ fn transCreateCompoundAssign( // c: lhs += rhs // zig: lhs += rhs if ((is_mod or is_div) and is_signed) { - const lhs_node = try transExpr(c, scope, lhs, .used, .l_value); - const rhs_node = try transExpr(c, scope, rhs, .used, .r_value); + const lhs_node = try transExpr(c, scope, lhs, .used); + const rhs_node = try transExpr(c, scope, rhs, .used); const builtin = if (is_mod) - try Node.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) + try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) else - try Node.divTrunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); } - const lhs_node = try transExpr(c, scope, lhs, .used, .l_value); + const lhs_node = try transExpr(c, scope, lhs, .used); var rhs_node = if (is_shift or requires_int_cast) - try transExprCoercing(c, scope, rhs, .used, .r_value) + try transExprCoercing(c, scope, rhs, .used) else - try transExpr(c, scope, rhs, .used, .r_value); + try transExpr(c, scope, rhs, .used); if (is_shift or requires_int_cast) { // @intCast(rhs) @@ -2871,11 +2832,11 @@ fn transCreateCompoundAssign( try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc) else try transQualType(c, getExprQualType(c, lhs), loc); - - rhs_node = try Node.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); + + rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } - return transCreateNodeInfixOp(c, scope, assign_op, lhs_node, rhs_node, .used); + return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used); } // worst case // c: lhs += rhs @@ -2888,25 +2849,25 @@ fn transCreateCompoundAssign( defer block_scope.deinit(); const ref = try block_scope.makeMangledName(c, "ref"); - const expr = try transExpr(c, scope, op_expr, .used, .r_value); - const addr_of = try Node.address_of.create(c.arena, expr); - const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of}); + const expr = try transExpr(c, scope, lhs, .used); + const addr_of = try Tag.address_of.create(c.arena, expr); + const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); try block_scope.statements.append(ref_decl); - const lhs_node = try Node.identifier.create(c.arena, ref); - const ref_node = try Node.deref.create(c.arena, lhs_node); + const lhs_node = try Tag.identifier.create(c.arena, ref); + const ref_node = try Tag.deref.create(c.arena, lhs_node); if ((is_mod or is_div) and is_signed) { - const rhs_node = try transExpr(c, scope, rhs, .used, .r_value); + const rhs_node = try transExpr(c, scope, rhs, .used); const builtin = if (is_mod) - try Node.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) + try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) else - try Node.divTrunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); const assign = try transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); try block_scope.statements.append(assign); } else { - var rhs_node = try transExpr(c, scope, rhs, .used, .r_value); + var rhs_node = try transExpr(c, scope, rhs, .used); if (is_shift or requires_int_cast) { // @intCast(rhs) @@ -2914,15 +2875,15 @@ fn transCreateCompoundAssign( try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc) else try transQualType(c, getExprQualType(c, lhs), loc); - - rhs_node = try Node.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); + + rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } const assign = try transCreateNodeInfixOp(c, scope, op, ref_node, rhs_node, .used); try block_scope.statements.append(assign); } - const break_node = try Node.break_val.create(c.arena, .{ + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, .val = ref_node, }); @@ -2941,7 +2902,7 @@ fn transCPtrCast( const child_type = ty.getPointeeType(); const src_ty = src_type.getTypePtr(); const src_child_type = src_ty.getPointeeType(); - const dst_type = try transType(c, ty, loc); + const dst_type_node = try transType(c, ty, loc); if ((src_child_type.isConstQualified() and !child_type.isConstQualified()) or @@ -2949,8 +2910,8 @@ fn transCPtrCast( !child_type.isVolatileQualified())) { // Casting away const or volatile requires us to use @intToPtr - const ptr_to_int = try Node.ptr_to_int.create(c.arena, expr); - const int_to_ptr = try Node.int_to_ptr.create(c.arena, .{ .lhs = dst_type, .rhs = ptr_to_int }); + const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); + const int_to_ptr = try Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int }); return int_to_ptr; } else { // Implicit downcasting from higher to lower alignment values is forbidden, @@ -2963,17 +2924,17 @@ fn transCPtrCast( expr else blk: { const child_type_node = try transQualType(c, child_type, loc); - const alignof = try Node.alignof.create(c.arena, child_type_node); - const align_cast = try Node.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); + const alignof = try Tag.alignof.create(c.arena, child_type_node); + const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); break :blk align_cast; }; - return Node.ptr_cast.create(c.arena, .{ .lhs = dst_type, .rhs = rhs }); + return Tag.ptr_cast.create(c.arena, .{ .lhs = dst_type_node, .rhs = rhs }); } } fn transBreak(c: *Context, scope: *Scope) TransError!Node { const break_scope = scope.getBreakableScope(); - const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: { + const label_text: ?[]const u8 = if (break_scope.id == .@"switch") blk: { const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope); const block_scope = try scope.findBlockScope(c); swtch.switch_label = try block_scope.makeMangledName(c, "switch"); @@ -2981,20 +2942,20 @@ fn transBreak(c: *Context, scope: *Scope) TransError!Node { } else null; - return Node.@"break".create(c.arena, label_text); + return Tag.@"break".create(c.arena, label_text); } fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { // TODO use something more accurate const dbl = stmt.getValueAsApproximateDouble(); const node = try transCreateNodeNumber(c, dbl); - return maybeSuppressResult(c, scope, used, &node.base); + return maybeSuppressResult(c, scope, used, node); } fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!Node { // GNU extension of the ternary operator where the middle expression is // omitted, the conditition itself is returned if it evaluates to true - const qt = @ptrCast(*const clang.Stmt, stmt).getType(); + const qt = @ptrCast(*const clang.Expr, stmt).getType(); const res_is_bool = qualTypeIsBoolean(qt); const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); const cond_expr = casted_stmt.getCond(); @@ -3010,26 +2971,33 @@ fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang defer block_scope.deinit(); const mangled_name = try block_scope.makeMangledName(c, "cond_temp"); - const init_node = try transExpr(c, &block_scope.base, cond_expr, .used, .r_value); - const ref_decl = try Node.var_simple.create(c.arena, .{ .name = mangled_name, .init = init_node}); + const init_node = try transExpr(c, &block_scope.base, cond_expr, .used); + const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = mangled_name, .init = init_node }); try block_scope.statements.append(ref_decl); + var cond_scope = Scope.Condition{ + .base = .{ + .parent = &block_scope.base, + .id = .condition, + }, + }; + defer cond_scope.deinit(); const cond_node = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); - var then_body = try Node.identifier.create(c.arena, mangled_name); + var then_body = try Tag.identifier.create(c.arena, mangled_name); if (!res_is_bool and isBoolRes(init_node)) { - then_body = try Node.bool_to_int.create(c.arena, then_body); + then_body = try Tag.bool_to_int.create(c.arena, then_body); } - var else_body = try transExpr(c, &block_scope.base, false_expr, .used, .r_value); + var else_body = try transExpr(c, &block_scope.base, false_expr, .used); if (!res_is_bool and isBoolRes(else_body)) { - else_body = try Node.bool_to_int.create(c.arena, else_body); + else_body = try Tag.bool_to_int.create(c.arena, else_body); } - const if_node = try Node.@"if".create(c.arena, .{ - .cond = cond, + const if_node = try Tag.@"if".create(c.arena, .{ + .cond = cond_node, .then = then_body, .@"else" = else_body, }); - const break_node = try Node.break_val.create(c.arena, .{ + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, .val = if_node, }); @@ -3042,31 +3010,31 @@ fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.Condi var cond_scope = Scope.Condition{ .base = .{ .parent = scope, - .id = .Condition, + .id = .condition, }, }; defer cond_scope.deinit(); - const qt = @ptrCast(*const clang.Stmt, stmt).getType(); + const qt = @ptrCast(*const clang.Expr, stmt).getType(); const res_is_bool = qualTypeIsBoolean(qt); const casted_stmt = @ptrCast(*const clang.AbstractConditionalOperator, stmt); const cond_expr = casted_stmt.getCond(); const true_expr = casted_stmt.getTrueExpr(); const false_expr = casted_stmt.getFalseExpr(); - const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used, .r_value); + const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); - var then_body = try transExpr(c, scope, true_expr, .used, .r_value); + var then_body = try transExpr(c, scope, true_expr, .used); if (!res_is_bool and isBoolRes(then_body)) { - then_body = try Node.bool_to_int.create(c.arena, then_body); + then_body = try Tag.bool_to_int.create(c.arena, then_body); } - var else_body = try transExpr(c, scope, false_expr, .used, .r_value); + var else_body = try transExpr(c, scope, false_expr, .used); if (!res_is_bool and isBoolRes(else_body)) { - else_body = try Node.bool_to_int.create(c.arena, else_body); + else_body = try Tag.bool_to_int.create(c.arena, else_body); } - const if_node = try Node.@"if".create(c.arena, .{ + const if_node = try Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body, @@ -3081,7 +3049,7 @@ fn maybeSuppressResult( result: Node, ) TransError!Node { if (used == .used) return result; - return Node.ignore.create(c.arena, result); + return Tag.ignore.create(c.arena, result); } fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { @@ -3100,19 +3068,19 @@ fn transQualTypeInitialized( const ty = qt.getTypePtr(); if (ty.getTypeClass() == .IncompleteArray) { const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); - const elem_ty = incomplete_array_ty.getElementType().getTypePtr(); + const elem_ty = try transType(c, incomplete_array_ty.getElementType().getTypePtr(), source_loc); switch (decl_init.getStmtClass()) { .StringLiteralClass => { const string_lit = @ptrCast(*const clang.StringLiteral, decl_init); const string_lit_size = string_lit.getLength() + 1; // +1 for null terminator const array_size = @intCast(usize, string_lit_size); - return Node.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); + return Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty }); }, .InitListExprClass => { const init_expr = @ptrCast(*const clang.InitListExpr, decl_init); const size = init_expr.getNumInits(); - return Node.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty }); + return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty }); }, else => {}, } @@ -3135,7 +3103,7 @@ fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) Typ fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!Node { assert(ty.getTypeClass() == .Builtin); const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); - return Node.type.create(c.arena, switch (builtin_ty.getKind()) { + return Tag.type.create(c.arena, switch (builtin_ty.getKind()) { .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8", .UShort, .Short => if (is_signed) "c_short" else "c_ushort", .UInt, .Int => if (is_signed) "c_int" else "c_uint", @@ -3214,11 +3182,11 @@ fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.Sourc if (int_bit_width != 0) { // we can perform the log2 now. const cast_bit_width = math.log2_int(u64, int_bit_width); - return Node.log2_int_type.create(c.arena, cast_bit_width); + return Tag.log2_int_type.create(c.arena, cast_bit_width); } const zig_type = try transQualType(c, qt, source_loc); - return Node.std_math_Log2Int.create(c.arena, zig_type); + return Tag.std_math_Log2Int.create(c.arena, zig_type); } fn qualTypeChildIsFnProto(qt: clang.QualType) bool { @@ -3392,10 +3360,10 @@ fn transCreateNodeAssign( // c: lhs = rhs // zig: lhs = rhs if (result_used == .unused) { - const lhs_node = try transExpr(c, scope, lhs, .used, .l_value); - var rhs_node = try transExprCoercing(c, scope, rhs, .used, .r_value); + const lhs_node = try transExpr(c, scope, lhs, .used); + var rhs_node = try transExprCoercing(c, scope, rhs, .used); if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { - rhs_node = try Node.bool_to_int.create(c.arena, rhs_node); + rhs_node = try Tag.bool_to_int.create(c.arena, rhs_node); } return transCreateNodeInfixOp(c, scope, .assign, lhs_node, rhs_node, .used); } @@ -3411,17 +3379,16 @@ fn transCreateNodeAssign( defer block_scope.deinit(); const tmp = try block_scope.makeMangledName(c, "tmp"); - const rhs = try transExpr(c, scope, op_expr, .used, .r_value); - const tmp_decl = try Node.var_simple.create(c.arena, .{ .name = tmp, .init = rhs}); + const rhs_node = try transExpr(c, scope, rhs, .used); + const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); try block_scope.statements.append(tmp_decl); - - const lhs = try transExpr(c, &block_scope.base, lhs, .used, .l_value); - const tmp_ident = try Node.identifier.create(c.arena, tmp); - const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs, tmp_iden, .used); + const lhs_node = try transExpr(c, &block_scope.base, lhs, .used); + const tmp_ident = try Tag.identifier.create(c.arena, tmp); + const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs_node, tmp_ident, .used); try block_scope.statements.append(assign); - const break_node = try Node.break_val.create(c.arena, .{ + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, .val = tmp_ident, }); @@ -3432,7 +3399,7 @@ fn transCreateNodeAssign( fn transCreateNodeInfixOp( c: *Context, scope: *Scope, - op: ast.Node.Tag, + op: Tag, lhs: Node, rhs: Node, used: ResultUsed, @@ -3452,13 +3419,13 @@ fn transCreateNodeBoolInfixOp( c: *Context, scope: *Scope, stmt: *const clang.BinaryOperator, - op: ast.Node.Tag, + op: Tag, used: ResultUsed, ) !Node { - std.debug.assert(op == .bool_and or op == .bool_or); + std.debug.assert(op == .@"and" or op == .@"or"); - const lhs = try transBoolExpr(rp, scope, stmt.getLHS(), .used, .l_value); - const rhs = try transBoolExpr(rp, scope, stmt.getRHS(), .used, .r_value); + const lhs = try transBoolExpr(c, scope, stmt.getLHS(), .used); + const rhs = try transBoolExpr(c, scope, stmt.getRHS(), .used); return transCreateNodeInfixOp(c, scope, op, lhs, rhs, used); } @@ -3503,22 +3470,22 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, }; - return Node.int_literal.create(c.arena, str); + return Tag.number_literal.create(c.arena, str); } fn transCreateNodeNumber(c: *Context, int: anytype) !Node { const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(int))) "{d}" else "{s}"; const str = try std.fmt.allocPrint(c.arena, fmt_s, .{int}); - return Node.int_literal.create(c.arena, str); + return Tag.number_literal.create(c.arena, str); } fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node { const scope = &c.global_scope.base; - var fn_params = std.ArrayList(Node).init(c.gpa); + var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); defer fn_params.deinit(); - for (proto_alias.params()) |param, i| { + for (proto_alias.data.params) |param, i| { const param_name = param.name orelse try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()}); @@ -3529,29 +3496,29 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: }); } - const init = if (value.castTag(.var_decl)) |v| - v.data.init - else if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |v| + const init = if (ref.castTag(.var_decl)) |v| + v.data.init.? + else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| v.data.init else unreachable; - const unwrap_expr = try Node.unwrap.create(c.arena, init); - const call_params = try c.arena.alloc(Node, fn_params.items.len); + const unwrap_expr = try Tag.unwrap.create(c.arena, init); + const args = try c.arena.alloc(Node, fn_params.items.len); for (fn_params.items) |param, i| { - call_params[i] = try Node.identifier.create(c.arena, param.name); + args[i] = try Tag.identifier.create(c.arena, param.name.?); } - const call_expr = try Node.call.create(c.arean, .{ + const call_expr = try Tag.call.create(c.arena, .{ .lhs = unwrap_expr, - .args = call_params, + .args = args, }); - const return_expr = try Node.@"return".create(c.arean, call_expr); - const block = try Node.block_single.create(c.arean, return_expr); + const return_expr = try Tag.@"return".create(c.arena, call_expr); + const block = try Tag.block_single.create(c.arena, return_expr); - return Node.pub_inline_fn.create(c.arena, .{ + return Tag.pub_inline_fn.create(c.arena, .{ .name = name, - .params = try c.arena.dupe(ast.Node.Param, fn_params.items), - .return_type = proto_alias.return_type, + .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), + .return_type = proto_alias.data.return_type, .body = block, }); } @@ -3560,7 +3527,7 @@ fn transCreateNodeShiftOp( c: *Context, scope: *Scope, stmt: *const clang.BinaryOperator, - op: Node.Tag, + op: Tag, used: ResultUsed, ) !Node { std.debug.assert(op == .shl or op == .shr); @@ -3570,11 +3537,11 @@ fn transCreateNodeShiftOp( const rhs_location = rhs_expr.getBeginLoc(); // lhs >> @as(u5, rh) - const lhs = try transExpr(c, scope, lhs_expr, .used, .l_value); + const lhs = try transExpr(c, scope, lhs_expr, .used); const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location); - const rhs = try transExprCoercing(c, scope, rhs_expr, .used, .r_value); - const rhs_casted = try Node.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type }); + const rhs = try transExprCoercing(c, scope, rhs_expr, .used); + const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type }); return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used); } @@ -3583,7 +3550,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio switch (ty.getTypeClass()) { .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); - return Node.type.create(c.arena, switch (builtin_ty.getKind()) { + return Tag.type.create(c.arena, switch (builtin_ty.getKind()) { .Void => "c_void", .Bool => "bool", .Char_U, .UChar, .Char_S, .Char8 => "u8", @@ -3608,11 +3575,13 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio }, .FunctionProto => { const fn_proto_ty = @ptrCast(*const clang.FunctionProtoType, ty); - return transFnProto(c, null, fn_proto_ty, source_loc, null, false); + const fn_proto = try transFnProto(c, null, fn_proto_ty, source_loc, null, false); + return Node.initPayload(&fn_proto.base); }, .FunctionNoProto => { const fn_no_proto_ty = @ptrCast(*const clang.FunctionType, ty); - return transFnNoProto(c, fn_no_proto_ty, source_loc, null, false); + const fn_proto = try transFnNoProto(c, fn_no_proto_ty, source_loc, null, false); + return Node.initPayload(&fn_proto.base); }, .Paren => { const paren_ty = @ptrCast(*const clang.ParenType, ty); @@ -3621,16 +3590,16 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio .Pointer => { const child_qt = ty.getPointeeType(); if (qualTypeChildIsFnProto(child_qt)) { - return Node.optional_type.create(c.arena, try transQualType(c, child_qt, source_loc)); + return Tag.optional_type.create(c.arena, try transQualType(c, child_qt, source_loc)); } const is_const = child_qt.isConstQualified(); const is_volatile = child_qt.isVolatileQualified(); const elem_type = try transQualType(c, child_qt, source_loc); - if (typeIsOpaque(rp.c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(rp.c, child_qt)) { - return Node.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); + if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) { + return Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); } - return Node.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); + return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); }, .ConstantArray => { const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); @@ -3639,7 +3608,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const size = size_ap_int.getLimitedValue(math.maxInt(usize)); const elem_type = try transType(c, const_arr_ty.getElementType().getTypePtr(), source_loc); - return Node.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); + return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); }, .IncompleteArray => { const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); @@ -3649,7 +3618,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const is_volatile = child_qt.isVolatileQualified(); const elem_type = try transQualType(c, child_qt, source_loc); - return Node.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); + return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); }, .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); @@ -3690,7 +3659,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio }, else => { const type_name = c.str(ty.getTypeClassName()); - return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name}); + return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); }, } } @@ -3770,7 +3739,7 @@ fn transCC( .AAPCS => return CallingConvention.AAPCS, .AAPCS_VFP => return CallingConvention.AAPCSVFP, else => return fail( - rp, + c, error.UnsupportedType, source_loc, "unsupported calling convention: {s}", @@ -3786,7 +3755,7 @@ fn transFnProto( source_loc: clang.SourceLocation, fn_decl_context: ?FnDeclContext, is_pub: bool, -) !Node.FnProto { +) !*ast.Payload.Func { const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty); const cc = try transCC(c, fn_ty, source_loc); const is_var_args = fn_proto_ty.isVariadic(); @@ -3799,7 +3768,7 @@ fn transFnNoProto( source_loc: clang.SourceLocation, fn_decl_context: ?FnDeclContext, is_pub: bool, -) !Node.FnProto { +) !*ast.Payload.Func { const cc = try transCC(c, fn_ty, source_loc); const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true; return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); @@ -3822,7 +3791,7 @@ fn finishTransFnProto( // TODO check for always_inline attribute // TODO check for align attribute - var fn_params = std.ArrayList(ast.Payload.Func.Param).init(c.gpa); + var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa); defer fn_params.deinit(); const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0; try fn_params.ensureCapacity(param_count); @@ -3861,7 +3830,7 @@ fn finishTransFnProto( break :blk null; }; - const alignment: c_uint = blk: { + const alignment = blk: { if (fn_decl) |decl| { const alignment = decl.getAlignedAttribute(c.clang_context); if (alignment != 0) { @@ -3876,16 +3845,16 @@ fn finishTransFnProto( const return_type_node = blk: { if (fn_ty.getNoReturnAttr()) { - break :blk Node.noreturn_type.init(); + break :blk Tag.noreturn_type.init(); } else { const return_qt = fn_ty.getReturnType(); if (isCVoid(return_qt)) { // convert primitive c_void to actual void (only for return type) - break :blk Node.void_type.init(); + break :blk Tag.void_type.init(); } else { break :blk transQualType(c, return_qt, source_loc) catch |err| switch (err) { error.UnsupportedType => { - try warn(c, source_loc, "unsupported function proto return type", .{}); + try warn(c, &c.global_scope.base, source_loc, "unsupported function proto return type", .{}); return err; }, error.OutOfMemory => |e| return e, @@ -3893,26 +3862,31 @@ fn finishTransFnProto( } } }; - - return Node.func.create(c.arena, .{ - .is_pub = is_pub, - .is_extern = is_extern, - .is_export = is_export, - .is_var_args = is_var_args, - .name = name, - .linksection_string = linksection_string, - .explicit_callconv = explicit_callconv, - .params = try c.arena.dupe(ast.Payload.Func.Param, fn_params.items), - .return_type = return_node, - .body = null, - .alignment = alignment, - }); + const name: ?[]const u8 = if (fn_decl_context) |ctx| ctx.fn_name else null; + const payload = try c.arena.create(ast.Payload.Func); + payload.* = .{ + .base = .{ .tag = .func }, + .data = .{ + .is_pub = is_pub, + .is_extern = is_extern, + .is_export = is_export, + .is_var_args = is_var_args, + .name = name, + .linksection_string = linksection_string, + .explicit_callconv = explicit_callconv, + .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), + .return_type = return_type_node, + .body = null, + .alignment = alignment, + }, + }; + return payload; } fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { const args_prefix = .{c.locStr(loc)}; - const value = std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, args_prefix ++ args); - try scope.appendNode(c.gpa, try Node.warning.create(c.arena, value)); + const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, args_prefix ++ args); + try scope.appendNode(try Tag.warning.create(c.arena, value)); } fn fail( @@ -3922,17 +3896,17 @@ fn fail( comptime format: []const u8, args: anytype, ) (@TypeOf(err) || error{OutOfMemory}) { - try warn(c, source_loc, format, args); + try warn(c, &c.global_scope.base, source_loc, format, args); return err; } -pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void { +pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) Error!void { // location // pub const name = @compileError(msg); - const location_comment = std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)}); - try c.global_scope.nodes.append(try Node.warning.create(c.arena, location_comment)); - const fail_msg = std.fmt.allocPrint(c.arena, format, args); - try c.global_scope.nodes.append(try Node.fail_decl.create(c.arena, fail_msg)); + const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)}); + try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment)); + const fail_msg = try std.fmt.allocPrint(c.arena, format, args); + try c.global_scope.nodes.append(try Tag.fail_decl.create(c.arena, fail_msg)); } pub fn freeErrors(errors: []ClangErrMsg) void { @@ -4075,7 +4049,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { if (last != .Eof and last != .Nl) return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); - const var_decl = try Node.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); + const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node }); _ = try c.global_scope.macro_table.put(m.name, var_decl); } @@ -4099,7 +4073,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { try fn_params.append(.{ .is_noalias = false, .name = mangled_name, - .type = Node.@"anytype".init(), + .type = Tag.@"anytype".init(), }); if (m.peek().? != .Comma) break; @@ -4119,19 +4093,19 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { const stmts = some.data.stmts; const blk_last = stmts[stmts.len - 1]; const br = blk_last.castTag(.break_val).?; - break :blk br.data; + break :blk br.data.val; } else expr; - const typeof = try Node.typeof.create(c.arean, typeof_arg); - const return_expr = try Node.@"return".create(c.arena, expr); - try block_scope.statements.append(&return_expr.base); - - const fn_decl = try Node.pub_inline_fn.create(c.arena, .{ + const typeof = try Tag.typeof.create(c.arena, typeof_arg); + const return_expr = try Tag.@"return".create(c.arena, expr); + try block_scope.statements.append(return_expr); + + const fn_decl = try Tag.pub_inline_fn.create(c.arena, .{ .name = m.name, .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), .return_type = typeof, .body = try block_scope.complete(c), }); - _ = try c.global_scope.macro_table.put(m.name, &fn_proto.base); + _ = try c.global_scope.macro_table.put(m.name, fn_decl); } const ParseError = Error || error{ParseError}; @@ -4149,7 +4123,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var last = node; while (true) { // suppress result - const ignore = try Node.ignore.create(c.arena, last); + const ignore = try Tag.ignore.create(c.arena, last); try block_scope.statements.append(ignore); last = try parseCCondExpr(c, m, scope); @@ -4159,7 +4133,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { } } - const break_node = try Node.break_val.create(c.arena, .{ + const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, .val = last, }); @@ -4190,7 +4164,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { return transCreateNodeNumber(c, lit_bytes); } - const type_node = try Node.type.create(c.arena, switch (suffix) { + const type_node = try Tag.type.create(c.arena, switch (suffix) { .u => "c_uint", .l => "c_long", .lu => "c_ulong", @@ -4205,7 +4179,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { else => unreachable, }]; const rhs = try transCreateNodeNumber(c, lit_bytes); - return Node.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); + return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); }, .FloatLiteral => |suffix| { if (lit_bytes[0] == '.') @@ -4213,13 +4187,13 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { if (suffix == .none) { return transCreateNodeNumber(c, lit_bytes); } - const type_node = try Node.type.create(c.arena, switch (suffix) { + const type_node = try Tag.type.create(c.arena, switch (suffix) { .f => "f32", .l => "c_longdouble", else => unreachable, }); const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1]); - return Node.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); + return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); }, else => unreachable, } @@ -4391,56 +4365,56 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N switch (tok) { .CharLiteral => { if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) { - return Node.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); + return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); } else { const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]}); - return Node.int_literal.create(c.arena, str); + return Tag.number_literal.create(c.arena, str); } }, .StringLiteral => { - return Node.string_literal.create(c.arena, try zigifyEscapeSequences(c, m)); + return Tag.string_literal.create(c.arena, try zigifyEscapeSequences(c, m)); }, .IntegerLiteral, .FloatLiteral => { return parseCNumLit(c, m); }, // eventually this will be replaced by std.c.parse which will handle these correctly - .Keyword_void => return Node.type.create(c.arena, "c_void"), - .Keyword_bool => return Node.type.create(c.arena, "bool"), - .Keyword_double => return Node.type.create(c.arena, "f64"), - .Keyword_long => return Node.type.create(c.arena, "c_long"), - .Keyword_int => return Node.type.create(c.arena, "c_int"), - .Keyword_float => return Node.type.create(c.arena, "f32"), - .Keyword_short => return Node.type.create(c.arena, "c_short"), - .Keyword_char => return Node.type.create(c.arena, "u8"), + .Keyword_void => return Tag.type.create(c.arena, "c_void"), + .Keyword_bool => return Tag.type.create(c.arena, "bool"), + .Keyword_double => return Tag.type.create(c.arena, "f64"), + .Keyword_long => return Tag.type.create(c.arena, "c_long"), + .Keyword_int => return Tag.type.create(c.arena, "c_int"), + .Keyword_float => return Tag.type.create(c.arena, "f32"), + .Keyword_short => return Tag.type.create(c.arena, "c_short"), + .Keyword_char => return Tag.type.create(c.arena, "u8"), .Keyword_unsigned => if (m.next()) |t| switch (t) { - .Keyword_char => return Node.type.create(c.arena, "u8"), - .Keyword_short => return Node.type.create(c.arena, "c_ushort"), - .Keyword_int => return Node.type.create(c.arena, "c_uint"), + .Keyword_char => return Tag.type.create(c.arena, "u8"), + .Keyword_short => return Tag.type.create(c.arena, "c_ushort"), + .Keyword_int => return Tag.type.create(c.arena, "c_uint"), .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { _ = m.next(); - return Node.type.create(c.arena, "c_ulonglong"); - } else return Node.type.create(c.arena, "c_ulong"), + return Tag.type.create(c.arena, "c_ulonglong"); + } else return Tag.type.create(c.arena, "c_ulong"), else => { m.i -= 1; - return Node.type.create(c.arena, "c_uint"); + return Tag.type.create(c.arena, "c_uint"); }, } else { - return Node.type.create(c.arena, "c_uint"); + return Tag.type.create(c.arena, "c_uint"); }, .Keyword_signed => if (m.next()) |t| switch (t) { - .Keyword_char => return Node.type.create(c.arena, "i8"), - .Keyword_short => return Node.type.create(c.arena, "c_short"), - .Keyword_int => return Node.type.create(c.arena, "c_int"), + .Keyword_char => return Tag.type.create(c.arena, "i8"), + .Keyword_short => return Tag.type.create(c.arena, "c_short"), + .Keyword_int => return Tag.type.create(c.arena, "c_int"), .Keyword_long => if (m.peek() != null and m.peek().? == .Keyword_long) { _ = m.next(); - return Node.type.create(c.arena, "c_longlong"); - } else return Node.type.create(c.arena, "c_long"), + return Tag.type.create(c.arena, "c_longlong"); + } else return Tag.type.create(c.arena, "c_long"), else => { m.i -= 1; - return Node.type.create(c.arena, "c_int"); + return Tag.type.create(c.arena, "c_int"); }, } else { - return Node.type.create(c.arena, "c_int"); + return Tag.type.create(c.arena, "c_int"); }, .Keyword_enum, .Keyword_struct, .Keyword_union => { // struct Foo will be declared as struct_Foo by transRecordDecl @@ -4451,11 +4425,11 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N } const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() }); - return Node.identifier.create(c.arena, name); + return Tag.identifier.create(c.arena, name); }, .Identifier => { const mangled_name = scope.getAlias(slice); - return Node.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); + return Tag.identifier.create(c.arena, builtin_typedef_map.get(mangled_name) orelse mangled_name); }, .LParen => { const inner_node = try parseCExpr(c, m, scope); @@ -4492,7 +4466,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N return error.ParseError; } - return Node.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast }); + return Tag.std_meta_cast.create(c.arena, .{ .lhs = inner_node, .rhs = node_to_cast }); }, else => { try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); @@ -4511,7 +4485,7 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { .StringLiteral, .Identifier => {}, else => break, } - node = try Node.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) }); + node = try Tag.array_cat.create(c.arena, .{ .lhs = node, .rhs = try parseCPrimaryExprInner(c, m, scope) }); } return node; } @@ -4521,7 +4495,7 @@ fn macroBoolToInt(c: *Context, node: Node) !Node { return node; } - return Node.bool_to_int.create(c.arena, node); + return Tag.bool_to_int.create(c.arena, node); } fn macroIntToBool(c: *Context, node: Node) !Node { @@ -4529,7 +4503,7 @@ fn macroIntToBool(c: *Context, node: Node) !Node { return node; } - return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init() }); + return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); } fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { @@ -4545,7 +4519,7 @@ fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { return error.ParseError; } const else_body = try parseCCondExpr(c, m, scope); - return Node.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body }); + return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body }); } fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { @@ -4553,7 +4527,7 @@ fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (m.next().? == .PipePipe) { const lhs = try macroIntToBool(c, node); const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope)); - node = try Node.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; @@ -4564,7 +4538,7 @@ fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (m.next().? == .AmpersandAmpersand) { const lhs = try macroIntToBool(c, node); const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope)); - node = try Node.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; @@ -4575,7 +4549,7 @@ fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (m.next().? == .Pipe) { const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCBitXorExpr(c, m, scope)); - node = try Node.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; @@ -4586,7 +4560,7 @@ fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (m.next().? == .Caret) { const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCBitAndExpr(c, m, scope)); - node = try Node.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; @@ -4597,7 +4571,7 @@ fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (m.next().? == .Ampersand) { const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCEqExpr(c, m, scope)); - node = try Node.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } m.i -= 1; return node; @@ -4611,13 +4585,13 @@ fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope)); - node = try Node.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .EqualEqual => { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCRelExpr(c, m, scope)); - node = try Node.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } @@ -4632,25 +4606,25 @@ fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); - node = try Node.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketRightEqual => { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); - node = try Node.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketLeft => { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); - node = try Node.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketLeftEqual => { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCShiftExpr(c, m, scope)); - node = try Node.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } @@ -4665,13 +4639,13 @@ fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope)); - node = try Node.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .AngleBracketAngleBracketRight => { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCAddSubExpr(c, m, scope)); - node = try Node.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } @@ -4686,13 +4660,13 @@ fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope)); - node = try Node.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .Minus => { _ = m.next(); const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCMulExpr(c, m, scope)); - node = try Node.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => return node, } @@ -4711,14 +4685,14 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { const prev_id = m.list[m.i - 1].id; if (prev_id == .Keyword_void) { - const ptr = try Node.single_pointer.create(c.arena, .{ + const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = false, .is_volatile = false, .elem_type = node, }); - return Node.optional_type.create(c.arena, ptr); + return Tag.optional_type.create(c.arena, ptr); } else { - return Node.c_pointer.create(c.arena, .{ + return Tag.c_pointer.create(c.arena, .{ .is_const = false, .is_volatile = false, .elem_type = node, @@ -4728,18 +4702,18 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { // expr * expr const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - node = try Node.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); } }, .Slash => { const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - node = try Node.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, .Percent => { const lhs = try macroBoolToInt(c, node); const rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - node = try Node.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); + node = try Tag.mod.create(c.arena, .{ .lhs = lhs, .rhs = rhs }); }, else => { m.i -= 1; @@ -4759,8 +4733,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { return error.ParseError; } - const ident = try Node.identifier.create(c.arena, m.slice()); - node = try Node.field_access.create(c.arena, .{ .lhs = node, .rhs = ident }); + const ident = try Tag.identifier.create(c.arena, m.slice()); + node = try Tag.field_access.create(c.arena, .{ .lhs = node, .rhs = ident }); }, .Arrow => { if (m.next().? != .Identifier) { @@ -4768,20 +4742,20 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { return error.ParseError; } - const deref = try Node.deref.create(c.arena, node); - const ident = try Node.identifier.create(c.arena, m.slice()); - node = try Node.field_access.create(c.arena, .{ .lhs = deref, .rhs = ident }); + const deref = try Tag.deref.create(c.arena, node); + const ident = try Tag.identifier.create(c.arena, m.slice()); + node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .rhs = ident }); }, .LBracket => { const index = try macroBoolToInt(c, try parseCExpr(c, m, scope)); - node = try Node.array_access.create(c.arena, .{ .lhs = node, .rhs = index }); + node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index }); }, .LParen => { - var call_params = std.ArrayList(Node).init(c.gpa); - defer call_params.deinit(); + var args = std.ArrayList(Node).init(c.gpa); + defer args.deinit(); while (true) { const arg = try parseCCondExpr(c, m, scope); - try call_params.append(arg); + try args.append(arg); switch (m.next().?) { .Comma => {}, .RParen => break, @@ -4791,7 +4765,7 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { }, } } - node = try Node.call.create(c.arena, .{ .lhs = node, .rhs = try c.arena.dupe(Node, call_params.items) }); + node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) }); }, .LBrace => { var init_vals = std.ArrayList(Node).init(c.gpa); @@ -4809,8 +4783,8 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { }, } } - const tuple_node = try Node.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items)); - node = try Node.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node }); + const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items)); + node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node }); }, .PlusPlus, .MinusMinus => { try m.fail(c, "TODO postfix inc/dec expr", .{}); @@ -4828,24 +4802,24 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { switch (m.next().?) { .Bang => { const operand = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); - return Node.not.create(c.arena, operand); + return Tag.not.create(c.arena, operand); }, .Minus => { const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - return Node.negate.create(c.arena, operand); + return Tag.negate.create(c.arena, operand); }, .Plus => return try parseCUnaryExpr(c, m, scope), .Tilde => { const operand = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); - return Node.bit_not.create(c.arena, operand); + return Tag.bit_not.create(c.arena, operand); }, .Asterisk => { const operand = try parseCUnaryExpr(c, m, scope); - return Node.deref.create(c.arena, operand); + return Tag.deref.create(c.arena, operand); }, .Ampersand => { const operand = try parseCUnaryExpr(c, m, scope); - return Node.address_of.create(c.arena, operand); + return Tag.address_of.create(c.arena, operand); }, .Keyword_sizeof => { const operand = if (m.peek().? == .LParen) blk: { @@ -4860,7 +4834,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { break :blk inner; } else try parseCUnaryExpr(c, m, scope); - return Node.std_meta_sizeof.create(c.arena, operand); + return Tag.std_meta_sizeof.create(c.arena, operand); }, .Keyword_alignof => { // TODO this won't work if using 's @@ -4877,7 +4851,7 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { return error.ParseError; } - return Node.alignof.create(c.arena, operand); + return Tag.alignof.create(c.arena, operand); }, .PlusPlus, .MinusMinus => { try m.fail(c, "TODO unary inc/dec expr", .{}); @@ -4902,7 +4876,7 @@ fn getContainer(c: *Context, node: Node) ?Node { .negate, .negate_wrap, .array_type, - .c_pointer, + .c_pointer, .single_pointer, => return node, @@ -4910,7 +4884,7 @@ fn getContainer(c: *Context, node: Node) ?Node { const ident = node.castTag(.identifier).?; if (c.global_scope.sym_table.get(ident.data)) |value| { if (value.castTag(.var_decl)) |var_decl| - return getContainer(c, var_decl.data.init); + return getContainer(c, var_decl.data.init.?); if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl| return getContainer(c, var_decl.data.init); } @@ -4923,8 +4897,8 @@ fn getContainer(c: *Context, node: Node) ?Node { if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { for (container.data.fields) |field| { const ident = infix.data.rhs.castTag(.identifier).?; - if (mem.eql(u8, field.data.name, field.data)) { - return getContainer(c, field.type_expr.?); + if (mem.eql(u8, field.name, ident.data)) { + return getContainer(c, field.type); } } } @@ -4960,9 +4934,9 @@ fn getContainerTypeOf(c: *Context, ref: Node) ?Node { } fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func { - const init = if (value.castTag(.var_decl)) |v| - v.data.init - else if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |v| + const init = if (ref.castTag(.var_decl)) |v| + v.data.init orelse return null + else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v| v.data.init else return null; diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 1cf014e5dc..24689c89db 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1,5 +1,6 @@ const std = @import("std"); const Type = @import("../type.zig").Type; +const Allocator = std.mem.Allocator; pub const Node = extern union { /// If the tag value is less than Tag.no_payload_count, then no pointer @@ -20,6 +21,8 @@ pub const Node = extern union { one_literal, void_type, noreturn_type, + @"anytype", + @"continue", /// pub usingnamespace @import("std").c.builtins; usingnamespace_builtins, // After this, the tag requires a payload. @@ -40,7 +43,6 @@ pub const Node = extern union { switch_else, /// lhs => rhs, switch_prong, - @"continue", @"break", break_val, @"return", @@ -60,7 +62,6 @@ pub const Node = extern union { container_init, std_meta_cast, discard, - block, // a + b add, @@ -111,8 +112,11 @@ pub const Node = extern union { equal, not_equal, bit_and, + bit_and_assign, bit_or, + bit_or_assign, bit_xor, + bit_xor_assign, array_cat, ellipsis3, assign, @@ -126,7 +130,7 @@ pub const Node = extern union { rem, /// @divTrunc(lhs, rhs) div_trunc, - /// @boolToInt(lhs, rhs) + /// @boolToInt(operand) bool_to_int, /// @as(lhs, rhs) as, @@ -150,24 +154,26 @@ pub const Node = extern union { ptr_to_int, /// @alignCast(lhs, rhs) align_cast, + /// @ptrCast(lhs, rhs) + ptr_cast, negate, negate_wrap, bit_not, not, address_of, - /// operand.?.* - unwrap_deref, + /// .? + unwrap, /// .* deref, block, /// { operand } block_single, - @"break", sizeof, alignof, + typeof, type, optional_type, @@ -185,6 +191,8 @@ pub const Node = extern union { fail_decl, // var actual = mangled; arg_redecl, + /// pub const alias = actual; + alias, /// const name = init; typedef, var_simple, @@ -204,18 +212,17 @@ pub const Node = extern union { /// _ = operand; ignore, - @"anytype", pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; - pub fn Type(tag: Tag) ?type { - return switch (tag) { + pub fn Type(comptime t: Tag) type { + return switch (t) { .null_literal, .undefined_literal, .opaque_literal, .true_literal, - .false_litral, + .false_literal, .empty_block, .usingnamespace_builtins, .return_void, @@ -224,6 +231,7 @@ pub const Node = extern union { .void_type, .noreturn_type, .@"anytype", + .@"continue", => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .std_mem_zeroes, @@ -236,7 +244,7 @@ pub const Node = extern union { .not, .optional_type, .address_of, - .unwrap_deref, + .unwrap, .deref, .ptr_to_int, .enum_to_int, @@ -246,6 +254,11 @@ pub const Node = extern union { .switch_else, .ignore, .block_single, + .std_meta_sizeof, + .bool_to_int, + .sizeof, + .alignof, + .typeof, => Payload.UnOp, .add, @@ -294,12 +307,14 @@ pub const Node = extern union { .equal, .not_equal, .bit_and, + .bit_and_assign, .bit_or, + .bit_or_assign, .bit_xor, + .bit_xor_assign, .div_trunc, .rem, .int_cast, - .bool_to_int, .as, .truncate, .bit_cast, @@ -316,6 +331,7 @@ pub const Node = extern union { .align_cast, .array_access, .std_mem_zeroinit, + .ptr_cast, => Payload.BinOp, .number_literal, @@ -324,8 +340,6 @@ pub const Node = extern union { .identifier, .warning, .failed_decl, - .sizeof, - .alignof, .type, .fail_decl, => Payload.Value, @@ -345,7 +359,7 @@ pub const Node = extern union { .block => Payload.Block, .c_pointer, .single_pointer => Payload.Pointer, .array_type => Payload.Array, - .arg_redecl => Payload.ArgRedecl, + .arg_redecl, .alias => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, @@ -375,7 +389,7 @@ pub const Node = extern union { pub fn tag(self: Node) 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; } @@ -392,16 +406,16 @@ pub const Node = extern union { } pub fn initPayload(payload: *Payload) Node { - assert(@enumToInt(payload.tag) >= Tag.no_payload_count); + std.debug.assert(@enumToInt(payload.tag) >= Tag.no_payload_count); return .{ .ptr_otherwise = payload }; } }; pub const Payload = struct { - tag: Tag, + tag: Node.Tag, pub const Infix = struct { - base: Node, + base: Payload, data: struct { lhs: Node, rhs: Node, @@ -409,17 +423,17 @@ pub const Payload = struct { }; pub const Value = struct { - base: Node, + base: Payload, data: []const u8, }; pub const UnOp = struct { - base: Node, + base: Payload, data: Node, }; pub const BinOp = struct { - base: Node, + base: Payload, data: struct { lhs: Node, rhs: Node, @@ -427,7 +441,7 @@ pub const Payload = struct { }; pub const If = struct { - base: Node = .{ .tag = .@"if" }, + base: Payload, data: struct { cond: Node, then: Node, @@ -436,7 +450,7 @@ pub const Payload = struct { }; pub const While = struct { - base: Node = .{ .tag = .@"while" }, + base: Payload, data: struct { cond: Node, body: Node, @@ -445,7 +459,7 @@ pub const Payload = struct { }; pub const Switch = struct { - base: Node = .{ .tag = .@"switch" }, + base: Payload, data: struct { cond: Node, cases: []Node, @@ -453,12 +467,12 @@ pub const Payload = struct { }; pub const Break = struct { - base: Node = .{ .tag = .@"break" }, + base: Payload, data: ?[]const u8, }; pub const BreakVal = struct { - base: Node = .{ .tag = .break_val }, + base: Payload, data: struct { label: ?[]const u8, val: Node, @@ -466,7 +480,7 @@ pub const Payload = struct { }; pub const Call = struct { - base: Node = .{.call}, + base: Payload, data: struct { lhs: Node, args: []Node, @@ -474,7 +488,7 @@ pub const Payload = struct { }; pub const VarDecl = struct { - base: Node = .{ .tag = .var_decl }, + base: Payload, data: struct { is_pub: bool, is_const: bool, @@ -489,13 +503,13 @@ pub const Payload = struct { }; pub const Func = struct { - base: Node = .{.func}, + base: Payload, data: struct { is_pub: bool, is_extern: bool, is_export: bool, is_var_args: bool, - name: []const u8, + name: ?[]const u8, linksection_string: ?[]const u8, explicit_callconv: ?std.builtin.CallingConvention, params: []Param, @@ -512,7 +526,7 @@ pub const Payload = struct { }; pub const Enum = struct { - base: Node = .{ .tag = .@"enum" }, + base: Payload, data: []Field, pub const Field = struct { @@ -522,9 +536,9 @@ pub const Payload = struct { }; pub const Record = struct { - base: Node, + base: Payload, data: struct { - @"packed": bool, + is_packed: bool, fields: []Field, }, @@ -536,12 +550,12 @@ pub const Payload = struct { }; pub const ArrayInit = struct { - base: Node = .{ .tag = .array_init }, + base: Payload, data: []Node, }; pub const ContainerInit = struct { - base: Node = .{ .tag = .container_init }, + base: Payload, data: []Initializer, pub const Initializer = struct { @@ -551,7 +565,7 @@ pub const Payload = struct { }; pub const Block = struct { - base: Node, + base: Payload, data: struct { label: ?[]const u8, stmts: []Node @@ -559,15 +573,15 @@ pub const Payload = struct { }; pub const Array = struct { - base: Node, + base: Payload, data: struct { elem_type: Node, - len: Node, + len: usize, }, }; pub const Pointer = struct { - base: Node, + base: Payload, data: struct { elem_type: Node, is_const: bool, @@ -576,7 +590,7 @@ pub const Payload = struct { }; pub const ArgRedecl = struct { - base: Node, + base: Payload, data: struct { actual: []const u8, mangled: []const u8, @@ -584,12 +598,12 @@ pub const Payload = struct { }; pub const Log2IntType = struct { - base: Node, + base: Payload, data: std.math.Log2Int(u64), }; pub const SimpleVarDecl = struct { - base: Node, + base: Payload, data: struct { name: []const u8, init: Node, @@ -597,7 +611,7 @@ pub const Payload = struct { }; pub const EnumRedecl = struct { - base: Node, + base: Payload, data: struct { enum_val_name: []const u8, field_name: []const u8, @@ -606,7 +620,7 @@ pub const Payload = struct { }; pub const ArrayFiller = struct { - base: Node, + base: Payload, data: struct { type: Node, filler: Node, @@ -615,7 +629,7 @@ pub const Payload = struct { }; pub const PubInlineFn = struct { - base: Node, + base: Payload, data: struct { name: []const u8, params: []Param, @@ -626,6 +640,6 @@ pub const Payload = struct { }; /// Converts the nodes into a Zig ast. -pub fn render(allocator: *Allocator, nodes: []const Node) !*ast.Tree { +pub fn render(allocator: *Allocator, nodes: []const Node) !std.zig.ast.Tree { @panic("TODO"); } diff --git a/src/type.zig b/src/type.zig index 8fcaba6fad..38fe6dd3e6 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1682,6 +1682,8 @@ pub const Type = extern union { .i32 => unreachable, .u64 => unreachable, .i64 => unreachable, + .u128 => unreachable, + .i128 => unreachable, .usize => unreachable, .isize => unreachable, .c_short => unreachable, @@ -2197,6 +2199,8 @@ pub const Type = extern union { .i32 => .{ .signedness = .signed, .bits = 32 }, .u64 => .{ .signedness = .unsigned, .bits = 64 }, .i64 => .{ .signedness = .signed, .bits = 64 }, + .u128 => .{ .signedness = .unsigned, .bits = 128 }, + .i128 => .{ .signedness = .signed, .bits = 128 }, .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, From 13a9db208566449cd6bcfa5fd77f2707f7c9f394 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 12 Feb 2021 13:53:04 +0200 Subject: [PATCH 17/36] translate-c: begin implementing ast.render --- CMakeLists.txt | 1 + src/translate_c/ast.zig | 184 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0c3ae84fa..71dd2c0cee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" "${CMAKE_SOURCE_DIR}/src/zir.zig" "${CMAKE_SOURCE_DIR}/src/zir_sema.zig" + "${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig" ) if(MSVC) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 24689c89db..8e18b55c04 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -454,7 +454,7 @@ pub const Payload = struct { data: struct { cond: Node, body: Node, - cont_expr: ?Node + cont_expr: ?Node, }, }; @@ -568,7 +568,7 @@ pub const Payload = struct { base: Payload, data: struct { label: ?[]const u8, - stmts: []Node + stmts: []Node, }, }; @@ -640,6 +640,182 @@ pub const Payload = struct { }; /// Converts the nodes into a Zig ast. -pub fn render(allocator: *Allocator, nodes: []const Node) !std.zig.ast.Tree { - @panic("TODO"); +/// Caller must free the source slice. +pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.ast.Tree { + var ctx = Context{ + .gpa = gpa, + .buf = std.ArrayList(u8).init(gpa), + }; + defer ctx.buf.deinit(); + defer ctx.nodes.deinit(gpa); + defer ctx.extra_data.deinit(gpa); + defer ctx.tokens.deinit(gpa); + + // Estimate that each top level node has 25 child nodes. + const estimated_node_count = nodes.len * 25; + try ctx.nodes.ensureCapacity(gpa, estimated_node_count); + + ctx.nodes.appendAssumeCapacity(.{ + .tag = .root, + .main_token = 0, + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + const root_members = try renderNodes(&ctx, nodes); + ctx.nodes.items(.data)[0] = .{ + .lhs = root_members.start, + .rhs = root_members.end, + }; + + try ctx.tokens.append(gpa, .{ + .tag = .eof, + .start = @intCast(u32, ctx.buf.items.len), + }); + + return std.zig.ast.Tree{ + .source = ctx.buf.toOwnedSlice(), + .tokens = ctx.tokens.toOwnedSlice(), + .nodes = ctx.nodes.toOwnedSlice(), + .extra_data = ctx.extra_data.toOwnedSlice(gpa), + .errors = &.{}, + }; +} + +const NodeIndex = std.zig.ast.Node.Index; +const NodeSubRange = std.zig.ast.Node.SubRange; +const TokenIndex = std.zig.ast.TokenIndex; +const TokenTag = std.zig.Token.Tag; + +const Context = struct { + gpa: *Allocator, + buf: std.ArrayList(u8) = .{}, + nodes: std.zig.ast.NodeList = .{}, + extra_data: std.ArrayListUnmanaged(std.zig.ast.Node.Index) = .{}, + tokens: std.zig.ast.TokenList = .{}, + + fn appendTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex { + const start_index = c.buf.items.len; + try c.buf.writer().print(format ++ " ", args); + + try c.tokens.append(c.gpa, .{ + .tag = tag, + .start = @intCast(u32, start_index), + }); + + return @intCast(u32, c.tokens.len - 1); + } + + fn appendToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex { + std.debug.assert(tag != .identifier); // use appendIdentifier + return appendTokenFmt(c, tag, "{s}", .{bytes}); + } + + fn appendIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { + return appendTokenFmt(c, .identifier, "{s}", .{std.zig.fmtId(bytes)}); + } + + fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange { + try c.extra_data.appendSlice(c.gpa, list); + return NodeSubRange{ + .start = @intCast(NodeIndex, c.extra_data.items.len - list.len), + .end = @intCast(NodeIndex, c.extra_data.items.len), + }; + } + + fn appendNode(c: *Context, elem: std.zig.ast.NodeList.Elem) Allocator.Error!NodeIndex { + const result = @intCast(NodeIndex, c.nodes.len); + try c.nodes.append(c.gpa, elem); + return result; + } +}; + +fn renderNodes(c: *Context, nodes: []const Node) !NodeSubRange { + var result = std.ArrayList(NodeIndex).init(c.gpa); + defer result.deinit(); + + for (nodes) |node| { + const res = try renderNode(c, node); + if (res == 0) continue; + try result.append(res); + } + + return try c.listToSpan(result.items); +} + +fn renderNode(c: *Context, node: Node) !NodeIndex { + switch (node.tag()) { + .warning => { + const payload = node.castTag(.warning).?; + try c.buf.appendSlice(payload.data); + try c.buf.append('\n'); + return 0; + }, + .usingnamespace_builtins => { + // pub usingnamespace @import("std").c.builtins; + _ = try c.appendToken(.keyword_pub, "pub"); + const usingnamespace_token = try c.appendToken(.keyword_usingnamespace, "usingnamespace"); + const import_node = try renderStdImport(c, "c", "builtins"); + _ = try c.appendToken(.semicolon, ";"); + + return c.appendNode(.{ + .tag = .@"usingnamespace", + .main_token = usingnamespace_token, + .data = .{ + .lhs = import_node, + .rhs = undefined, + }, + }); + }, + else => { + try c.buf.writer().print("// TODO renderNode {}\n", .{node.tag()}); + return @as(u32, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' + }, + } +} + +fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { + const import_tok = try c.appendToken(.builtin, "@import"); + _ = try c.appendToken(.l_paren, "("); + + const std_tok = try c.appendToken(.string_literal, "\"std\""); + const std_node = try c.appendNode(.{ + .tag = .string_literal, + .main_token = std_tok, + .data = .{ + .lhs = std_tok, + .rhs = std_tok, + }, + }); + + _ = try c.appendToken(.r_paren, ")"); + + const import_node = try c.appendNode(.{ + .tag = .builtin_call_two, + .main_token = import_tok, + .data = .{ + .lhs = std_node, + .rhs = 0, + }, + }); + + var access_chain = import_node; + access_chain = try c.appendNode(.{ + .tag = .field_access, + .main_token = try c.appendToken(.period, "."), + .data = .{ + .lhs = access_chain, + .rhs = try c.appendIdentifier(first), + }, + }); + access_chain = try c.appendNode(.{ + .tag = .field_access, + .main_token = try c.appendToken(.period, "."), + .data = .{ + .lhs = access_chain, + .rhs = try c.appendIdentifier(second), + }, + }); + return access_chain; } From d7460db044ef6649486a27f2b9ebb1de9e2ce2b0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 13 Feb 2021 17:57:52 +0200 Subject: [PATCH 18/36] translate-c: render a bunch of simple nodes and calls --- lib/std/zig/render.zig | 9 +- src/translate_c.zig | 5 +- src/translate_c/ast.zig | 297 +++++++++++++++++++++++++++++++++++----- 3 files changed, 270 insertions(+), 41 deletions(-) diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 2d9c2ae9a9..6fd091d32c 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -550,14 +550,11 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac .builtin_call_two, .builtin_call_two_comma => { if (datas[node].lhs == 0) { - const params = [_]ast.Node.Index{}; - return renderBuiltinCall(ais, tree, main_tokens[node], ¶ms, space); + return renderBuiltinCall(ais, tree, main_tokens[node], &.{}, space); } else if (datas[node].rhs == 0) { - const params = [_]ast.Node.Index{datas[node].lhs}; - return renderBuiltinCall(ais, tree, main_tokens[node], ¶ms, space); + return renderBuiltinCall(ais, tree, main_tokens[node], &.{datas[node].lhs}, space); } else { - const params = [_]ast.Node.Index{ datas[node].lhs, datas[node].rhs }; - return renderBuiltinCall(ais, tree, main_tokens[node], ¶ms, space); + return renderBuiltinCall(ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space); } }, .builtin_call, .builtin_call_comma => { diff --git a/src/translate_c.zig b/src/translate_c.zig index c7a30ff919..9b80582ab5 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3054,6 +3054,7 @@ fn maybeSuppressResult( fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { _ = try c.global_scope.sym_table.put(name, decl_node); + try c.global_scope.nodes.append(decl_node); } /// Translate a qual type for a variable with an initializer. The initializer @@ -3903,10 +3904,10 @@ fn fail( pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) Error!void { // location // pub const name = @compileError(msg); + const fail_msg = try std.fmt.allocPrint(c.arena, format, args); + try c.global_scope.nodes.append(try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg })); const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)}); try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment)); - const fail_msg = try std.fmt.allocPrint(c.arena, format, args); - try c.global_scope.nodes.append(try Tag.fail_decl.create(c.arena, fail_msg)); } pub fn freeErrors(errors: []ClangErrMsg) void { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 8e18b55c04..4e91f13757 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -52,7 +52,6 @@ pub const Node = extern union { var_decl, func, warning, - failed_decl, /// All enums are non-exhaustive @"enum", @"struct", @@ -339,9 +338,7 @@ pub const Node = extern union { .char_literal, .identifier, .warning, - .failed_decl, .type, - .fail_decl, => Payload.Value, .@"if" => Payload.If, .@"while" => Payload.While, @@ -359,7 +356,7 @@ pub const Node = extern union { .block => Payload.Block, .c_pointer, .single_pointer => Payload.Pointer, .array_type => Payload.Array, - .arg_redecl, .alias => Payload.ArgRedecl, + .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, @@ -695,7 +692,7 @@ const Context = struct { extra_data: std.ArrayListUnmanaged(std.zig.ast.Node.Index) = .{}, tokens: std.zig.ast.TokenList = .{}, - fn appendTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex { + fn addTokenFmt(c: *Context, tag: TokenTag, comptime format: []const u8, args: anytype) Allocator.Error!TokenIndex { const start_index = c.buf.items.len; try c.buf.writer().print(format ++ " ", args); @@ -707,13 +704,13 @@ const Context = struct { return @intCast(u32, c.tokens.len - 1); } - fn appendToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex { - std.debug.assert(tag != .identifier); // use appendIdentifier - return appendTokenFmt(c, tag, "{s}", .{bytes}); + fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex { + std.debug.assert(tag != .identifier); // use addIdentifier + return addTokenFmt(c, tag, "{s}", .{bytes}); } - fn appendIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { - return appendTokenFmt(c, .identifier, "{s}", .{std.zig.fmtId(bytes)}); + fn addIdentifier(c: *Context, bytes: []const u8) Allocator.Error!TokenIndex { + return addTokenFmt(c, .identifier, "{s}", .{std.zig.fmtId(bytes)}); } fn listToSpan(c: *Context, list: []const NodeIndex) Allocator.Error!NodeSubRange { @@ -724,14 +721,14 @@ const Context = struct { }; } - fn appendNode(c: *Context, elem: std.zig.ast.NodeList.Elem) Allocator.Error!NodeIndex { + fn addNode(c: *Context, elem: std.zig.ast.NodeList.Elem) Allocator.Error!NodeIndex { const result = @intCast(NodeIndex, c.nodes.len); try c.nodes.append(c.gpa, elem); return result; } }; -fn renderNodes(c: *Context, nodes: []const Node) !NodeSubRange { +fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { var result = std.ArrayList(NodeIndex).init(c.gpa); defer result.deinit(); @@ -744,7 +741,7 @@ fn renderNodes(c: *Context, nodes: []const Node) !NodeSubRange { return try c.listToSpan(result.items); } -fn renderNode(c: *Context, node: Node) !NodeIndex { +fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { switch (node.tag()) { .warning => { const payload = node.castTag(.warning).?; @@ -754,12 +751,12 @@ fn renderNode(c: *Context, node: Node) !NodeIndex { }, .usingnamespace_builtins => { // pub usingnamespace @import("std").c.builtins; - _ = try c.appendToken(.keyword_pub, "pub"); - const usingnamespace_token = try c.appendToken(.keyword_usingnamespace, "usingnamespace"); + _ = try c.addToken(.keyword_pub, "pub"); + const usingnamespace_token = try c.addToken(.keyword_usingnamespace, "usingnamespace"); const import_node = try renderStdImport(c, "c", "builtins"); - _ = try c.appendToken(.semicolon, ";"); + _ = try c.addToken(.semicolon, ";"); - return c.appendNode(.{ + return c.addNode(.{ .tag = .@"usingnamespace", .main_token = usingnamespace_token, .data = .{ @@ -768,6 +765,196 @@ fn renderNode(c: *Context, node: Node) !NodeIndex { }, }); }, + .std_math_Log2Int => { + const payload = node.castTag(.std_math_Log2Int).?; + const import_node = try renderStdImport(c, "math", "Log2Int"); + return renderCall(c, import_node, &.{payload.data}); + }, + .std_meta_cast => { + const payload = node.castTag(.std_meta_cast).?; + const import_node = try renderStdImport(c, "meta", "cast"); + return renderCall(c, import_node, &.{ payload.data.lhs, payload.data.rhs }); + }, + .std_meta_sizeof => { + const payload = node.castTag(.std_meta_sizeof).?; + const import_node = try renderStdImport(c, "meta", "sizeof"); + return renderCall(c, import_node, &.{payload.data}); + }, + .std_mem_zeroes => { + const payload = node.castTag(.std_mem_zeroes).?; + const import_node = try renderStdImport(c, "mem", "zeroes"); + return renderCall(c, import_node, &.{payload.data}); + }, + .std_mem_zeroinit => { + const payload = node.castTag(.std_mem_zeroinit).?; + const import_node = try renderStdImport(c, "mem", "zeroInit"); + return renderCall(c, import_node, &.{ payload.data.lhs, payload.data.rhs }); + }, + .call => { + const payload = node.castTag(.call).?; + const lhs = try renderNode(c, payload.data.lhs); + return renderCall(c, lhs, payload.data.args); + }, + .null_literal => return c.addNode(.{ + .tag = .null_literal, + .main_token = try c.addToken(.keyword_null, "null"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .undefined_literal => return c.addNode(.{ + .tag = .undefined_literal, + .main_token = try c.addToken(.keyword_undefined, "undefined"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .true_literal => return c.addNode(.{ + .tag = .true_literal, + .main_token = try c.addToken(.keyword_true, "true"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .false_literal => return c.addNode(.{ + .tag = .false_literal, + .main_token = try c.addToken(.keyword_false, "false"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .zero_literal => return c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addToken(.integer_literal, "0"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .one_literal => return c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addToken(.integer_literal, "1"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .void_type => return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.identifier, "void"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .@"anytype" => return try c.addNode(.{ + .tag = .@"anytype", + .main_token = try c.addToken(.keyword_anytype, "anytype"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), + .type => { + const payload = node.castTag(.type).?; + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.identifier, payload.data), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, + .identifier => { + const payload = node.castTag(.identifier).?; + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addIdentifier(payload.data), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, + .number_literal => { + const payload = node.castTag(.number_literal).?; + return c.addNode(.{ + .tag = .identifier, + // might be integer or float, but it doesn't matter for rendering + .main_token = try c.addToken(.integer_literal, payload.data), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, + .string_literal => { + const payload = node.castTag(.string_literal).?; + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.char_literal, payload.data), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, + .char_literal => { + const payload = node.castTag(.char_literal).?; + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.string_literal, payload.data), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, + .fail_decl => { + const payload = node.castTag(.fail_decl).?; + // pub const name = @compileError(msg); + _ = try c.addToken(.keyword_pub, "pub"); + const const_kw = try c.addToken(.keyword_const, "const"); + _ = try c.addIdentifier(payload.data.actual); + _ = try c.addToken(.equal, "="); + + + 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.data.mangled)}); + const err_msg = try c.addNode(.{ + .tag = .string_literal, + .main_token = err_msg_tok, + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + _ = try c.addToken(.r_paren, ")"); + const compile_error = try c.addNode(.{ + .tag = .builtin_call_two, + .main_token = compile_error_tok, + .data = .{ + .lhs = err_msg, + .rhs = 0, + }, + }); + _ = try c.addToken(.semicolon, ";"); + + return c.addNode(.{ + .tag = .simple_var_decl, + .main_token = const_kw, + .data = .{ + .lhs = 0, + .rhs = compile_error, + } + }); + }, else => { try c.buf.writer().print("// TODO renderNode {}\n", .{node.tag()}); return @as(u32, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' @@ -776,22 +963,20 @@ fn renderNode(c: *Context, node: Node) !NodeIndex { } fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { - const import_tok = try c.appendToken(.builtin, "@import"); - _ = try c.appendToken(.l_paren, "("); - - const std_tok = try c.appendToken(.string_literal, "\"std\""); - const std_node = try c.appendNode(.{ + const import_tok = try c.addToken(.builtin, "@import"); + _ = try c.addToken(.l_paren, "("); + const std_tok = try c.addToken(.string_literal, "\"std\""); + const std_node = try c.addNode(.{ .tag = .string_literal, .main_token = std_tok, .data = .{ - .lhs = std_tok, - .rhs = std_tok, + .lhs = undefined, + .rhs = undefined, }, }); + _ = try c.addToken(.r_paren, ")"); - _ = try c.appendToken(.r_paren, ")"); - - const import_node = try c.appendNode(.{ + const import_node = try c.addNode(.{ .tag = .builtin_call_two, .main_token = import_tok, .data = .{ @@ -801,21 +986,67 @@ fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeInde }); var access_chain = import_node; - access_chain = try c.appendNode(.{ + access_chain = try c.addNode(.{ .tag = .field_access, - .main_token = try c.appendToken(.period, "."), + .main_token = try c.addToken(.period, "."), .data = .{ .lhs = access_chain, - .rhs = try c.appendIdentifier(first), + .rhs = try c.addIdentifier(first), }, }); - access_chain = try c.appendNode(.{ + access_chain = try c.addNode(.{ .tag = .field_access, - .main_token = try c.appendToken(.period, "."), + .main_token = try c.addToken(.period, "."), .data = .{ .lhs = access_chain, - .rhs = try c.appendIdentifier(second), + .rhs = try c.addIdentifier(second), }, }); return access_chain; } + +fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex { + const lparen = try c.addToken(.l_paren, "("); + const res = switch (args.len) { + 0 => try c.addNode(.{ + .tag = .call_one, + .main_token = lparen, + .data = .{ + .lhs = lhs, + .rhs = 0, + }, + }), + 1 => blk: { + const arg = try renderNode(c, args[0]); + break :blk try c.addNode(.{ + .tag = .call_one, + .main_token = lparen, + .data = .{ + .lhs = lhs, + .rhs = arg, + }, + }); + }, + else => blk: { + const start = @intCast(u32, c.extra_data.items.len); + const end = @intCast(u32, start + args.len); + try c.extra_data.ensureCapacity(c.gpa, end + 2); // + 2 for span start + end + for (args) |arg, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + c.extra_data.appendAssumeCapacity(try renderNode(c, arg)); + } + c.extra_data.appendAssumeCapacity(start); + c.extra_data.appendAssumeCapacity(end); + break :blk try c.addNode(.{ + .tag = .call_comma, + .main_token = lparen, + .data = .{ + .lhs = lhs, + .rhs = end + 2, + }, + }); + }, + }; + _ = try c.addToken(.r_paren, ")"); + return res; +} From 1147ecc5fda6d46c5e4d02eeda18238f34ae56dd Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 13 Feb 2021 20:50:39 +0200 Subject: [PATCH 19/36] translate-c: render variables and builtin calls --- src/translate_c.zig | 31 ++-- src/translate_c/ast.zig | 312 ++++++++++++++++++++++++++++++++++------ 2 files changed, 282 insertions(+), 61 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 9b80582ab5..821f3c5b74 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -769,7 +769,7 @@ fn transCreateNodeTypedef( const payload = try c.arena.create(ast.Payload.SimpleVarDecl); payload.* = .{ - .base = .{ .tag = ([2]Tag{ .typedef, .pub_typedef })[@boolToInt(toplevel)] }, + .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(toplevel)] }, .data = .{ .name = checked_name, .init = init_node, @@ -1678,7 +1678,7 @@ fn transStringLiteralAsArray( init_list[i] = try transCreateCharLitNode(c, narrow, code_unit); } while (i < array_size) : (i += 1) { - init_list[i] = try transCreateNodeNumber(c, 0); + init_list[i] = try transCreateNodeNumber(c, 0, .int); } return Tag.array_init.create(c.arena, init_list); @@ -2345,7 +2345,7 @@ fn transCharLiteral( // C has a somewhat obscure feature called multi-character character constant // e.g. 'abcd' const int_lit_node = if (kind == .Ascii and val > 255) - try transCreateNodeNumber(c, val) + try transCreateNodeNumber(c, val, .int) else try transCreateCharLitNode(c, narrow, val); @@ -2948,7 +2948,7 @@ fn transBreak(c: *Context, scope: *Scope) TransError!Node { fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { // TODO use something more accurate const dbl = stmt.getValueAsApproximateDouble(); - const node = try transCreateNodeNumber(c, dbl); + const node = try transCreateNodeNumber(c, dbl, .float); return maybeSuppressResult(c, scope, used, node); } @@ -3471,13 +3471,16 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, }; - return Tag.number_literal.create(c.arena, str); + return Tag.integer_literal.create(c.arena, str); } -fn transCreateNodeNumber(c: *Context, int: anytype) !Node { - const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(int))) "{d}" else "{s}"; - const str = try std.fmt.allocPrint(c.arena, fmt_s, .{int}); - return Tag.number_literal.create(c.arena, str); +fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node { + const fmt_s = if (comptime std.meta.trait.isNumber(@TypeOf(num))) "{d}" else "{s}"; + const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num}); + if (num_kind == .float) + return Tag.float_literal.create(c.arena, str) + else + return Tag.integer_literal.create(c.arena, str); } fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node { @@ -4162,7 +4165,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { } if (suffix == .none) { - return transCreateNodeNumber(c, lit_bytes); + return transCreateNodeNumber(c, lit_bytes, .int); } const type_node = try Tag.type.create(c.arena, switch (suffix) { @@ -4179,21 +4182,21 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node { .llu => 3, else => unreachable, }]; - const rhs = try transCreateNodeNumber(c, lit_bytes); + const rhs = try transCreateNodeNumber(c, lit_bytes, .int); return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); }, .FloatLiteral => |suffix| { if (lit_bytes[0] == '.') lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes}); if (suffix == .none) { - return transCreateNodeNumber(c, lit_bytes); + return transCreateNodeNumber(c, lit_bytes, .float); } const type_node = try Tag.type.create(c.arena, switch (suffix) { .f => "f32", .l => "c_longdouble", else => unreachable, }); - const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1]); + const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1], .float); return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs }); }, else => unreachable, @@ -4369,7 +4372,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!N return Tag.char_literal.create(c.arena, try zigifyEscapeSequences(c, m)); } else { const str = try std.fmt.allocPrint(c.arena, "0x{x}", .{slice[1 .. slice.len - 1]}); - return Tag.number_literal.create(c.arena, str); + return Tag.integer_literal.create(c.arena, str); } }, .StringLiteral => { diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 4e91f13757..282e645f2e 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -27,8 +27,8 @@ pub const Node = extern union { usingnamespace_builtins, // After this, the tag requires a payload. - // int or float, doesn't really matter - number_literal, + integer_literal, + float_literal, string_literal, char_literal, identifier, @@ -193,10 +193,8 @@ pub const Node = extern union { /// pub const alias = actual; alias, /// const name = init; - typedef, var_simple, /// pub const name = init; - pub_typedef, pub_var_simple, /// pub const enum_field_name = @enumToInt(enum_name.field_name); enum_redecl, @@ -333,7 +331,8 @@ pub const Node = extern union { .ptr_cast, => Payload.BinOp, - .number_literal, + .integer_literal, + .float_literal, .string_literal, .char_literal, .identifier, @@ -358,7 +357,7 @@ pub const Node = extern union { .array_type => Payload.Array, .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, - .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, + .var_simple, .pub_var_simple => Payload.SimpleVarDecl, .enum_redecl => Payload.EnumRedecl, .array_filler => Payload.ArrayFiller, .pub_inline_fn => Payload.PubInlineFn, @@ -705,7 +704,6 @@ const Context = struct { } fn addToken(c: *Context, tag: TokenTag, bytes: []const u8) Allocator.Error!TokenIndex { - std.debug.assert(tag != .identifier); // use addIdentifier return addTokenFmt(c, tag, "{s}", .{bytes}); } @@ -726,6 +724,17 @@ const Context = struct { try c.nodes.append(c.gpa, elem); return result; } + + fn addExtra(c: *Context, extra: anytype) Allocator.Error!NodeIndex { + const fields = std.meta.fields(@TypeOf(extra)); + try c.extra_data.ensureCapacity(c.gpa, c.extra_data.items.len + fields.len); + const result = @intCast(u32, c.extra_data.items.len); + inline for (fields) |field| { + comptime std.debug.assert(field.field_type == NodeIndex); + c.extra_data.appendAssumeCapacity(@field(extra, field.name)); + } + return result; + } }; fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { @@ -734,7 +743,8 @@ fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { for (nodes) |node| { const res = try renderNode(c, node); - if (res == 0) continue; + if (node.tag() == .warning) continue; + if (c.nodes.items(.tag)[res] == .identifier) continue; // TODO remove try result.append(res); } @@ -744,10 +754,10 @@ fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { switch (node.tag()) { .warning => { - const payload = node.castTag(.warning).?; - try c.buf.appendSlice(payload.data); + const payload = node.castTag(.warning).?.data; + try c.buf.appendSlice(payload); try c.buf.append('\n'); - return 0; + return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' }, .usingnamespace_builtins => { // pub usingnamespace @import("std").c.builtins; @@ -766,34 +776,34 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .std_math_Log2Int => { - const payload = node.castTag(.std_math_Log2Int).?; + const payload = node.castTag(.std_math_Log2Int).?.data; const import_node = try renderStdImport(c, "math", "Log2Int"); - return renderCall(c, import_node, &.{payload.data}); + return renderCall(c, import_node, &.{payload}); }, .std_meta_cast => { - const payload = node.castTag(.std_meta_cast).?; + const payload = node.castTag(.std_meta_cast).?.data; const import_node = try renderStdImport(c, "meta", "cast"); - return renderCall(c, import_node, &.{ payload.data.lhs, payload.data.rhs }); + return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); }, .std_meta_sizeof => { - const payload = node.castTag(.std_meta_sizeof).?; + const payload = node.castTag(.std_meta_sizeof).?.data; const import_node = try renderStdImport(c, "meta", "sizeof"); - return renderCall(c, import_node, &.{payload.data}); + return renderCall(c, import_node, &.{payload}); }, .std_mem_zeroes => { - const payload = node.castTag(.std_mem_zeroes).?; + const payload = node.castTag(.std_mem_zeroes).?.data; const import_node = try renderStdImport(c, "mem", "zeroes"); - return renderCall(c, import_node, &.{payload.data}); + return renderCall(c, import_node, &.{payload}); }, .std_mem_zeroinit => { - const payload = node.castTag(.std_mem_zeroinit).?; + const payload = node.castTag(.std_mem_zeroinit).?.data; const import_node = try renderStdImport(c, "mem", "zeroInit"); - return renderCall(c, import_node, &.{ payload.data.lhs, payload.data.rhs }); + return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); }, .call => { - const payload = node.castTag(.call).?; - const lhs = try renderNode(c, payload.data.lhs); - return renderCall(c, lhs, payload.data.args); + const payload = node.castTag(.call).?.data; + const lhs = try renderNode(c, payload.lhs); + return renderCall(c, lhs, payload.args); }, .null_literal => return c.addNode(.{ .tag = .null_literal, @@ -860,10 +870,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }), .type => { - const payload = node.castTag(.type).?; + const payload = node.castTag(.type).?.data; return c.addNode(.{ .tag = .identifier, - .main_token = try c.addToken(.identifier, payload.data), + .main_token = try c.addToken(.identifier, payload), .data = .{ .lhs = undefined, .rhs = undefined, @@ -871,22 +881,32 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .identifier => { - const payload = node.castTag(.identifier).?; + const payload = node.castTag(.identifier).?.data; return c.addNode(.{ .tag = .identifier, - .main_token = try c.addIdentifier(payload.data), + .main_token = try c.addIdentifier(payload), .data = .{ .lhs = undefined, .rhs = undefined, }, }); }, - .number_literal => { - const payload = node.castTag(.number_literal).?; + .float_literal => { + const payload = node.castTag(.float_literal).?.data; return c.addNode(.{ - .tag = .identifier, - // might be integer or float, but it doesn't matter for rendering - .main_token = try c.addToken(.integer_literal, payload.data), + .tag = .float_literal, + .main_token = try c.addToken(.float_literal, payload), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, + .integer_literal => { + const payload = node.castTag(.integer_literal).?.data; + return c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addToken(.integer_literal, payload), .data = .{ .lhs = undefined, .rhs = undefined, @@ -894,10 +914,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .string_literal => { - const payload = node.castTag(.string_literal).?; + const payload = node.castTag(.string_literal).?.data; return c.addNode(.{ .tag = .identifier, - .main_token = try c.addToken(.char_literal, payload.data), + .main_token = try c.addToken(.string_literal, payload), .data = .{ .lhs = undefined, .rhs = undefined, @@ -905,10 +925,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .char_literal => { - const payload = node.castTag(.char_literal).?; + const payload = node.castTag(.char_literal).?.data; return c.addNode(.{ .tag = .identifier, - .main_token = try c.addToken(.string_literal, payload.data), + .main_token = try c.addToken(.string_literal, payload), .data = .{ .lhs = undefined, .rhs = undefined, @@ -916,17 +936,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .fail_decl => { - const payload = node.castTag(.fail_decl).?; + const payload = node.castTag(.fail_decl).?.data; // pub const name = @compileError(msg); _ = try c.addToken(.keyword_pub, "pub"); - const const_kw = try c.addToken(.keyword_const, "const"); - _ = try c.addIdentifier(payload.data.actual); + const const_tok = try c.addToken(.keyword_const, "const"); + _ = try c.addIdentifier(payload.actual); _ = try c.addToken(.equal, "="); - 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.data.mangled)}); + const err_msg_tok = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(payload.mangled)}); const err_msg = try c.addNode(.{ .tag = .string_literal, .main_token = err_msg_tok, @@ -948,17 +967,105 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .simple_var_decl, - .main_token = const_kw, + .main_token = const_tok, .data = .{ .lhs = 0, .rhs = compile_error, - } + }, }); }, - else => { - try c.buf.writer().print("// TODO renderNode {}\n", .{node.tag()}); - return @as(u32, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32' + .pub_var_simple, .var_simple => { + const payload = @fieldParentPtr(Payload.SimpleVarDecl, "base", node.ptr_otherwise).data; + if (node.tag() == .pub_var_simple) _ = try c.addToken(.keyword_pub, "pub"); + const const_tok = try c.addToken(.keyword_const, "const"); + _ = try c.addIdentifier(payload.name); + _ = try c.addToken(.equal, "="); + + const init = try renderNode(c, payload.init); + _ = try c.addToken(.semicolon, ";"); + + return c.addNode(.{ + .tag = .simple_var_decl, + .main_token = const_tok, + .data = .{ + .lhs = 0, + .rhs = init, + }, + }); }, + .var_decl => return renderVar(c, node), + .int_cast => { + const payload = node.castTag(.int_cast).?.data; + return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs }); + }, + .rem => { + const payload = node.castTag(.rem).?.data; + return renderBuiltinCall(c, "@rem", &.{ payload.lhs, payload.rhs }); + }, + .div_trunc => { + const payload = node.castTag(.div_trunc).?.data; + return renderBuiltinCall(c, "@divTrunc", &.{ payload.lhs, payload.rhs }); + }, + .bool_to_int => { + const payload = node.castTag(.bool_to_int).?.data; + return renderBuiltinCall(c, "@boolToInt", &.{payload}); + }, + .as => { + const payload = node.castTag(.as).?.data; + return renderBuiltinCall(c, "@as", &.{ payload.lhs, payload.rhs }); + }, + .truncate => { + const payload = node.castTag(.truncate).?.data; + return renderBuiltinCall(c, "@truncate", &.{ payload.lhs, payload.rhs }); + }, + .bit_cast => { + const payload = node.castTag(.bit_cast).?.data; + return renderBuiltinCall(c, "@bitCast", &.{ payload.lhs, payload.rhs }); + }, + .float_cast => { + const payload = node.castTag(.float_cast).?.data; + return renderBuiltinCall(c, "@floatCast", &.{ payload.lhs, payload.rhs }); + }, + .float_to_int => { + const payload = node.castTag(.float_to_int).?.data; + return renderBuiltinCall(c, "@floatToInt", &.{ payload.lhs, payload.rhs }); + }, + .int_to_float => { + const payload = node.castTag(.int_to_float).?.data; + return renderBuiltinCall(c, "@intToFloat", &.{ payload.lhs, payload.rhs }); + }, + .int_to_enum => { + const payload = node.castTag(.int_to_enum).?.data; + return renderBuiltinCall(c, "@intToEnum", &.{ payload.lhs, payload.rhs }); + }, + .enum_to_int => { + const payload = node.castTag(.enum_to_int).?.data; + return renderBuiltinCall(c, "@enumToInt", &.{payload}); + }, + .int_to_ptr => { + const payload = node.castTag(.int_to_ptr).?.data; + return renderBuiltinCall(c, "@intToPtr", &.{ payload.lhs, payload.rhs }); + }, + .ptr_to_int => { + const payload = node.castTag(.ptr_to_int).?.data; + return renderBuiltinCall(c, "@ptrToInt", &.{payload}); + }, + .align_cast => { + const payload = node.castTag(.align_cast).?.data; + return renderBuiltinCall(c, "@alignCast", &.{ payload.lhs, payload.rhs }); + }, + .ptr_cast => { + const payload = node.castTag(.ptr_cast).?.data; + return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs }); + }, + else => return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), } } @@ -1050,3 +1157,114 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex { _ = try c.addToken(.r_paren, ")"); return res; } + +fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !NodeIndex { + const builtin_tok = try c.addToken(.builtin, builtin); + _ = try c.addToken(.l_paren, "("); + var arg_1: NodeIndex = 0; + var arg_2: NodeIndex = 0; + switch (args.len) { + 0 => {}, + 1 => { + arg_1 = try renderNode(c, args[0]); + }, + 2 => { + arg_1 = try renderNode(c, args[0]); + _ = try c.addToken(.comma, ","); + arg_2 = try renderNode(c, args[1]); + }, + else => unreachable, // expand this function as needed. + } + + _ = try c.addToken(.r_paren, ")"); + return c.addNode(.{ + .tag = .builtin_call_two, + .main_token = builtin_tok, + .data = .{ + .lhs = arg_1, + .rhs = arg_2, + }, + }); +} + +fn renderVar(c: *Context, node: Node) !NodeIndex { + const payload = node.castTag(.var_decl).?.data; + if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub"); + if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern"); + if (payload.is_export) _ = try c.addToken(.keyword_export, "export"); + const mut_tok = if (payload.is_const) + try c.addToken(.keyword_const, "const") + else + try c.addToken(.keyword_var, "var"); + _ = try c.addIdentifier(payload.name); + _ = try c.addToken(.colon, ":"); + const type_node = try renderNode(c, payload.type); + + const align_node = if (payload.alignment) |some| blk: { + _ = try c.addToken(.keyword_align, "align"); + _ = try c.addToken(.l_paren, "("); + const res = try c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + + const section_node = if (payload.linksection_string) |some| blk: { + _ = try c.addToken(.keyword_linksection, "linksection"); + _ = 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)}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + + const init_node = if (payload.init) |some| blk: { + _ = try c.addToken(.equal, "="); + break :blk try renderNode(c, some); + } else 0; + _ = try c.addToken(.semicolon, ";"); + + if (section_node == 0) { + if (align_node == 0) { + return c.addNode(.{ + .tag = .simple_var_decl, + .main_token = mut_tok, + .data = .{ + .lhs = type_node, + .rhs = init_node, + }, + }); + } else { + return c.addNode(.{ + .tag = .local_var_decl, + .main_token = mut_tok, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.LocalVarDecl{ + .type_node = type_node, + .align_node = align_node, + }), + .rhs = init_node, + }, + }); + } + } else { + return c.addNode(.{ + .tag = .global_var_decl, + .main_token = mut_tok, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.GlobalVarDecl{ + .type_node = type_node, + .align_node = align_node, + .section_node = section_node, + }), + .rhs = init_node, + }, + }); + } +} From 685778c5a73bf08487ebfb4b1632aee089a89e79 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 13 Feb 2021 21:24:40 +0200 Subject: [PATCH 20/36] translate-c: render unary ops --- src/translate_c.zig | 4 +- src/translate_c/ast.zig | 262 +++++++++++++++++++++++++++++++++++----- 2 files changed, 234 insertions(+), 32 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 821f3c5b74..03695186ec 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -3049,7 +3049,7 @@ fn maybeSuppressResult( result: Node, ) TransError!Node { if (used == .used) return result; - return Tag.ignore.create(c.arena, result); + return Tag.discard.create(c.arena, result); } fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { @@ -4127,7 +4127,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { var last = node; while (true) { // suppress result - const ignore = try Tag.ignore.create(c.arena, last); + const ignore = try Tag.discard.create(c.arena, last); try block_scope.statements.append(ignore); last = try parseCCondExpr(c, m, scope); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 282e645f2e..3bc1dc986f 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -60,6 +60,7 @@ pub const Node = extern union { tuple, container_init, std_meta_cast, + /// _ = operand; discard, // a + b @@ -67,43 +68,30 @@ pub const Node = extern union { // a = b add_assign, // c = (a = b) - add_assign_value, add_wrap, add_wrap_assign, - add_wrap_assign_value, sub, sub_assign, - sub_assign_value, sub_wrap, sub_wrap_assign, - sub_wrap_assign_value, mul, mul_assign, - mul_assign_value, mul_wrap, mul_wrap_assign, - mul_wrap_assign_value, div, div_assign, - div_assign_value, shl, shl_assign, - shl_assign_value, shr, shr_assign, - shr_assign_value, mod, mod_assign, - mod_assign_value, @"and", and_assign, - and_assign_value, @"or", or_assign, - or_assign_value, xor, xor_assign, - xor_assign_value, less_than, less_than_equal, greater_than, @@ -207,9 +195,6 @@ pub const Node = extern union { /// [1]type{val} ** count array_filler, - /// _ = operand; - ignore, - pub const last_no_payload_tag = Tag.usingnamespace_builtins; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -249,7 +234,6 @@ pub const Node = extern union { .while_true, .if_not_break, .switch_else, - .ignore, .block_single, .std_meta_sizeof, .bool_to_int, @@ -260,43 +244,30 @@ pub const Node = extern union { .add, .add_assign, - .add_assign_value, .add_wrap, .add_wrap_assign, - .add_wrap_assign_value, .sub, .sub_assign, - .sub_assign_value, .sub_wrap, .sub_wrap_assign, - .sub_wrap_assign_value, .mul, .mul_assign, - .mul_assign_value, .mul_wrap, .mul_wrap_assign, - .mul_wrap_assign_value, .div, .div_assign, - .div_assign_value, .shl, .shl_assign, - .shl_assign_value, .shr, .shr_assign, - .shr_assign_value, .mod, .mod_assign, - .mod_assign_value, .@"and", .and_assign, - .and_assign_value, .@"or", .or_assign, - .or_assign_value, .xor, .xor_assign, - .xor_assign_value, .less_than, .less_than_equal, .greater_than, @@ -869,6 +840,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), + .noreturn_type => return try c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.identifier, "noreturn"), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }), .type => { const payload = node.castTag(.type).?.data; return c.addNode(.{ @@ -880,6 +859,17 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .log2_int_type => { + const payload = node.castTag(.log2_int_type).?.data; + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addTokenFmt(.identifier, "u{d}", .{payload}), + .data = .{ + .lhs = undefined, + .rhs = undefined, + }, + }); + }, .identifier => { const payload = node.castTag(.identifier).?.data; return c.addNode(.{ @@ -1058,6 +1048,51 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const payload = node.castTag(.ptr_cast).?.data; return renderBuiltinCall(c, "@ptrCast", &.{ payload.lhs, payload.rhs }); }, + .sizeof => { + const payload = node.castTag(.sizeof).?.data; + return renderBuiltinCall(c, "@sizeOf", &.{payload}); + }, + .alignof => { + const payload = node.castTag(.alignof).?.data; + return renderBuiltinCall(c, "@alignOf", &.{payload}); + }, + .typeof => { + const payload = node.castTag(.typeof).?.data; + return renderBuiltinCall(c, "@TypeOf", &.{payload}); + }, + .negate => return renderPrefixOp(c, node, .negation, .minus, "-"), + .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"), + .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"), + .not => return renderPrefixOp(c, node, .bool_not, .bang, "!"), + .optional_type => return renderPrefixOp(c, node, .optional_type, .question_mark, "?"), + .address_of => return renderPrefixOp(c, node, .address_of, .ampersand, "&"), + .deref => { + const payload = node.castTag(.deref).?.data; + const operand = try renderNodeGrouped(c, payload); + const deref_tok = try c.addToken(.period_asterisk, ".*"); + return c.addNode(.{ + .tag = .deref, + .main_token = deref_tok, + .data = .{ + .lhs = operand, + .rhs = undefined, + }, + }); + }, + .unwrap => { + const payload = node.castTag(.unwrap).?.data; + const operand = try renderNodeGrouped(c, payload); + const period = try c.addToken(.period, "."); + const question_mark = try c.addToken(.question_mark, "?"); + return c.addNode(.{ + .tag = .unwrap_optional, + .main_token = period, + .data = .{ + .lhs = operand, + .rhs = question_mark, + }, + }); + }, else => return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), @@ -1069,6 +1104,173 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { } } +fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { + switch (node.tag()) { + .null_literal, + .undefined_literal, + .true_literal, + .false_literal, + .return_void, + .zero_literal, + .one_literal, + .void_type, + .noreturn_type, + .@"anytype", + .div_trunc, + .rem, + .int_cast, + .as, + .truncate, + .bit_cast, + .float_cast, + .float_to_int, + .int_to_float, + .int_to_enum, + .int_to_ptr, + .std_mem_zeroes, + .std_math_Log2Int, + .log2_int_type, + .ptr_to_int, + .enum_to_int, + .sizeof, + .alignof, + .typeof, + .std_meta_sizeof, + .std_meta_cast, + .std_mem_zeroinit, + .integer_literal, + .float_literal, + .string_literal, + .char_literal, + .identifier, + .field_access, + .ptr_cast, + .type, + .array_access, + .align_cast, + => { + // no grouping needed + return renderNode(c, node); + }, + + .negate, + .negate_wrap, + .bit_not, + .opaque_literal, + .not, + .optional_type, + .address_of, + .unwrap, + .deref, + .empty_array, + .block_single, + .bool_to_int, + .add, + .add_wrap, + .sub, + .sub_wrap, + .mul, + .mul_wrap, + .div, + .shl, + .shr, + .mod, + .@"and", + .@"or", + .xor, + .less_than, + .less_than_equal, + .greater_than, + .greater_than_equal, + .equal, + .not_equal, + .bit_and, + .bit_or, + .bit_xor, + .empty_block, + .array_cat, + .array_filler, + .@"if", + .call, + .@"enum", + .@"struct", + .@"union", + .array_init, + .tuple, + .container_init, + .block, + .c_pointer, + .single_pointer, + .array_type, + => return c.addNode(.{ + .tag = .grouped_expression, + .main_token = try c.addToken(.l_paren, "("), + .data = .{ + .lhs = try renderNode(c, node), + .rhs = try c.addToken(.r_paren, ")"), + }, + }), + .ellipsis3, + .switch_prong, + .warning, + .var_decl, + .func, + .fail_decl, + .arg_redecl, + .alias, + .var_simple, + .pub_var_simple, + .enum_redecl, + .@"while", + .@"switch", + .@"break", + .break_val, + .pub_inline_fn, + .discard, + .@"continue", + .@"return", + .usingnamespace_builtins, + .while_true, + .if_not_break, + .switch_else, + .add_assign, + .add_wrap_assign, + .sub_assign, + .sub_wrap_assign, + .mul_assign, + .mul_wrap_assign, + .div_assign, + .shl_assign, + .shr_assign, + .mod_assign, + .and_assign, + .or_assign, + .xor_assign, + .bit_and_assign, + .bit_or_assign, + .bit_xor_assign, + .assign, + => { + // these should never appear in places where grouping might be needed. + unreachable; + }, + } +} + +fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { + const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data; + const tok = try c.addToken(tok_tag, bytes); + const operand = try renderNodeGrouped(c, payload); + return c.addNode(.{ + .tag = tag, + .main_token = tok, + .data = .{ + .lhs = operand, + .rhs = undefined, + }, + }); +} + fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { const import_tok = try c.addToken(.builtin, "@import"); _ = try c.addToken(.l_paren, "("); From 227f167958ab37fe1df1b1dea5e3da42651b49c9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 13 Feb 2021 21:45:25 +0200 Subject: [PATCH 21/36] translate-c: render binops and break/continue --- src/translate_c/ast.zig | 113 +++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 3bc1dc986f..11a8fe4544 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -87,11 +87,7 @@ pub const Node = extern union { mod, mod_assign, @"and", - and_assign, @"or", - or_assign, - xor, - xor_assign, less_than, less_than_equal, greater_than, @@ -263,11 +259,7 @@ pub const Node = extern union { .mod, .mod_assign, .@"and", - .and_assign, .@"or", - .or_assign, - .xor, - .xor_assign, .less_than, .less_than_equal, .greater_than, @@ -832,7 +824,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), - .@"anytype" => return try c.addNode(.{ + .@"anytype" => return c.addNode(.{ .tag = .@"anytype", .main_token = try c.addToken(.keyword_anytype, "anytype"), .data = .{ @@ -840,7 +832,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), - .noreturn_type => return try c.addNode(.{ + .noreturn_type => return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.identifier, "noreturn"), .data = .{ @@ -848,6 +840,53 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), + .@"continue" => { + const tok = try c.addToken(.keyword_continue, "continue"); + _ = try c.addToken(.semicolon, ";"); + return c.addNode(.{ + .tag = .@"continue", + .main_token = tok, + .data = .{ + .lhs = 0, + .rhs = undefined, + }, + }); + }, + .@"break" => { + const payload = node.castTag(.@"break").?.data; + const tok = try c.addToken(.keyword_break, "break"); + const break_label = if (payload) |some| blk: { + _ = try c.addToken(.colon, ":"); + break :blk try c.addIdentifier(some); + } else 0; + _ = try c.addToken(.semicolon, ";"); + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.keyword_break, "break"), + .data = .{ + .lhs = break_label, + .rhs = 0, + }, + }); + }, + .break_val => { + const payload = node.castTag(.break_val).?.data; + const tok = try c.addToken(.keyword_break, "break"); + const break_label = if (payload.label) |some| blk: { + _ = try c.addToken(.colon, ":"); + break :blk try c.addIdentifier(some); + } else 0; + const val = try renderNode(c, payload.val); + _ = try c.addToken(.semicolon, ";"); + return c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.keyword_break, "break"), + .data = .{ + .lhs = break_label, + .rhs = val, + }, + }); + }, .type => { const payload = node.castTag(.type).?.data; return c.addNode(.{ @@ -1093,6 +1132,43 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .add => return renderBinOp(c, node, .add, .plus, "+"), + .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="), + .add_wrap => return renderBinOp(c, node, .add_wrap, .plus_percent, "+%"), + .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="), + .sub => return renderBinOp(c, node, .sub, .minus, "-"), + .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="), + .sub_wrap => return renderBinOp(c, node, .sub_wrap, .minus_percent, "-%"), + .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="), + .mul => return renderBinOp(c, node, .mul, .asterisk, "*"), + .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="), + .mul_wrap => return renderBinOp(c, node, .mul_wrap, .asterisk_percent, "*="), + .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="), + .div => return renderBinOp(c, node, .div, .slash, "/"), + .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="), + .shl => return renderBinOp(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"), + .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="), + .shr => return renderBinOp(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"), + .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="), + .mod => return renderBinOp(c, node, .mod, .percent, "%"), + .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="), + .@"and" => return renderBinOp(c, node, .bool_and, .keyword_and, "and"), + .@"or" => return renderBinOp(c, node, .bool_or, .keyword_or, "or"), + .less_than => return renderBinOp(c, node, .less_than, .angle_bracket_left, "<"), + .less_than_equal => return renderBinOp(c, node, .less_or_equal, .angle_bracket_left_equal, "<="), + .greater_than => return renderBinOp(c, node, .greater_than, .angle_bracket_right, ">="), + .greater_than_equal => return renderBinOp(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="), + .equal => return renderBinOp(c, node, .equal_equal, .equal_equal, "=="), + .not_equal => return renderBinOp(c, node, .bang_equal, .bang_equal, "!="), + .bit_and => return renderBinOp(c, node, .bit_and, .ampersand, "&"), + .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="), + .bit_or => return renderBinOp(c, node, .bit_or, .pipe, "|"), + .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="), + .bit_xor => return renderBinOp(c, node, .bit_xor, .caret, "^"), + .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="), + .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"), + .ellipsis3 => return renderBinOp(c, node, .switch_range, .ellipsis3, "..."), + .assign => return renderBinOp(c, node, .assign, .equal, "="), else => return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), @@ -1177,7 +1253,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .mod, .@"and", .@"or", - .xor, .less_than, .less_than_equal, .greater_than, @@ -1243,9 +1318,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .shl_assign, .shr_assign, .mod_assign, - .and_assign, - .or_assign, - .xor_assign, .bit_and_assign, .bit_or_assign, .bit_xor_assign, @@ -1271,6 +1343,19 @@ fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: T }); } +fn renderBinOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { + const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data; + const lhs = try renderNodeGrouped(c, payload.lhs); + return c.addNode(.{ + .tag = tag, + .main_token = try c.addToken(tok_tag, bytes), + .data = .{ + .lhs = lhs, + .rhs = try renderNodeGrouped(c, payload.rhs), + }, + }); +} + fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { const import_tok = try c.addToken(.builtin, "@import"); _ = try c.addToken(.l_paren, "("); From f191251ddb424eba7384d31fa2d50e50586f3993 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 13 Feb 2021 22:53:24 +0200 Subject: [PATCH 22/36] translate-c: render functions --- src/translate_c.zig | 2 +- src/translate_c/ast.zig | 261 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 246 insertions(+), 17 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 03695186ec..abb4fff7f4 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -107,7 +107,7 @@ const Scope = struct { // do while, we want to put `if (cond) break;` at the end. const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .loop); var stmts = try c.arena.alloc(Node, alloc_len); - stmts.len -= 1; + stmts.len = self.statements.items.len; mem.copy(Node, stmts, self.statements.items); return Tag.block.create(c.arena, .{ .label = self.label, diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 11a8fe4544..6d08688de4 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1,3 +1,8 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. const std = @import("std"); const Type = @import("../type.zig").Type; const Allocator = std.mem.Allocator; @@ -840,18 +845,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), - .@"continue" => { - const tok = try c.addToken(.keyword_continue, "continue"); - _ = try c.addToken(.semicolon, ";"); - return c.addNode(.{ - .tag = .@"continue", - .main_token = tok, - .data = .{ - .lhs = 0, - .rhs = undefined, - }, - }); - }, + .@"continue" => return c.addNode(.{ + .tag = .@"continue", + .main_token = try c.addToken(.keyword_continue, "continue"), + .data = .{ + .lhs = 0, + .rhs = undefined, + }, + }), .@"break" => { const payload = node.castTag(.@"break").?.data; const tok = try c.addToken(.keyword_break, "break"); @@ -859,7 +860,6 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { _ = try c.addToken(.colon, ":"); break :blk try c.addIdentifier(some); } else 0; - _ = try c.addToken(.semicolon, ";"); return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.keyword_break, "break"), @@ -876,14 +876,12 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { _ = try c.addToken(.colon, ":"); break :blk try c.addIdentifier(some); } else 0; - const val = try renderNode(c, payload.val); - _ = try c.addToken(.semicolon, ";"); return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.keyword_break, "break"), .data = .{ .lhs = break_label, - .rhs = val, + .rhs = try renderNode(c, payload.val), }, }); }, @@ -1169,6 +1167,84 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"), .ellipsis3 => return renderBinOp(c, node, .switch_range, .ellipsis3, "..."), .assign => return renderBinOp(c, node, .assign, .equal, "="), + .empty_block => { + const l_brace = try c.addToken(.l_brace, "{"); + _ = try c.addToken(.r_brace, "}"); + return c.addNode(.{ + .tag = .block_two, + .main_token = l_brace, + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }); + }, + .block_single => { + const payload = node.castTag(.block_single).?.data; + const l_brace = try c.addToken(.l_brace, "{"); + + const stmt = try renderNode(c, payload); + _ = try c.addToken(.semicolon, ";"); + + _ = try c.addToken(.r_brace, "}"); + return c.addNode(.{ + .tag = .block_two, + .main_token = l_brace, + .data = .{ + .lhs = stmt, + .rhs = 0, + }, + }); + }, + .block => { + const payload = node.castTag(.block).?.data; + if (payload.label) |some| { + _ = try c.addIdentifier(some); + _ = try c.addToken(.colon, ":"); + } + const l_brace = try c.addToken(.l_brace, "{"); + + var stmts = std.ArrayList(NodeIndex).init(c.gpa); + defer stmts.deinit(); + for (payload.stmts) |stmt| { + const res = try renderNode(c, stmt); + switch (stmt.tag()) { + .warning => continue, + .var_decl, .var_simple => {}, + else => _ = try c.addToken(.semicolon, ";"), + } + try stmts.append(res); + } + const span = try c.listToSpan(stmts.items); + _ = try c.addToken(.r_brace, "}"); + + const semicolon = c.tokens.items(.tag)[c.tokens.len - 2] == .semicolon; + return c.addNode(.{ + .tag = if (semicolon) .block_semicolon else .block, + .main_token = l_brace, + .data = .{ + .lhs = span.start, + .rhs = span.end, + }, + }); + }, + .func => return renderFunc(c, node), + .discard => { + const payload = node.castTag(.discard).?.data; + const lhs = try c.addNode(.{ + .tag = .identifier, + .main_token = try c.addToken(.identifier, "_"), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + return c.addNode(.{ + .tag = .assign, + .main_token = try c.addToken(.equal, "="), + .data = .{ + .lhs = lhs, + .rhs = try renderNode(c, payload), + }, + }); + }, else => return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), @@ -1555,3 +1631,156 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { }); } } + +fn renderFunc(c: *Context, node: Node) !NodeIndex { + const payload = node.castTag(.func).?.data; + if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub"); + if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern"); + if (payload.is_export) _ = try c.addToken(.keyword_export, "export"); + const fn_token = try c.addToken(.keyword_fn, "fn"); + if (payload.name) |some| _ = try c.addIdentifier(some); + + _ = try c.addToken(.l_paren, "("); + const first = if (payload.params.len != 0) blk: { + const param = payload.params[0]; + if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); + if (param.name) |some| { + _ = try c.addIdentifier(some); + _ = try c.addToken(.colon, ":"); + } + break :blk try renderNode(c, param.type); + } else 0; + + var span: NodeSubRange = undefined; + if (payload.params.len > 1) { + var params = std.ArrayList(NodeIndex).init(c.gpa); + defer params.deinit(); + + try params.append(first); + for (payload.params[1..]) |param| { + _ = try c.addToken(.comma, ","); + if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); + if (param.name) |some| { + _ = try c.addIdentifier(some); + _ = try c.addToken(.colon, ":"); + } + try params.append(try renderNode(c, param.type)); + } + span = try c.listToSpan(params.items); + } + if (payload.is_var_args) { + if (payload.params.len != 0) _ = try c.addToken(.comma, ","); + _ = try c.addToken(.ellipsis3, "..."); + } + _ = try c.addToken(.r_paren, ")"); + + const return_type_expr = try renderNode(c, payload.return_type); + + const align_expr = if (payload.alignment) |some| blk: { + _ = try c.addToken(.keyword_align, "align"); + _ = try c.addToken(.l_paren, "("); + const res = try c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + + const section_expr = if (payload.linksection_string) |some| blk: { + _ = try c.addToken(.keyword_linksection, "linksection"); + _ = 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)}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + + const callconv_expr = if (payload.explicit_callconv) |some| blk: { + _ = try c.addToken(.keyword_linksection, "callconv"); + _ = try c.addToken(.l_paren, "("); + _ = try c.addToken(.period, "."); + const res = try c.addNode(.{ + .tag = .enum_literal, + .main_token = try c.addTokenFmt(.string_literal, "{}", .{some}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + + const fn_proto = try blk: { + if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { + if (payload.params.len < 2) + break :blk c.addNode(.{ + .tag = .fn_proto_simple, + .main_token = fn_token, + .data = .{ + .lhs = first, + .rhs = return_type_expr, + }, + }) + else + break :blk c.addNode(.{ + .tag = .fn_proto_multi, + .main_token = fn_token, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.SubRange{ + .start = span.start, + .end = span.end, + }), + .rhs = return_type_expr, + }, + }); + } + if (payload.params.len < 2) + break :blk c.addNode(.{ + .tag = .fn_proto_one, + .main_token = fn_token, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{ + .param = first, + .align_expr = align_expr, + .section_expr = section_expr, + .callconv_expr = callconv_expr, + }), + .rhs = return_type_expr, + }, + }) + else + break :blk c.addNode(.{ + .tag = .fn_proto, + .main_token = fn_token, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.FnProto{ + .params_start = span.start, + .params_end = span.end, + .align_expr = align_expr, + .section_expr = section_expr, + .callconv_expr = callconv_expr, + }), + .rhs = return_type_expr, + }, + }); + }; + + const body = if (payload.body) |some| + try renderNode(c, some) + else blk: { + _ = try c.addToken(.semicolon, ";"); + break :blk 0; + }; + + return c.addNode(.{ + .tag = .fn_decl, + .main_token = fn_token, + .data = .{ + .lhs = fn_proto, + .rhs = body, + }, + }); +} From c4dfabf4dcfc29f48356e3c5f762f1ca2c21f088 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 14 Feb 2021 15:35:51 +0200 Subject: [PATCH 23/36] translate-c: render macro functions, use cast type as return type Closes #8004 --- src/translate_c.zig | 8 +- src/translate_c/ast.zig | 280 ++++++++++++++++++++++++++-------------- 2 files changed, 191 insertions(+), 97 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index abb4fff7f4..6fa3110118 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4099,14 +4099,18 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { const br = blk_last.castTag(.break_val).?; break :blk br.data.val; } else expr; - const typeof = try Tag.typeof.create(c.arena, typeof_arg); + const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some| + some.data.lhs + else + try Tag.typeof.create(c.arena, typeof_arg); + const return_expr = try Tag.@"return".create(c.arena, expr); try block_scope.statements.append(return_expr); const fn_decl = try Tag.pub_inline_fn.create(c.arena, .{ .name = m.name, .params = try c.arena.dupe(ast.Payload.Param, fn_params.items), - .return_type = typeof, + .return_type = return_type, .body = try block_scope.complete(c), }); _ = try c.global_scope.macro_table.put(m.name, fn_decl); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 6d08688de4..2c3b9f29fe 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -615,9 +615,15 @@ pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.ast.Tree { defer ctx.extra_data.deinit(gpa); defer ctx.tokens.deinit(gpa); - // Estimate that each top level node has 25 child nodes. - const estimated_node_count = nodes.len * 25; + // Estimate that each top level node has 10 child nodes. + const estimated_node_count = nodes.len * 10; try ctx.nodes.ensureCapacity(gpa, estimated_node_count); + // Estimate that each each node has 2 tokens. + const estimated_tokens_count = estimated_node_count * 2; + try ctx.tokens.ensureCapacity(gpa, estimated_tokens_count); + // Estimate that each each token is 3 bytes long. + const estimated_buf_len = estimated_tokens_count * 3; + try ctx.buf.ensureCapacity(estimated_buf_len); ctx.nodes.appendAssumeCapacity(.{ .tag = .root, @@ -776,74 +782,47 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .null_literal => return c.addNode(.{ .tag = .null_literal, .main_token = try c.addToken(.keyword_null, "null"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .undefined_literal => return c.addNode(.{ .tag = .undefined_literal, .main_token = try c.addToken(.keyword_undefined, "undefined"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .true_literal => return c.addNode(.{ .tag = .true_literal, .main_token = try c.addToken(.keyword_true, "true"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .false_literal => return c.addNode(.{ .tag = .false_literal, .main_token = try c.addToken(.keyword_false, "false"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .zero_literal => return c.addNode(.{ .tag = .integer_literal, .main_token = try c.addToken(.integer_literal, "0"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .one_literal => return c.addNode(.{ .tag = .integer_literal, .main_token = try c.addToken(.integer_literal, "1"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .void_type => return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.identifier, "void"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .@"anytype" => return c.addNode(.{ .tag = .@"anytype", .main_token = try c.addToken(.keyword_anytype, "anytype"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .noreturn_type => return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.identifier, "noreturn"), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }), .@"continue" => return c.addNode(.{ .tag = .@"continue", @@ -853,6 +832,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), + .return_void => return c.addNode(.{ + .tag = .@"return", + .main_token = try c.addToken(.keyword_return, "return"), + .data = .{ + .lhs = 0, + .rhs = undefined, + }, + }), .@"break" => { const payload = node.castTag(.@"break").?.data; const tok = try c.addToken(.keyword_break, "break"); @@ -885,15 +872,23 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .@"return" => { + const payload = node.castTag(.@"return").?.data; + return c.addNode(.{ + .tag = .@"return", + .main_token = try c.addToken(.keyword_return, "return"), + .data = .{ + .lhs = try renderNode(c, payload), + .rhs = undefined, + }, + }); + }, .type => { const payload = node.castTag(.type).?.data; return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.identifier, payload), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .log2_int_type => { @@ -901,10 +896,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "u{d}", .{payload}), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .identifier => { @@ -912,10 +904,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .identifier, .main_token = try c.addIdentifier(payload), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .float_literal => { @@ -923,10 +912,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .float_literal, .main_token = try c.addToken(.float_literal, payload), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .integer_literal => { @@ -934,10 +920,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .integer_literal, .main_token = try c.addToken(.integer_literal, payload), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .string_literal => { @@ -945,10 +928,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.string_literal, payload), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .char_literal => { @@ -956,10 +936,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.string_literal, payload), - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); }, .fail_decl => { @@ -976,10 +953,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const err_msg = try c.addNode(.{ .tag = .string_literal, .main_token = err_msg_tok, - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); const compile_error = try c.addNode(.{ @@ -1130,6 +1104,31 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .c_pointer, .single_pointer => { + const payload = @fieldParentPtr(Payload.Pointer, "base", node.ptr_otherwise).data; + + const asterisk = if (node.tag() == .single_pointer) + try c.addToken(.asterisk, "*") + else blk: { + _ = try c.addToken(.l_bracket, "["); + const res = try c.addToken(.asterisk, "*"); + _ = try c.addIdentifier("c"); + _ = try c.addToken(.r_bracket, "]"); + break :blk res; + }; + if (payload.is_const) _ = try c.addToken(.keyword_const, "const"); + if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile"); + const elem_type = try renderNode(c, payload.elem_type); + + return c.addNode(.{ + .tag = .ptr_type_aligned, + .main_token = asterisk, + .data = .{ + .lhs = 0, + .rhs = elem_type, + }, + }); + }, .add => return renderBinOp(c, node, .add, .plus, "+"), .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="), .add_wrap => return renderBinOp(c, node, .add_wrap, .plus_percent, "+%"), @@ -1229,6 +1228,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .func => return renderFunc(c, node), + .pub_inline_fn => return renderMacroFunc(c, node), .discard => { const payload = node.castTag(.discard).?.data; const lhs = try c.addNode(.{ @@ -1300,6 +1300,9 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .type, .array_access, .align_cast, + .optional_type, + .c_pointer, + .single_pointer, => { // no grouping needed return renderNode(c, node); @@ -1310,7 +1313,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .bit_not, .opaque_literal, .not, - .optional_type, .address_of, .unwrap, .deref, @@ -1350,8 +1352,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .tuple, .container_init, .block, - .c_pointer, - .single_pointer, .array_type, => return c.addNode(.{ .tag = .grouped_expression, @@ -1439,10 +1439,7 @@ fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeInde const std_node = try c.addNode(.{ .tag = .string_literal, .main_token = std_tok, - .data = .{ - .lhs = undefined, - .rhs = undefined, - }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); @@ -1498,21 +1495,23 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex { }); }, else => blk: { - const start = @intCast(u32, c.extra_data.items.len); - const end = @intCast(u32, start + args.len); - try c.extra_data.ensureCapacity(c.gpa, end + 2); // + 2 for span start + end + var rendered = try c.gpa.alloc(NodeIndex, args.len); + defer c.gpa.free(rendered); + for (args) |arg, i| { if (i != 0) _ = try c.addToken(.comma, ","); - c.extra_data.appendAssumeCapacity(try renderNode(c, arg)); + rendered[i] = try renderNode(c, arg); } - c.extra_data.appendAssumeCapacity(start); - c.extra_data.appendAssumeCapacity(end); + const span = try c.listToSpan(rendered); break :blk try c.addNode(.{ - .tag = .call_comma, + .tag = .call, .main_token = lparen, .data = .{ .lhs = lhs, - .rhs = end + 2, + .rhs = try c.addExtra(std.zig.ast.Node.SubRange{ + .start = span.start, + .end = span.end, + }), }, }); }, @@ -1653,20 +1652,20 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { var span: NodeSubRange = undefined; if (payload.params.len > 1) { - var params = std.ArrayList(NodeIndex).init(c.gpa); - defer params.deinit(); + var params = try c.gpa.alloc(NodeIndex, payload.params.len); + defer c.gpa.free(params); - try params.append(first); - for (payload.params[1..]) |param| { + params[0] = first; + for (payload.params[1..]) |param, i| { _ = try c.addToken(.comma, ","); if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); if (param.name) |some| { _ = try c.addIdentifier(some); _ = try c.addToken(.colon, ":"); } - try params.append(try renderNode(c, param.type)); + params[i + 1] = try renderNode(c, param.type); } - span = try c.listToSpan(params.items); + span = try c.listToSpan(params); } if (payload.is_var_args) { if (payload.params.len != 0) _ = try c.addToken(.comma, ","); @@ -1674,8 +1673,6 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { } _ = try c.addToken(.r_paren, ")"); - const return_type_expr = try renderNode(c, payload.return_type); - const align_expr = if (payload.alignment) |some| blk: { _ = try c.addToken(.keyword_align, "align"); _ = try c.addToken(.l_paren, "("); @@ -1701,18 +1698,20 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { } else 0; const callconv_expr = if (payload.explicit_callconv) |some| blk: { - _ = try c.addToken(.keyword_linksection, "callconv"); + _ = try c.addToken(.keyword_callconv, "callconv"); _ = try c.addToken(.l_paren, "("); _ = try c.addToken(.period, "."); const res = try c.addNode(.{ .tag = .enum_literal, - .main_token = try c.addTokenFmt(.string_literal, "{}", .{some}), + .main_token = try c.addTokenFmt(.identifier, "{}", .{some}), .data = .{ .lhs = undefined, .rhs = undefined }, }); _ = try c.addToken(.r_paren, ")"); break :blk res; } else 0; + const return_type_expr = try renderNode(c, payload.return_type); + const fn_proto = try blk: { if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { if (payload.params.len < 2) @@ -1784,3 +1783,94 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { }, }); } + +fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { + const payload = node.castTag(.pub_inline_fn).?.data; + _ = try c.addToken(.keyword_pub, "pub"); + const fn_token = try c.addToken(.keyword_fn, "fn"); + _ = try c.addIdentifier(payload.name); + + _ = try c.addToken(.l_paren, "("); + const first = if (payload.params.len != 0) blk: { + const param = payload.params[0]; + if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); + if (param.name) |some| { + _ = try c.addIdentifier(some); + _ = try c.addToken(.colon, ":"); + } + break :blk try renderNode(c, param.type); + } else 0; + + var span: NodeSubRange = undefined; + if (payload.params.len > 1) { + var params = try c.gpa.alloc(NodeIndex, payload.params.len); + defer c.gpa.free(params); + + params[0] = first; + for (payload.params[1..]) |param, i| { + _ = try c.addToken(.comma, ","); + if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); + if (param.name) |some| { + _ = try c.addIdentifier(some); + _ = try c.addToken(.colon, ":"); + } + params[i + 1] = try renderNode(c, param.type); + } + span = try c.listToSpan(params); + } + _ = try c.addToken(.r_paren, ")"); + + const callconv_expr = blk: { + _ = try c.addToken(.keyword_callconv, "callconv"); + _ = try c.addToken(.l_paren, "("); + _ = try c.addToken(.period, "."); + const res = try c.addNode(.{ + .tag = .enum_literal, + .main_token = try c.addToken(.identifier, "Inline"), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + }; + const return_type_expr = try renderNode(c, payload.return_type); + + const fn_proto = try blk: { + if (payload.params.len < 2) + break :blk c.addNode(.{ + .tag = .fn_proto_one, + .main_token = fn_token, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{ + .param = first, + .align_expr = 0, + .section_expr = 0, + .callconv_expr = callconv_expr, + }), + .rhs = return_type_expr, + }, + }) + else + break :blk c.addNode(.{ + .tag = .fn_proto, + .main_token = fn_token, + .data = .{ + .lhs = try c.addExtra(std.zig.ast.Node.FnProto{ + .params_start = span.start, + .params_end = span.end, + .align_expr = 0, + .section_expr = 0, + .callconv_expr = callconv_expr, + }), + .rhs = return_type_expr, + }, + }); + }; + return c.addNode(.{ + .tag = .fn_decl, + .main_token = fn_token, + .data = .{ + .lhs = fn_proto, + .rhs = try renderNode(c, payload.body), + }, + }); +} From 62162a07171ccbdb753650205880c3745a599366 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 14 Feb 2021 16:32:31 +0200 Subject: [PATCH 24/36] translate-c: render control flow --- src/translate_c.zig | 41 ++++--- src/translate_c/ast.zig | 251 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 256 insertions(+), 36 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 6fa3110118..894502ecc5 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -36,6 +36,7 @@ const Scope = struct { root, condition, loop, + do_loop, }; /// Represents an in-progress Node.Switch. This struct is stack-allocated. @@ -103,15 +104,22 @@ const Scope = struct { } fn complete(self: *Block, c: *Context) !Node { - // We reserve 1 extra statement if the parent is a Loop. This is in case of - // do while, we want to put `if (cond) break;` at the end. - const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .loop); - var stmts = try c.arena.alloc(Node, alloc_len); - stmts.len = self.statements.items.len; - mem.copy(Node, stmts, self.statements.items); + if (self.base.parent.?.id == .do_loop) { + // We reserve 1 extra statement if the parent is a do_loop. This is in case of + // do while, we want to put `if (cond) break;` at the end. + const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .do_loop); + var stmts = try c.arena.alloc(Node, alloc_len); + stmts.len = self.statements.items.len; + mem.copy(Node, stmts, self.statements.items); + return Tag.block.create(c.arena, .{ + .label = self.label, + .stmts = stmts, + }); + } + if (self.statements.items.len == 0) return Tag.empty_block.init(); return Tag.block.create(c.arena, .{ .label = self.label, - .stmts = stmts, + .stmts = try c.arena.dupe(Node, self.statements.items), }); } @@ -222,7 +230,7 @@ const Scope = struct { return switch (scope.id) { .root => return name, .block => @fieldParentPtr(Block, "base", scope).getAlias(name), - .@"switch", .loop, .condition => scope.parent.?.getAlias(name), + .@"switch", .loop, .do_loop, .condition => scope.parent.?.getAlias(name), }; } @@ -230,7 +238,7 @@ const Scope = struct { return switch (scope.id) { .root => @fieldParentPtr(Root, "base", scope).contains(name), .block => @fieldParentPtr(Block, "base", scope).contains(name), - .@"switch", .loop, .condition => scope.parent.?.contains(name), + .@"switch", .loop, .do_loop, .condition => scope.parent.?.contains(name), }; } @@ -240,7 +248,7 @@ const Scope = struct { switch (scope.id) { .root => unreachable, .@"switch" => return scope, - .loop => return scope, + .loop, .do_loop => return scope, else => scope = scope.parent.?, } } @@ -2063,7 +2071,7 @@ fn transDoWhileLoop( ) TransError!Node { var loop_scope = Scope{ .parent = scope, - .id = .loop, + .id = .do_loop, }; // if (!cond) break; @@ -2075,7 +2083,14 @@ fn transDoWhileLoop( }; defer cond_scope.deinit(); const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); - const if_not_break = try Tag.if_not_break.create(c.arena, cond); + const if_not_break = switch (cond.tag()) { + .false_literal => try Tag.@"break".create(c.arena, null), + .true_literal => { + const body_node = try transStmt(c, scope, stmt.getBody(), .unused); + return Tag.while_true.create(c.arena, body_node); + }, + else => try Tag.if_not_break.create(c.arena, cond), + }; const body_node = if (stmt.getBody().getStmtClass() == .CompoundStmtClass) blk: { // there's already a block in C, so we'll append our condition to it. @@ -4099,7 +4114,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { const br = blk_last.castTag(.break_val).?; break :blk br.data.val; } else expr; - const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some| + const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some| some.data.lhs else try Tag.typeof.create(c.arena, typeof_arg); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 2c3b9f29fe..d108a2852e 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -848,8 +848,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { break :blk try c.addIdentifier(some); } else 0; return c.addNode(.{ - .tag = .identifier, - .main_token = try c.addToken(.keyword_break, "break"), + .tag = .@"break", + .main_token = tok, .data = .{ .lhs = break_label, .rhs = 0, @@ -864,8 +864,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { break :blk try c.addIdentifier(some); } else 0; return c.addNode(.{ - .tag = .identifier, - .main_token = try c.addToken(.keyword_break, "break"), + .tag = .@"break", + .main_token = tok, .data = .{ .lhs = break_label, .rhs = try renderNode(c, payload.val), @@ -1183,7 +1183,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const l_brace = try c.addToken(.l_brace, "{"); const stmt = try renderNode(c, payload); - _ = try c.addToken(.semicolon, ";"); + try addSemicolonIfNeeded(c, payload); _ = try c.addToken(.r_brace, "}"); return c.addNode(.{ @@ -1207,11 +1207,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { defer stmts.deinit(); for (payload.stmts) |stmt| { const res = try renderNode(c, stmt); - switch (stmt.tag()) { - .warning => continue, - .var_decl, .var_simple => {}, - else => _ = try c.addToken(.semicolon, ";"), - } + if (res == 0) continue; + try addSemicolonIfNeeded(c, stmt); try stmts.append(res); } const span = try c.listToSpan(stmts.items); @@ -1245,6 +1242,194 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, + .@"while" => { + const payload = node.castTag(.@"while").?.data; + const while_tok = try c.addToken(.keyword_while, "while"); + _ = try c.addToken(.l_paren, "("); + const cond = try renderNode(c, payload.cond); + _ = try c.addToken(.r_paren, ")"); + + const cont_expr = if (payload.cont_expr) |some| blk: { + _ = try c.addToken(.colon, ":"); + _ = try c.addToken(.l_paren, "("); + const res = try renderNode(c, some); + _ = try c.addToken(.r_paren, ")"); + break :blk res; + } else 0; + const body = try renderNode(c, payload.body); + + if (cont_expr == 0) { + return c.addNode(.{ + .tag = .while_simple, + .main_token = while_tok, + .data = .{ + .lhs = cond, + .rhs = body, + }, + }); + } else { + return c.addNode(.{ + .tag = .while_cont, + .main_token = while_tok, + .data = .{ + .lhs = cond, + .rhs = try c.addExtra(std.zig.ast.Node.WhileCont{ + .cont_expr = cont_expr, + .then_expr = body, + }), + }, + }); + } + }, + .while_true => { + const payload = node.castTag(.while_true).?.data; + const while_tok = try c.addToken(.keyword_while, "while"); + _ = try c.addToken(.l_paren, "("); + const cond = try c.addNode(.{ + .tag = .true_literal, + .main_token = try c.addToken(.keyword_true, "true"), + .data = undefined, + }); + _ = try c.addToken(.r_paren, ")"); + const body = try renderNode(c, payload); + + return c.addNode(.{ + .tag = .while_simple, + .main_token = while_tok, + .data = .{ + .lhs = cond, + .rhs = body, + }, + }); + }, + .@"if" => { + const payload = node.castTag(.@"if").?.data; + const if_tok = try c.addToken(.keyword_if, "if"); + _ = try c.addToken(.l_paren, "("); + const cond = try renderNode(c, payload.cond); + _ = try c.addToken(.r_paren, ")"); + + const then_expr = try renderNode(c, payload.then); + const else_node = payload.@"else" orelse return c.addNode(.{ + .tag = .if_simple, + .main_token = if_tok, + .data = .{ + .lhs = cond, + .rhs = then_expr, + }, + }); + _ = try c.addToken(.keyword_else, "else"); + const else_expr = try renderNode(c, else_node); + + return c.addNode(.{ + .tag = .@"if", + .main_token = if_tok, + .data = .{ + .lhs = cond, + .rhs = try c.addExtra(std.zig.ast.Node.If{ + .then_expr = then_expr, + .else_expr = else_expr, + }), + }, + }); + }, + .if_not_break => { + const payload = node.castTag(.if_not_break).?.data; + const if_tok = try c.addToken(.keyword_if, "if"); + _ = try c.addToken(.l_paren, "("); + const cond = try c.addNode(.{ + .tag = .bool_not, + .main_token = try c.addToken(.bang, "!"), + .data = .{ + .lhs = try renderNodeGrouped(c, payload), + .rhs = undefined, + }, + }); + _ = try c.addToken(.r_paren, ")"); + const then_expr = try c.addNode(.{ + .tag = .@"break", + .main_token = try c.addToken(.keyword_break, "break"), + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }); + + return c.addNode(.{ + .tag = .if_simple, + .main_token = if_tok, + .data = .{ + .lhs = cond, + .rhs = then_expr, + }, + }); + }, + .@"switch" => { + const payload = node.castTag(.@"switch").?.data; + const switch_tok = try c.addToken(.keyword_switch, "switch"); + _ = try c.addToken(.l_paren, "("); + const cond = try renderNode(c, payload.cond); + _ = try c.addToken(.r_paren, ")"); + + _ = try c.addToken(.l_brace, "{"); + var cases = try c.gpa.alloc(NodeIndex, payload.cases.len); + defer c.gpa.free(cases); + for (payload.cases) |case, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + cases[i] = try renderNode(c, case); + } + const span = try c.listToSpan(cases); + _ = try c.addToken(.r_brace, "}"); + return c.addNode(.{ + .tag = .@"switch", + .main_token = switch_tok, + .data = .{ + .lhs = cond, + .rhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + }, + }); + }, + .switch_else => { + const payload = node.castTag(.switch_else).?.data; + _ = try c.addToken(.keyword_else, "else"); + return c.addNode(.{ + .tag = .switch_case_one, + .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), + .data = .{ + .lhs = 0, + .rhs = try renderNode(c, payload), + }, + }); + }, + .switch_prong => { + const payload = node.castTag(.switch_prong).?.data; + const item = try renderNode(c, payload.lhs); + return c.addNode(.{ + .tag = .switch_case_one, + .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), + .data = .{ + .lhs = item, + .rhs = try renderNode(c, payload.rhs), + }, + }); + }, + .opaque_literal => { + const opaque_tok = try c.addToken(.keyword_opaque, "opaque"); + _ = try c.addToken(.l_brace, "{"); + _ = try c.addToken(.r_brace, "}"); + + return c.addNode(.{ + .tag = .container_decl_two, + .main_token = opaque_tok, + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }); + }, else => return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), @@ -1256,6 +1441,28 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { } } +fn addSemicolonIfNeeded(c: *Context, node: Node) !void { + switch (node.tag()) { + .warning => unreachable, + .var_decl, .var_simple, .block, .empty_block, .@"switch" => {}, + .while_true => { + const payload = node.castTag(.while_true).?.data; + return addSemicolonIfNeeded(c, payload); + }, + .@"while" => { + const payload = node.castTag(.@"while").?.data; + return addSemicolonIfNeeded(c, payload.body); + }, + .@"if" => { + const payload = node.castTag(.@"if").?.data; + if (payload.@"else") |some| + return addSemicolonIfNeeded(c, some); + return addSemicolonIfNeeded(c, payload.then); + }, + else => _ = try c.addToken(.semicolon, ";"), + } +} + fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { switch (node.tag()) { .null_literal, @@ -1303,19 +1510,19 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .optional_type, .c_pointer, .single_pointer, + .unwrap, + .deref, + .address_of, + .not, + .negate, + .negate_wrap, + .bit_not, => { // no grouping needed return renderNode(c, node); }, - .negate, - .negate_wrap, - .bit_not, .opaque_literal, - .not, - .address_of, - .unwrap, - .deref, .empty_array, .block_single, .bool_to_int, @@ -1407,13 +1614,11 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { const payload = @fieldParentPtr(Payload.UnOp, "base", node.ptr_otherwise).data; - const tok = try c.addToken(tok_tag, bytes); - const operand = try renderNodeGrouped(c, payload); return c.addNode(.{ .tag = tag, - .main_token = tok, + .main_token = try c.addToken(tok_tag, bytes), .data = .{ - .lhs = operand, + .lhs = try renderNodeGrouped(c, payload), .rhs = undefined, }, }); @@ -1508,7 +1713,7 @@ fn renderCall(c: *Context, lhs: NodeIndex, args: []const Node) !NodeIndex { .main_token = lparen, .data = .{ .lhs = lhs, - .rhs = try c.addExtra(std.zig.ast.Node.SubRange{ + .rhs = try c.addExtra(NodeSubRange{ .start = span.start, .end = span.end, }), @@ -1728,7 +1933,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { .tag = .fn_proto_multi, .main_token = fn_token, .data = .{ - .lhs = try c.addExtra(std.zig.ast.Node.SubRange{ + .lhs = try c.addExtra(NodeSubRange{ .start = span.start, .end = span.end, }), From c0540967e974b59bf85f71be9f085b5852d20ef6 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 14 Feb 2021 18:06:15 +0200 Subject: [PATCH 25/36] translate-c: render array stuff --- src/Compilation.zig | 2 +- src/translate_c.zig | 64 ++++++------ src/translate_c/ast.zig | 220 +++++++++++++++++++++++++++++++++++----- 3 files changed, 227 insertions(+), 59 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 89c047065c..b3ee73f03f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1334,7 +1334,7 @@ pub fn update(self: *Compilation) !void { self.c_object_work_queue.writeItemAssumeCapacity(entry.key); } - const use_stage1 = build_options.is_stage1 and self.bin_file.options.use_llvm; + const use_stage1 = build_options.omit_stage2 or build_options.is_stage1 and self.bin_file.options.use_llvm; if (!use_stage1) { if (self.bin_file.options.module) |module| { module.compile_log_text.shrinkAndFree(module.gpa, 0); diff --git a/src/translate_c.zig b/src/translate_c.zig index 894502ecc5..ce8fea7a71 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1689,7 +1689,10 @@ fn transStringLiteralAsArray( init_list[i] = try transCreateNodeNumber(c, 0, .int); } - return Tag.array_init.create(c.arena, init_list); + return Tag.array_init.create(c.arena, .{ + .cond = arr_type, + .cases = init_list, + }); } fn cIsEnum(qt: clang.QualType) bool { @@ -1880,6 +1883,7 @@ fn transInitListExprArray( ) TransError!Node { const arr_type = ty.getAsArrayTypeUnsafe(); const child_qt = arr_type.getElementType(); + const child_type = try transQualType(c, child_qt, loc); const init_count = expr.getNumInits(); assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); @@ -1888,18 +1892,20 @@ fn transInitListExprArray( const leftover_count = all_count - init_count; if (all_count == 0) { - return Tag.empty_array.create(c.arena, try transQualType(c, child_qt, loc)); + return Tag.empty_array.create(c.arena, child_type); } - const ty_node = try transType(c, ty, loc); const init_node = if (init_count != 0) blk: { const init_list = try c.arena.alloc(Node, init_count); for (init_list) |*init, i| { const elem_expr = expr.getInit(@intCast(c_uint, i)); - init.* = try transExpr(c, scope, elem_expr, .used); + init.* = try transExprCoercing(c, scope, elem_expr, .used); } - const init_node = try Tag.array_init.create(c.arena, init_list); + const init_node = try Tag.array_init.create(c.arena, .{ + .cond = try Tag.array_type.create(c.arena, .{ .len = init_count, .elem_type = child_type }), + .cases = init_list, + }); if (leftover_count == 0) { return init_node; } @@ -1908,8 +1914,8 @@ fn transInitListExprArray( const filler_val_expr = expr.getArrayFiller(); const filler_node = try Tag.array_filler.create(c.arena, .{ - .type = ty_node, - .filler = try transExpr(c, scope, filler_val_expr, .used), + .type = child_type, + .filler = try transExprCoercing(c, scope, filler_val_expr, .used), .count = leftover_count, }); @@ -2422,9 +2428,7 @@ fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, re const decl = @ptrCast(*const clang.NamedDecl, member_decl); break :blk try c.str(decl.getName_bytes_begin()); }; - const ident = try Tag.identifier.create(c.arena, name); - - const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .rhs = ident }); + const node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name }); return maybeSuppressResult(c, scope, result_used, node); } @@ -2698,14 +2702,14 @@ fn transCreatePreCrement( defer block_scope.deinit(); const ref = try block_scope.makeMangledName(c, "ref"); - const expr = try transExpr(c, scope, op_expr, .used); + const expr = try transExpr(c, &block_scope.base, op_expr, .used); const addr_of = try Tag.address_of.create(c.arena, expr); const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); try block_scope.statements.append(ref_decl); const lhs_node = try Tag.identifier.create(c.arena, ref); const ref_node = try Tag.deref.create(c.arena, lhs_node); - const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Tag.one_literal.init(), .used); + const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used); try block_scope.statements.append(node); const break_node = try Tag.break_val.create(c.arena, .{ @@ -2745,7 +2749,7 @@ fn transCreatePostCrement( defer block_scope.deinit(); const ref = try block_scope.makeMangledName(c, "ref"); - const expr = try transExpr(c, scope, op_expr, .used); + const expr = try transExpr(c, &block_scope.base, op_expr, .used); const addr_of = try Tag.address_of.create(c.arena, expr); const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); try block_scope.statements.append(ref_decl); @@ -2757,7 +2761,7 @@ fn transCreatePostCrement( const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node }); try block_scope.statements.append(tmp_decl); - const node = try transCreateNodeInfixOp(c, scope, op, ref_node, Tag.one_literal.init(), .used); + const node = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, Tag.one_literal.init(), .used); try block_scope.statements.append(node); const break_node = try Tag.break_val.create(c.arena, .{ @@ -2864,7 +2868,7 @@ fn transCreateCompoundAssign( defer block_scope.deinit(); const ref = try block_scope.makeMangledName(c, "ref"); - const expr = try transExpr(c, scope, lhs, .used); + const expr = try transExpr(c, &block_scope.base, lhs, .used); const addr_of = try Tag.address_of.create(c.arena, expr); const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of }); try block_scope.statements.append(ref_decl); @@ -2873,16 +2877,16 @@ fn transCreateCompoundAssign( const ref_node = try Tag.deref.create(c.arena, lhs_node); if ((is_mod or is_div) and is_signed) { - const rhs_node = try transExpr(c, scope, rhs, .used); + const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); const builtin = if (is_mod) try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) else try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); - const assign = try transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used); + const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs_node, builtin, .used); try block_scope.statements.append(assign); } else { - var rhs_node = try transExpr(c, scope, rhs, .used); + var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); if (is_shift or requires_int_cast) { // @intCast(rhs) @@ -2894,7 +2898,7 @@ fn transCreateCompoundAssign( rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } - const assign = try transCreateNodeInfixOp(c, scope, op, ref_node, rhs_node, .used); + const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used); try block_scope.statements.append(assign); } @@ -3395,7 +3399,7 @@ fn transCreateNodeAssign( defer block_scope.deinit(); const tmp = try block_scope.makeMangledName(c, "tmp"); - const rhs_node = try transExpr(c, scope, rhs, .used); + const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); try block_scope.statements.append(tmp_decl); @@ -4756,8 +4760,7 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { return error.ParseError; } - const ident = try Tag.identifier.create(c.arena, m.slice()); - node = try Tag.field_access.create(c.arena, .{ .lhs = node, .rhs = ident }); + node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() }); }, .Arrow => { if (m.next().? != .Identifier) { @@ -4766,8 +4769,7 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { } const deref = try Tag.deref.create(c.arena, node); - const ident = try Tag.identifier.create(c.arena, m.slice()); - node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .rhs = ident }); + node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() }); }, .LBracket => { const index = try macroBoolToInt(c, try parseCExpr(c, m, scope)); @@ -4914,13 +4916,12 @@ fn getContainer(c: *Context, node: Node) ?Node { }, .field_access => { - const infix = node.castTag(.field_access).?; + const field_access = node.castTag(.field_access).?; - if (getContainerTypeOf(c, infix.data.lhs)) |ty_node| { + if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| { if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { for (container.data.fields) |field| { - const ident = infix.data.rhs.castTag(.identifier).?; - if (mem.eql(u8, field.name, ident.data)) { + if (mem.eql(u8, field.name, field_access.data.field_name)) { return getContainer(c, field.type); } } @@ -4940,12 +4941,11 @@ fn getContainerTypeOf(c: *Context, ref: Node) ?Node { return getContainer(c, var_decl.data.type); } } - } else if (ref.castTag(.field_access)) |infix| { - if (getContainerTypeOf(c, infix.data.lhs)) |ty_node| { + } else if (ref.castTag(.field_access)) |field_access| { + if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| { if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| { for (container.data.fields) |field| { - const ident = infix.data.rhs.castTag(.identifier).?; - if (mem.eql(u8, field.name, ident.data)) { + if (mem.eql(u8, field.name, field_access.data.field_name)) { return getContainer(c, field.type); } } diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index d108a2852e..014236e002 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -291,7 +291,6 @@ pub const Node = extern union { .array_cat, .ellipsis3, .switch_prong, - .field_access, .assign, .align_cast, .array_access, @@ -309,7 +308,7 @@ pub const Node = extern union { => Payload.Value, .@"if" => Payload.If, .@"while" => Payload.While, - .@"switch" => Payload.Switch, + .@"switch", .array_init => Payload.Switch, .@"break" => Payload.Break, .break_val => Payload.BreakVal, .call => Payload.Call, @@ -317,7 +316,7 @@ pub const Node = extern union { .func => Payload.Func, .@"enum" => Payload.Enum, .@"struct", .@"union" => Payload.Record, - .array_init, .tuple => Payload.ArrayInit, + .tuple => Payload.TupleInit, .container_init => Payload.ContainerInit, .std_meta_cast => Payload.Infix, .block => Payload.Block, @@ -329,6 +328,7 @@ pub const Node = extern union { .enum_redecl => Payload.EnumRedecl, .array_filler => Payload.ArrayFiller, .pub_inline_fn => Payload.PubInlineFn, + .field_access => Payload.FieldAccess, }; } @@ -513,7 +513,7 @@ pub const Payload = struct { }; }; - pub const ArrayInit = struct { + pub const TupleInit = struct { base: Payload, data: []Node, }; @@ -601,6 +601,14 @@ pub const Payload = struct { body: Node, }, }; + + pub const FieldAccess = struct { + base: Payload, + data: struct { + lhs: Node, + field_name: []const u8, + }, + }; }; /// Converts the nodes into a Zig ast. @@ -995,6 +1003,32 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); }, .var_decl => return renderVar(c, node), + .arg_redecl, .alias => { + const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data; + if (node.tag() == .alias) _ = try c.addToken(.keyword_pub, "pub"); + const mut_tok = if (node.tag() == .alias) + try c.addToken(.keyword_const, "const") + else + try c.addToken(.keyword_var, "var"); + _ = try c.addIdentifier(payload.actual); + _ = try c.addToken(.equal, "="); + + const init = try c.addNode(.{ + .tag = .identifier, + .main_token = try c.addIdentifier(payload.mangled), + .data = undefined, + }); + _ = try c.addToken(.semicolon, ";"); + + return c.addNode(.{ + .tag = .simple_var_decl, + .main_token = mut_tok, + .data = .{ + .lhs = 0, + .rhs = init, + }, + }); + }, .int_cast => { const payload = node.castTag(.int_cast).?.data; return renderBuiltinCall(c, "@intCast", &.{ payload.lhs, payload.rhs }); @@ -1339,7 +1373,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { _ = try c.addToken(.l_paren, "("); const cond = try c.addNode(.{ .tag = .bool_not, - .main_token = try c.addToken(.bang, "!"), + .main_token = try c.addToken(.bang, "!"), .data = .{ .lhs = try renderNodeGrouped(c, payload), .rhs = undefined, @@ -1430,7 +1464,77 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, - else => return c.addNode(.{ + .array_access => { + const payload = node.castTag(.array_access).?.data; + const lhs = try renderNode(c, payload.lhs); + const l_bracket = try c.addToken(.l_bracket, "["); + const index_expr = try renderNode(c, payload.rhs); + _ = try c.addToken(.r_bracket, "]"); + return c.addNode(.{ + .tag = .array_access, + .main_token = l_bracket, + .data = .{ + .lhs = lhs, + .rhs = index_expr, + }, + }); + }, + .array_type => { + const payload = node.castTag(.array_type).?.data; + return renderArrayType(c, payload.len, payload.elem_type); + }, + .array_filler => { + const payload = node.castTag(.array_filler).?.data; + + const type_expr = try renderArrayType(c, 1, payload.type); + const l_brace = try c.addToken(.l_brace, "{"); + const val = try renderNode(c, payload.filler); + _ = try c.addToken(.r_brace, "}"); + + const init = try c.addNode(.{ + .tag = .array_init_one, + .main_token = l_brace, + .data = .{ + .lhs = type_expr, + .rhs = val, + }, + }); + return c.addNode(.{ + .tag = .array_cat, + .main_token = try c.addToken(.asterisk_asterisk, "**"), + .data = .{ + .lhs = init, + .rhs = try c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.count}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }), + }, + }); + }, + .empty_array => { + const payload = node.castTag(.empty_array).?.data; + + const type_expr = try renderArrayType(c, 0, payload); + return renderArrayInit(c, 0, &.{}); + }, + .array_init => { + const payload = node.castTag(.array_init).?.data; + const type_expr = try renderNode(c, payload.cond); + return renderArrayInit(c, type_expr, payload.cases); + }, + .field_access => { + const payload = node.castTag(.field_access).?.data; + const lhs = try renderNode(c, payload.lhs); + return renderFieldAccess(c, lhs, payload.field_name); + }, + .tuple, + .@"enum", + .@"struct", + .@"union", + .container_init, + .enum_redecl, + => return c.addNode(.{ .tag = .identifier, .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), .data = .{ @@ -1441,10 +1545,88 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { } } +fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeIndex { + return c.addNode(.{ + .tag = .field_access, + .main_token = try c.addToken(.period, "."), + .data = .{ + .lhs = lhs, + .rhs = try c.addIdentifier(field_name), + }, + }); +} + +fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex { + const l_brace = try c.addToken(.l_brace, "{"); + const res = switch (inits.len) { + 0 => try c.addNode(.{ + .tag = .struct_init_one, + .main_token = l_brace, + .data = .{ + .lhs = lhs, + .rhs = 0, + }, + }), + 1 => blk: { + const init = try renderNode(c, inits[0]); + break :blk try c.addNode(.{ + .tag = .array_init_one, + .main_token = l_brace, + .data = .{ + .lhs = lhs, + .rhs = init, + }, + }); + }, + else => blk: { + var rendered = try c.gpa.alloc(NodeIndex, inits.len); + defer c.gpa.free(rendered); + + for (inits) |init, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + rendered[i] = try renderNode(c, init); + } + const span = try c.listToSpan(rendered); + break :blk try c.addNode(.{ + .tag = .array_init, + .main_token = l_brace, + .data = .{ + .lhs = lhs, + .rhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + }, + }); + }, + }; + _ = try c.addToken(.r_brace, "}"); + return res; +} + +fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { + const l_bracket = try c.addToken(.l_bracket, "["); + const len_expr = try c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}), + .data = .{ .lhs = undefined, .rhs = undefined }, + }); + _ = try c.addToken(.r_bracket, "]"); + const elem_type_expr = try renderNode(c, elem_type); + return c.addNode(.{ + .tag = .array_type, + .main_token = l_bracket, + .data = .{ + .lhs = len_expr, + .rhs = elem_type_expr, + }, + }); +} + fn addSemicolonIfNeeded(c: *Context, node: Node) !void { switch (node.tag()) { .warning => unreachable, - .var_decl, .var_simple, .block, .empty_block, .@"switch" => {}, + .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .@"switch" => {}, .while_true => { const payload = node.castTag(.while_true).?.data; return addSemicolonIfNeeded(c, payload); @@ -1517,6 +1699,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .negate, .negate_wrap, .bit_not, + .func, => { // no grouping needed return renderNode(c, node); @@ -1572,7 +1755,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .switch_prong, .warning, .var_decl, - .func, .fail_decl, .arg_redecl, .alias, @@ -1658,22 +1840,8 @@ fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeInde }); var access_chain = import_node; - access_chain = try c.addNode(.{ - .tag = .field_access, - .main_token = try c.addToken(.period, "."), - .data = .{ - .lhs = access_chain, - .rhs = try c.addIdentifier(first), - }, - }); - access_chain = try c.addNode(.{ - .tag = .field_access, - .main_token = try c.addToken(.period, "."), - .data = .{ - .lhs = access_chain, - .rhs = try c.addIdentifier(second), - }, - }); + access_chain = try renderFieldAccess(c, access_chain, first); + access_chain = try renderFieldAccess(c, access_chain, second); return access_chain; } @@ -1974,10 +2142,10 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { const body = if (payload.body) |some| try renderNode(c, some) - else blk: { + else if (payload.is_extern) blk: { _ = try c.addToken(.semicolon, ";"); break :blk 0; - }; + } else return fn_proto; return c.addNode(.{ .tag = .fn_decl, From 77a11e6873d0f30950d8a523c990d32b5a25a658 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 15 Feb 2021 13:27:14 +0200 Subject: [PATCH 26/36] translate-c: render records and enums --- src/translate_c.zig | 5 +- src/translate_c/ast.zig | 326 +++++++++++++++++++++++++++++++++------- 2 files changed, 277 insertions(+), 54 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index ce8fea7a71..995ff16b1c 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1871,7 +1871,10 @@ fn transInitListExprRecord( }); } - return Tag.container_init.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items)); + return Tag.container_init.create(c.arena, .{ + .lhs = ty_node, + .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items), + }); } fn transInitListExprArray( diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 014236e002..b22e61c65e 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -520,7 +520,10 @@ pub const Payload = struct { pub const ContainerInit = struct { base: Payload, - data: []Initializer, + data: struct { + lhs: Node, + inits: []Initializer, + }, pub const Initializer = struct { name: []const u8, @@ -1528,20 +1531,250 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const lhs = try renderNode(c, payload.lhs); return renderFieldAccess(c, lhs, payload.field_name); }, - .tuple, - .@"enum", - .@"struct", - .@"union", - .container_init, - .enum_redecl, - => return c.addNode(.{ - .tag = .identifier, - .main_token = try c.addTokenFmt(.identifier, "@\"TODO {}\"", .{node.tag()}), + .@"struct", .@"union" => return renderRecord(c, node), + .@"enum" => { + const payload = node.castTag(.@"enum").?.data; + const enum_tok = try c.addToken(.keyword_enum, "enum"); + + _ = try c.addToken(.l_brace, "{"); + const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.len + 1, 1)); + defer c.gpa.free(members); + members[0] = 0; + members[1] = 0; + + for (payload) |field, i| { + const name_tok = try c.addIdentifier(field.name); + const value_expr = if (field.value) |some| blk: { + _ = try c.addToken(.equal, "="); + break :blk try renderNode(c, some); + } else 0; + + members[i] = try c.addNode(.{ + .tag = .container_field_init, + .main_token = name_tok, + .data = .{ + .lhs = 0, + .rhs = value_expr, + }, + }); + _ = try c.addToken(.comma, ","); + } + // make non-exhaustive + members[payload.len] = try c.addNode(.{ + .tag = .container_field_init, + .main_token = try c.addIdentifier("_"), + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }); + _ = try c.addToken(.comma, ","); + _ = try c.addToken(.r_brace, "}"); + + if (members.len <= 2) { + return c.addNode(.{ + .tag = .container_decl_two_comma, + .main_token = enum_tok, + .data = .{ + .lhs = members[0], + .rhs = members[1], + }, + }); + } else { + const span = try c.listToSpan(members); + return c.addNode(.{ + .tag = .container_decl_comma, + .main_token = enum_tok, + .data = .{ + .lhs = span.start, + .rhs = span.end, + }, + }); + } + }, + .enum_redecl => { + const payload = node.castTag(.enum_redecl).?.data; + _ = try c.addToken(.keyword_pub, "pub"); + const const_tok = try c.addToken(.keyword_const, "const"); + _ = try c.addIdentifier(payload.enum_val_name); + _ = try c.addToken(.equal, "="); + + const enum_to_int_tok = try c.addToken(.builtin, "@enumToInt"); + _ = try c.addToken(.l_paren, "("); + const enum_name = try c.addNode(.{ + .tag = .identifier, + .main_token = try c.addIdentifier(payload.enum_name), + .data = undefined, + }); + const field_access = try renderFieldAccess(c, enum_name, payload.field_name); + const init_node = try c.addNode(.{ + .tag = .builtin_call_two, + .main_token = enum_to_int_tok, + .data = .{ + .lhs = field_access, + .rhs = 0, + }, + }); + _ = try c.addToken(.r_paren, ")"); + _ = try c.addToken(.semicolon, ";"); + + return c.addNode(.{ + .tag = .simple_var_decl, + .main_token = const_tok, + .data = .{ + .lhs = 0, + .rhs = init_node, + }, + }); + }, + .tuple => { + const payload = node.castTag(.tuple).?.data; + _ = try c.addToken(.period, "."); + const l_brace = try c.addToken(.l_brace, "{"); + var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 1)); + defer c.gpa.free(inits); + inits[0] = 0; + for (payload) |init, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + inits[i] = try renderNode(c, init); + } + _ = try c.addToken(.r_brace, "}"); + if (payload.len < 3) { + return c.addNode(.{ + .tag = .array_init_dot_two, + .main_token = l_brace, + .data = .{ + .lhs = inits[0], + .rhs = inits[1], + }, + }); + } else { + const span = try c.listToSpan(inits); + return c.addNode(.{ + .tag = .array_init_dot, + .main_token = l_brace, + .data = .{ + .lhs = span.start, + .rhs = span.end, + }, + }); + } + }, + .container_init => { + const payload = node.castTag(.container_init).?.data; + const lhs = try renderNode(c, payload.lhs); + + const l_brace = try c.addToken(.l_brace, "{"); + var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.inits.len, 1)); + defer c.gpa.free(inits); + inits[0] = 0; + for (payload.inits) |init, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + _ = try c.addToken(.period, "."); + _ = try c.addIdentifier(init.name); + _ = try c.addToken(.equal, "="); + inits[i] = try renderNode(c, init.value); + } + _ = try c.addToken(.r_brace, "}"); + + if (payload.inits.len < 2) { + return c.addNode(.{ + .tag = .struct_init_one, + .main_token = l_brace, + .data = .{ + .lhs = lhs, + .rhs = inits[0], + }, + }); + } else { + const span = try c.listToSpan(inits); + return c.addNode(.{ + .tag = .struct_init, + .main_token = l_brace, + .data = .{ + .lhs = lhs, + .rhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + }, + }); + } + }, + } +} + +fn renderRecord(c: *Context, node: Node) !NodeIndex { + const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data; + if (payload.is_packed) _ = try c.addToken(.keyword_packed, "packed"); + const kind_tok = if (node.tag() == .@"struct") + try c.addToken(.keyword_struct, "struct") + else + try c.addToken(.keyword_union, "union"); + + _ = try c.addToken(.l_brace, "{"); + const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len, 2)); + defer c.gpa.free(members); + members[0] = 0; + members[1] = 0; + + for (payload.fields) |field, i| { + const name_tok = try c.addIdentifier(field.name); + _ = try c.addToken(.colon, ":"); + const type_expr = try renderNode(c, field.type); + + const alignment = field.alignment orelse { + members[i] = try c.addNode(.{ + .tag = .container_field_init, + .main_token = name_tok, + .data = .{ + .lhs = type_expr, + .rhs = 0, + }, + }); + _ = try c.addToken(.comma, ","); + continue; + }; + _ = try c.addToken(.keyword_align, "align"); + _ = try c.addToken(.l_paren, "("); + const align_expr = try c.addNode(.{ + .tag = .integer_literal, + .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{alignment}), + .data = undefined, + }); + _ = try c.addToken(.r_paren, ")"); + + members[i] = try c.addNode(.{ + .tag = .container_field_align, + .main_token = name_tok, .data = .{ - .lhs = undefined, - .rhs = undefined, + .lhs = type_expr, + .rhs = align_expr, }, - }), + }); + _ = try c.addToken(.comma, ","); + } + _ = try c.addToken(.r_brace, "}"); + + if (members.len <= 2) { + return c.addNode(.{ + .tag = .container_decl_two_comma, + .main_token = kind_tok, + .data = .{ + .lhs = members[0], + .rhs = members[1], + }, + }); + } else { + const span = try c.listToSpan(members); + return c.addNode(.{ + .tag = .container_decl_comma, + .main_token = kind_tok, + .data = .{ + .lhs = span.start, + .rhs = span.end, + }, + }); } } @@ -1558,50 +1791,37 @@ fn renderFieldAccess(c: *Context, lhs: NodeIndex, field_name: []const u8) !NodeI fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex { const l_brace = try c.addToken(.l_brace, "{"); - const res = switch (inits.len) { - 0 => try c.addNode(.{ - .tag = .struct_init_one, + var rendered = try c.gpa.alloc(NodeIndex, std.math.max(inits.len, 1)); + defer c.gpa.free(rendered); + rendered[0] = 0; + for (inits) |init, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + rendered[i] = try renderNode(c, init); + } + _ = try c.addToken(.r_brace, "}"); + if (inits.len < 2) { + return c.addNode(.{ + .tag = .array_init_one, .main_token = l_brace, .data = .{ .lhs = lhs, - .rhs = 0, + .rhs = rendered[0], }, - }), - 1 => blk: { - const init = try renderNode(c, inits[0]); - break :blk try c.addNode(.{ - .tag = .array_init_one, - .main_token = l_brace, - .data = .{ - .lhs = lhs, - .rhs = init, - }, - }); - }, - else => blk: { - var rendered = try c.gpa.alloc(NodeIndex, inits.len); - defer c.gpa.free(rendered); - - for (inits) |init, i| { - if (i != 0) _ = try c.addToken(.comma, ","); - rendered[i] = try renderNode(c, init); - } - const span = try c.listToSpan(rendered); - break :blk try c.addNode(.{ - .tag = .array_init, - .main_token = l_brace, - .data = .{ - .lhs = lhs, - .rhs = try c.addExtra(NodeSubRange{ - .start = span.start, - .end = span.end, - }), - }, - }); - }, - }; - _ = try c.addToken(.r_brace, "}"); - return res; + }); + } else { + const span = try c.listToSpan(rendered); + return c.addNode(.{ + .tag = .array_init, + .main_token = l_brace, + .data = .{ + .lhs = lhs, + .rhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + }, + }); + } } fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { From 78fba4e0213dc9bcc2274ef6c31023256a235301 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 15 Feb 2021 16:07:21 +0200 Subject: [PATCH 27/36] translate-c: get all run-translated-c tests passing --- lib/std/zig/parser_test.zig | 12 +-- lib/std/zig/render.zig | 2 +- src/translate_c.zig | 43 +++++--- src/translate_c/ast.zig | 195 +++++++++++++++--------------------- 4 files changed, 120 insertions(+), 132 deletions(-) diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e4778b35e1..623682056c 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3669,12 +3669,12 @@ test "zig fmt: hexadeciaml float literals with underscore separators" { ); } -//test "zig fmt: C var args" { -// try testCanonical( -// \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; -// \\ -// ); -//} +test "zig fmt: C var args" { + try testCanonical( + \\pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + \\ + ); +} //test "zig fmt: Only indent multiline string literals in function calls" { // try testCanonical( diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 6fd091d32c..89a9f472ef 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1308,7 +1308,7 @@ fn renderFnProto(ais: *Ais, tree: ast.Tree, fn_proto: ast.full.FnProto, space: S .r_paren => break, .comma => { try renderToken(ais, tree, last_param_token, .space); // , - last_param_token += 1; + continue; }, else => {}, // Parameter type without a name. } diff --git a/src/translate_c.zig b/src/translate_c.zig index 995ff16b1c..8b7aedcee1 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -990,7 +990,10 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { })); } - break :blk try Tag.@"enum".create(c.arena, try c.arena.dupe(ast.Payload.Enum.Field, fields.items)); + break :blk try Tag.@"enum".create(c.arena, .{ + .int_type = init_arg_expr, + .fields = try c.arena.dupe(ast.Payload.Enum.Field, fields.items), + }); } else blk: { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {}); break :blk Tag.opaque_literal.init(); @@ -1540,8 +1543,8 @@ fn finishBoolExpr( } }, .Pointer => { - // node == null - return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); + // node != null + return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); }, .Typedef => { const typedef_ty = @ptrCast(*const clang.TypedefType, ty); @@ -1675,7 +1678,8 @@ fn transStringLiteralAsArray( const ty = expr_base.getType().getTypePtr(); const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); - const arr_type = try transQualType(c, const_arr_ty.getElementType(), expr_base.getBeginLoc()); + const elem_type = try transQualType(c, const_arr_ty.getElementType(), expr_base.getBeginLoc()); + const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type }); const init_list = try c.arena.alloc(Node, array_size); var i: c_uint = 0; @@ -2668,7 +2672,7 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used)); }, .LNot => { - return Tag.not.create(c.arena, try transExpr(c, scope, op_expr, .used)); + return Tag.not.create(c.arena, try transBoolExpr(c, scope, op_expr, .used)); }, .Extension => { return transExpr(c, scope, stmt.getSubExpr(), used); @@ -2969,8 +2973,15 @@ fn transBreak(c: *Context, scope: *Scope) TransError!Node { fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { // TODO use something more accurate - const dbl = stmt.getValueAsApproximateDouble(); - const node = try transCreateNodeNumber(c, dbl, .float); + var dbl = stmt.getValueAsApproximateDouble(); + const is_negative = dbl < 0; + if (is_negative) dbl = -dbl; + const str = try std.fmt.allocPrint(c.arena, "{d}", .{dbl}); + var node = if (dbl == std.math.floor(dbl)) + try Tag.integer_literal.create(c.arena, str) + else + try Tag.float_literal.create(c.arena, str); + if (is_negative) node = try Tag.negate.create(c.arena, node); return maybeSuppressResult(c, scope, used, node); } @@ -3004,8 +3015,11 @@ fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang }, }; defer cond_scope.deinit(); - const cond_node = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); - var then_body = try Tag.identifier.create(c.arena, mangled_name); + + const cond_ident = try Tag.identifier.create(c.arena, mangled_name); + const ty = getExprQualType(c, cond_expr).getTypePtr(); + const cond_node = try finishBoolExpr(c, &cond_scope.base, cond_expr.getBeginLoc(), ty, cond_ident, .used); + var then_body = cond_ident; if (!res_is_bool and isBoolRes(init_node)) { then_body = try Tag.bool_to_int.create(c.arena, then_body); } @@ -3489,11 +3503,13 @@ fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node { else => @compileError("unimplemented"), } - const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative }; + const big: math.big.int.Const = .{ .limbs = limbs, .positive = true }; const str = big.toStringAlloc(c.arena, 10, false) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, }; - return Tag.integer_literal.create(c.arena, str); + const res = try Tag.integer_literal.create(c.arena, str); + if (is_negative) return Tag.negate.create(c.arena, res); + return res; } fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node { @@ -3567,7 +3583,7 @@ fn transCreateNodeShiftOp( const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location); const rhs = try transExprCoercing(c, scope, rhs_expr, .used); - const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type }); + const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs }); return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used); } @@ -3622,7 +3638,8 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const is_volatile = child_qt.isVolatileQualified(); const elem_type = try transQualType(c, child_qt, source_loc); if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) { - return Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); + const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); + return Tag.optional_type.create(c.arena, ptr); } return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index b22e61c65e..3921acd546 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -491,7 +491,10 @@ pub const Payload = struct { pub const Enum = struct { base: Payload, - data: []Field, + data: struct { + int_type: Node, + fields: []Field, + }, pub const Field = struct { name: []const u8, @@ -825,11 +828,6 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .main_token = try c.addToken(.identifier, "void"), .data = undefined, }), - .@"anytype" => return c.addNode(.{ - .tag = .@"anytype", - .main_token = try c.addToken(.keyword_anytype, "anytype"), - .data = undefined, - }), .noreturn_type => return c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.identifier, "noreturn"), @@ -946,7 +944,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const payload = node.castTag(.char_literal).?.data; return c.addNode(.{ .tag = .identifier, - .main_token = try c.addToken(.string_literal, payload), + .main_token = try c.addToken(.char_literal, payload), .data = undefined, }); }, @@ -1268,7 +1266,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const lhs = try c.addNode(.{ .tag = .identifier, .main_token = try c.addToken(.identifier, "_"), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); return c.addNode(.{ .tag = .assign, @@ -1510,7 +1508,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = try c.addNode(.{ .tag = .integer_literal, .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{payload.count}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }), }, }); @@ -1519,7 +1517,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const payload = node.castTag(.empty_array).?.data; const type_expr = try renderArrayType(c, 0, payload); - return renderArrayInit(c, 0, &.{}); + return renderArrayInit(c, type_expr, &.{}); }, .array_init => { const payload = node.castTag(.array_init).?.data; @@ -1534,15 +1532,17 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .@"struct", .@"union" => return renderRecord(c, node), .@"enum" => { const payload = node.castTag(.@"enum").?.data; + _ = try c.addToken(.keyword_extern, "extern"); const enum_tok = try c.addToken(.keyword_enum, "enum"); - + _ = try c.addToken(.l_paren, "("); + const arg_expr = try renderNode(c, payload.int_type); + _ = try c.addToken(.r_paren, ")"); _ = try c.addToken(.l_brace, "{"); - const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.len + 1, 1)); + const members = try c.gpa.alloc(NodeIndex, std.math.max(payload.fields.len + 1, 1)); defer c.gpa.free(members); members[0] = 0; - members[1] = 0; - for (payload) |field, i| { + for (payload.fields) |field, i| { const name_tok = try c.addIdentifier(field.name); const value_expr = if (field.value) |some| blk: { _ = try c.addToken(.equal, "="); @@ -1560,7 +1560,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { _ = try c.addToken(.comma, ","); } // make non-exhaustive - members[payload.len] = try c.addNode(.{ + members[payload.fields.len] = try c.addNode(.{ .tag = .container_field_init, .main_token = try c.addIdentifier("_"), .data = .{ @@ -1571,26 +1571,18 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { _ = try c.addToken(.comma, ","); _ = try c.addToken(.r_brace, "}"); - if (members.len <= 2) { - return c.addNode(.{ - .tag = .container_decl_two_comma, - .main_token = enum_tok, - .data = .{ - .lhs = members[0], - .rhs = members[1], - }, - }); - } else { - const span = try c.listToSpan(members); - return c.addNode(.{ - .tag = .container_decl_comma, - .main_token = enum_tok, - .data = .{ - .lhs = span.start, - .rhs = span.end, - }, - }); - } + const span = try c.listToSpan(members); + return c.addNode(.{ + .tag = .container_decl_arg_comma, + .main_token = enum_tok, + .data = .{ + .lhs = arg_expr, + .rhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + }, + }); }, .enum_redecl => { const payload = node.castTag(.enum_redecl).?.data; @@ -1701,12 +1693,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }); } }, + .@"anytype" => unreachable, // Handled in renderParams } } fn renderRecord(c: *Context, node: Node) !NodeIndex { const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data; - if (payload.is_packed) _ = try c.addToken(.keyword_packed, "packed"); + if (payload.is_packed) + _ = try c.addToken(.keyword_packed, "packed") + else + _ = try c.addToken(.keyword_extern, "extern"); const kind_tok = if (node.tag() == .@"struct") try c.addToken(.keyword_struct, "struct") else @@ -1829,7 +1825,7 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { const len_expr = try c.addNode(.{ .tag = .integer_literal, .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{len}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); _ = try c.addToken(.r_bracket, "]"); const elem_type_expr = try renderNode(c, elem_type); @@ -1920,6 +1916,8 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .negate_wrap, .bit_not, .func, + .call, + .array_type, => { // no grouping needed return renderNode(c, node); @@ -1954,7 +1952,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .array_cat, .array_filler, .@"if", - .call, .@"enum", .@"struct", .@"union", @@ -1962,7 +1959,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .tuple, .container_init, .block, - .array_type, => return c.addNode(.{ .tag = .grouped_expression, .main_token = try c.addToken(.l_paren, "("), @@ -2161,7 +2157,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { const res = try c.addNode(.{ .tag = .integer_literal, .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); break :blk res; @@ -2173,7 +2169,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { const res = try c.addNode(.{ .tag = .string_literal, .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); break :blk res; @@ -2232,39 +2228,10 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { const fn_token = try c.addToken(.keyword_fn, "fn"); if (payload.name) |some| _ = try c.addIdentifier(some); - _ = try c.addToken(.l_paren, "("); - const first = if (payload.params.len != 0) blk: { - const param = payload.params[0]; - if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); - if (param.name) |some| { - _ = try c.addIdentifier(some); - _ = try c.addToken(.colon, ":"); - } - break :blk try renderNode(c, param.type); - } else 0; - + const params = try renderParams(c, payload.params, payload.is_var_args); + defer params.deinit(); var span: NodeSubRange = undefined; - if (payload.params.len > 1) { - var params = try c.gpa.alloc(NodeIndex, payload.params.len); - defer c.gpa.free(params); - - params[0] = first; - for (payload.params[1..]) |param, i| { - _ = try c.addToken(.comma, ","); - if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); - if (param.name) |some| { - _ = try c.addIdentifier(some); - _ = try c.addToken(.colon, ":"); - } - params[i + 1] = try renderNode(c, param.type); - } - span = try c.listToSpan(params); - } - if (payload.is_var_args) { - if (payload.params.len != 0) _ = try c.addToken(.comma, ","); - _ = try c.addToken(.ellipsis3, "..."); - } - _ = try c.addToken(.r_paren, ")"); + if (params.items.len > 1) span = try c.listToSpan(params.items); const align_expr = if (payload.alignment) |some| blk: { _ = try c.addToken(.keyword_align, "align"); @@ -2272,7 +2239,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { const res = try c.addNode(.{ .tag = .integer_literal, .main_token = try c.addTokenFmt(.integer_literal, "{d}", .{some}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); break :blk res; @@ -2284,7 +2251,7 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { const res = try c.addNode(.{ .tag = .string_literal, .main_token = try c.addTokenFmt(.string_literal, "\"{s}\"", .{std.zig.fmtEscapes(some)}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); break :blk res; @@ -2296,8 +2263,8 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { _ = try c.addToken(.period, "."); const res = try c.addNode(.{ .tag = .enum_literal, - .main_token = try c.addTokenFmt(.identifier, "{}", .{some}), - .data = .{ .lhs = undefined, .rhs = undefined }, + .main_token = try c.addTokenFmt(.identifier, "{s}", .{@tagName(some)}), + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); break :blk res; @@ -2307,12 +2274,12 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { const fn_proto = try blk: { if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) { - if (payload.params.len < 2) + if (params.items.len < 2) break :blk c.addNode(.{ .tag = .fn_proto_simple, .main_token = fn_token, .data = .{ - .lhs = first, + .lhs = params.items[0], .rhs = return_type_expr, }, }) @@ -2329,13 +2296,13 @@ fn renderFunc(c: *Context, node: Node) !NodeIndex { }, }); } - if (payload.params.len < 2) + if (params.items.len < 2) break :blk c.addNode(.{ .tag = .fn_proto_one, .main_token = fn_token, .data = .{ .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{ - .param = first, + .param = params.items[0], .align_expr = align_expr, .section_expr = section_expr, .callconv_expr = callconv_expr, @@ -2383,35 +2350,10 @@ fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { const fn_token = try c.addToken(.keyword_fn, "fn"); _ = try c.addIdentifier(payload.name); - _ = try c.addToken(.l_paren, "("); - const first = if (payload.params.len != 0) blk: { - const param = payload.params[0]; - if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); - if (param.name) |some| { - _ = try c.addIdentifier(some); - _ = try c.addToken(.colon, ":"); - } - break :blk try renderNode(c, param.type); - } else 0; - + const params = try renderParams(c, payload.params, false); + defer params.deinit(); var span: NodeSubRange = undefined; - if (payload.params.len > 1) { - var params = try c.gpa.alloc(NodeIndex, payload.params.len); - defer c.gpa.free(params); - - params[0] = first; - for (payload.params[1..]) |param, i| { - _ = try c.addToken(.comma, ","); - if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); - if (param.name) |some| { - _ = try c.addIdentifier(some); - _ = try c.addToken(.colon, ":"); - } - params[i + 1] = try renderNode(c, param.type); - } - span = try c.listToSpan(params); - } - _ = try c.addToken(.r_paren, ")"); + if (params.items.len > 1) span = try c.listToSpan(params.items); const callconv_expr = blk: { _ = try c.addToken(.keyword_callconv, "callconv"); @@ -2420,7 +2362,7 @@ fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { const res = try c.addNode(.{ .tag = .enum_literal, .main_token = try c.addToken(.identifier, "Inline"), - .data = .{ .lhs = undefined, .rhs = undefined }, + .data = undefined, }); _ = try c.addToken(.r_paren, ")"); break :blk res; @@ -2428,13 +2370,13 @@ fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { const return_type_expr = try renderNode(c, payload.return_type); const fn_proto = try blk: { - if (payload.params.len < 2) + if (params.items.len < 2) break :blk c.addNode(.{ .tag = .fn_proto_one, .main_token = fn_token, .data = .{ .lhs = try c.addExtra(std.zig.ast.Node.FnProtoOne{ - .param = first, + .param = params.items[0], .align_expr = 0, .section_expr = 0, .callconv_expr = callconv_expr, @@ -2467,3 +2409,32 @@ fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { }, }); } + +fn renderParams(c: *Context, params: []Payload.Param, is_var_args: bool) !std.ArrayList(NodeIndex) { + _ = try c.addToken(.l_paren, "("); + var rendered = std.ArrayList(NodeIndex).init(c.gpa); + errdefer rendered.deinit(); + try rendered.ensureCapacity(std.math.max(params.len, 1)); + + for (params) |param, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + if (param.is_noalias) _ = try c.addToken(.keyword_noalias, "noalias"); + if (param.name) |some| { + _ = try c.addIdentifier(some); + _ = try c.addToken(.colon, ":"); + } + if (param.type.tag() == .@"anytype") { + _ = try c.addToken(.keyword_anytype, "anytype"); + continue; + } + rendered.appendAssumeCapacity(try renderNode(c, param.type)); + } + if (is_var_args) { + if (params.len != 0) _ = try c.addToken(.comma, ","); + _ = try c.addToken(.ellipsis3, "..."); + } + _ = try c.addToken(.r_paren, ")"); + + if (rendered.items.len == 0) rendered.appendAssumeCapacity(0); + return rendered; +} From 74e9d4ca820f9940b47ae658c41e7edb315681c8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 16 Feb 2021 15:45:33 +0200 Subject: [PATCH 28/36] translate-c: get all translate-c tests passing --- src/translate_c.zig | 14 +- src/translate_c/ast.zig | 101 +++++---- test/translate_c.zig | 441 +++++++++++++++++++++------------------- 3 files changed, 302 insertions(+), 254 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 8b7aedcee1..fbd0ec6245 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -622,7 +622,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co return; // Avoid processing this decl twice const is_pub = mangled_name == null; - const is_thread_local = var_decl.getTLSKind() != .None; + const is_threadlocal = var_decl.getTLSKind() != .None; const scope = &c.global_scope.base; // TODO https://github.com/ziglang/zig/issues/3756 @@ -706,6 +706,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co .is_const = is_const, .is_extern = is_extern, .is_export = is_export, + .is_threadlocal = is_threadlocal, .linksection_string = linksection_string, .alignment = alignment, .name = checked_name, @@ -1307,6 +1308,7 @@ fn transDeclStmtOne( .is_const = is_const, .is_extern = false, .is_export = false, + .is_threadlocal = false, .linksection_string = null, .alignment = null, .name = mangled_name, @@ -2886,11 +2888,11 @@ fn transCreateCompoundAssign( if ((is_mod or is_div) and is_signed) { const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); const builtin = if (is_mod) - try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }) + try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node }) else - try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node }); - const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, lhs_node, builtin, .used); + const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used); try block_scope.statements.append(assign); } else { var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); @@ -4794,6 +4796,10 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { .LBracket => { const index = try macroBoolToInt(c, try parseCExpr(c, m, scope)); node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index }); + if (m.next().? != .RBracket) { + try m.fail(c, "unable to translate C expr: expected ']'", .{}); + return error.ParseError; + } }, .LParen => { var args = std.ArrayList(Node).init(c.gpa); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 3921acd546..8f3d605d8e 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -458,6 +458,7 @@ pub const Payload = struct { is_const: bool, is_extern: bool, is_export: bool, + is_threadlocal: bool, alignment: ?c_uint, linksection_string: ?[]const u8, name: []const u8, @@ -1164,42 +1165,42 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, - .add => return renderBinOp(c, node, .add, .plus, "+"), + .add => return renderBinOpGrouped(c, node, .add, .plus, "+"), .add_assign => return renderBinOp(c, node, .assign_add, .plus_equal, "+="), - .add_wrap => return renderBinOp(c, node, .add_wrap, .plus_percent, "+%"), + .add_wrap => return renderBinOpGrouped(c, node, .add_wrap, .plus_percent, "+%"), .add_wrap_assign => return renderBinOp(c, node, .assign_add_wrap, .plus_percent_equal, "+%="), - .sub => return renderBinOp(c, node, .sub, .minus, "-"), + .sub => return renderBinOpGrouped(c, node, .sub, .minus, "-"), .sub_assign => return renderBinOp(c, node, .assign_sub, .minus_equal, "-="), - .sub_wrap => return renderBinOp(c, node, .sub_wrap, .minus_percent, "-%"), + .sub_wrap => return renderBinOpGrouped(c, node, .sub_wrap, .minus_percent, "-%"), .sub_wrap_assign => return renderBinOp(c, node, .assign_sub_wrap, .minus_percent_equal, "-%="), - .mul => return renderBinOp(c, node, .mul, .asterisk, "*"), + .mul => return renderBinOpGrouped(c, node, .mul, .asterisk, "*"), .mul_assign => return renderBinOp(c, node, .assign_mul, .asterisk_equal, "*="), - .mul_wrap => return renderBinOp(c, node, .mul_wrap, .asterisk_percent, "*="), + .mul_wrap => return renderBinOpGrouped(c, node, .mul_wrap, .asterisk_percent, "*%"), .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="), - .div => return renderBinOp(c, node, .div, .slash, "/"), + .div => return renderBinOpGrouped(c, node, .div, .slash, "/"), .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="), - .shl => return renderBinOp(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"), + .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"), .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="), - .shr => return renderBinOp(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"), + .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"), .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="), - .mod => return renderBinOp(c, node, .mod, .percent, "%"), + .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"), .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="), - .@"and" => return renderBinOp(c, node, .bool_and, .keyword_and, "and"), - .@"or" => return renderBinOp(c, node, .bool_or, .keyword_or, "or"), - .less_than => return renderBinOp(c, node, .less_than, .angle_bracket_left, "<"), - .less_than_equal => return renderBinOp(c, node, .less_or_equal, .angle_bracket_left_equal, "<="), - .greater_than => return renderBinOp(c, node, .greater_than, .angle_bracket_right, ">="), - .greater_than_equal => return renderBinOp(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="), - .equal => return renderBinOp(c, node, .equal_equal, .equal_equal, "=="), - .not_equal => return renderBinOp(c, node, .bang_equal, .bang_equal, "!="), - .bit_and => return renderBinOp(c, node, .bit_and, .ampersand, "&"), + .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"), + .@"or" => return renderBinOpGrouped(c, node, .bool_or, .keyword_or, "or"), + .less_than => return renderBinOpGrouped(c, node, .less_than, .angle_bracket_left, "<"), + .less_than_equal => return renderBinOpGrouped(c, node, .less_or_equal, .angle_bracket_left_equal, "<="), + .greater_than => return renderBinOpGrouped(c, node, .greater_than, .angle_bracket_right, ">="), + .greater_than_equal => return renderBinOpGrouped(c, node, .greater_or_equal, .angle_bracket_right_equal, ">="), + .equal => return renderBinOpGrouped(c, node, .equal_equal, .equal_equal, "=="), + .not_equal => return renderBinOpGrouped(c, node, .bang_equal, .bang_equal, "!="), + .bit_and => return renderBinOpGrouped(c, node, .bit_and, .ampersand, "&"), .bit_and_assign => return renderBinOp(c, node, .assign_bit_and, .ampersand_equal, "&="), - .bit_or => return renderBinOp(c, node, .bit_or, .pipe, "|"), + .bit_or => return renderBinOpGrouped(c, node, .bit_or, .pipe, "|"), .bit_or_assign => return renderBinOp(c, node, .assign_bit_or, .pipe_equal, "|="), - .bit_xor => return renderBinOp(c, node, .bit_xor, .caret, "^"), + .bit_xor => return renderBinOpGrouped(c, node, .bit_xor, .caret, "^"), .bit_xor_assign => return renderBinOp(c, node, .assign_bit_xor, .caret_equal, "^="), .array_cat => return renderBinOp(c, node, .array_cat, .plus_plus, "++"), - .ellipsis3 => return renderBinOp(c, node, .switch_range, .ellipsis3, "..."), + .ellipsis3 => return renderBinOpGrouped(c, node, .switch_range, .ellipsis3, "..."), .assign => return renderBinOp(c, node, .assign, .equal, "="), .empty_block => { const l_brace = try c.addToken(.l_brace, "{"); @@ -1222,7 +1223,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { _ = try c.addToken(.r_brace, "}"); return c.addNode(.{ - .tag = .block_two, + .tag = .block_two_semicolon, .main_token = l_brace, .data = .{ .lhs = stmt, @@ -1410,13 +1411,13 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { var cases = try c.gpa.alloc(NodeIndex, payload.cases.len); defer c.gpa.free(cases); for (payload.cases) |case, i| { - if (i != 0) _ = try c.addToken(.comma, ","); cases[i] = try renderNode(c, case); + _ = try c.addToken(.comma, ","); } const span = try c.listToSpan(cases); _ = try c.addToken(.r_brace, "}"); return c.addNode(.{ - .tag = .@"switch", + .tag = .switch_comma, .main_token = switch_tok, .data = .{ .lhs = cond, @@ -1623,9 +1624,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { const payload = node.castTag(.tuple).?.data; _ = try c.addToken(.period, "."); const l_brace = try c.addToken(.l_brace, "{"); - var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 1)); + var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 2)); defer c.gpa.free(inits); inits[0] = 0; + inits[1] = 0; for (payload) |init, i| { if (i != 0) _ = try c.addToken(.comma, ","); inits[i] = try renderNode(c, init); @@ -1661,17 +1663,17 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { defer c.gpa.free(inits); inits[0] = 0; for (payload.inits) |init, i| { - if (i != 0) _ = try c.addToken(.comma, ","); _ = try c.addToken(.period, "."); _ = try c.addIdentifier(init.name); _ = try c.addToken(.equal, "="); inits[i] = try renderNode(c, init.value); + _ = try c.addToken(.comma, ","); } _ = try c.addToken(.r_brace, "}"); if (payload.inits.len < 2) { return c.addNode(.{ - .tag = .struct_init_one, + .tag = .struct_init_one_comma, .main_token = l_brace, .data = .{ .lhs = lhs, @@ -1681,7 +1683,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { } else { const span = try c.listToSpan(inits); return c.addNode(.{ - .tag = .struct_init, + .tag = .struct_init_comma, .main_token = l_brace, .data = .{ .lhs = lhs, @@ -1791,13 +1793,13 @@ fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex defer c.gpa.free(rendered); rendered[0] = 0; for (inits) |init, i| { - if (i != 0) _ = try c.addToken(.comma, ","); rendered[i] = try renderNode(c, init); + _ = try c.addToken(.comma, ","); } _ = try c.addToken(.r_brace, "}"); if (inits.len < 2) { return c.addNode(.{ - .tag = .array_init_one, + .tag = .array_init_one_comma, .main_token = l_brace, .data = .{ .lhs = lhs, @@ -1807,7 +1809,7 @@ fn renderArrayInit(c: *Context, lhs: NodeIndex, inits: []const Node) !NodeIndex } else { const span = try c.listToSpan(rendered); return c.addNode(.{ - .tag = .array_init, + .tag = .array_init_comma, .main_token = l_brace, .data = .{ .lhs = lhs, @@ -1842,25 +1844,32 @@ fn renderArrayType(c: *Context, len: usize, elem_type: Node) !NodeIndex { fn addSemicolonIfNeeded(c: *Context, node: Node) !void { switch (node.tag()) { .warning => unreachable, - .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .@"switch" => {}, + .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .block_single, .@"switch" => {}, .while_true => { const payload = node.castTag(.while_true).?.data; - return addSemicolonIfNeeded(c, payload); + return addSemicolonIfNotBlock(c, payload); }, .@"while" => { const payload = node.castTag(.@"while").?.data; - return addSemicolonIfNeeded(c, payload.body); + return addSemicolonIfNotBlock(c, payload.body); }, .@"if" => { const payload = node.castTag(.@"if").?.data; if (payload.@"else") |some| - return addSemicolonIfNeeded(c, some); - return addSemicolonIfNeeded(c, payload.then); + return addSemicolonIfNotBlock(c, some); + return addSemicolonIfNotBlock(c, payload.then); }, else => _ = try c.addToken(.semicolon, ";"), } } +fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { + switch (node.tag()) { + .block, .empty_block, .block_single, => {}, + else => _ = try c.addToken(.semicolon, ";"), + } +} + fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { switch (node.tag()) { .null_literal, @@ -1918,6 +1927,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .func, .call, .array_type, + .bool_to_int, => { // no grouping needed return renderNode(c, node); @@ -1926,7 +1936,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .opaque_literal, .empty_array, .block_single, - .bool_to_int, .add, .add_wrap, .sub, @@ -2022,7 +2031,7 @@ fn renderPrefixOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: T }); } -fn renderBinOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { +fn renderBinOpGrouped(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data; const lhs = try renderNodeGrouped(c, payload.lhs); return c.addNode(.{ @@ -2035,6 +2044,19 @@ fn renderBinOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: Toke }); } +fn renderBinOp(c: *Context, node: Node, tag: std.zig.ast.Node.Tag, tok_tag: TokenTag, bytes: []const u8) !NodeIndex { + const payload = @fieldParentPtr(Payload.BinOp, "base", node.ptr_otherwise).data; + const lhs = try renderNode(c, payload.lhs); + return c.addNode(.{ + .tag = tag, + .main_token = try c.addToken(tok_tag, bytes), + .data = .{ + .lhs = lhs, + .rhs = try renderNode(c, payload.rhs), + }, + }); +} + fn renderStdImport(c: *Context, first: []const u8, second: []const u8) !NodeIndex { const import_tok = try c.addToken(.builtin, "@import"); _ = try c.addToken(.l_paren, "("); @@ -2143,6 +2165,7 @@ fn renderVar(c: *Context, node: Node) !NodeIndex { if (payload.is_pub) _ = try c.addToken(.keyword_pub, "pub"); if (payload.is_extern) _ = try c.addToken(.keyword_extern, "extern"); if (payload.is_export) _ = try c.addToken(.keyword_export, "export"); + if (payload.is_threadlocal) _ = try c.addToken(.keyword_threadlocal, "threadlocal"); const mut_tok = if (payload.is_const) try c.addToken(.keyword_const, "const") else diff --git a/test/translate_c.zig b/test/translate_c.zig index 95969a2f72..6cbdbe931c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,14 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("use cast param as macro fn return type", + \\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED) + , &[_][]const u8{ + \\pub fn MEM_PHYSICAL_TO_K0(x: anytype) callconv(.Inline) ?*c_void { + \\ return @import("std").meta.cast(?*c_void, @import("std").meta.cast(u32, x) + SYS_BASE_CACHED); + \\} + }); + cases.add("variadic function demoted to prototype", \\int foo(int bar, ...) { \\ return 1; @@ -21,11 +29,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ Foo *bar; \\} Bar; , &[_][]const u8{ - \\const struct_unnamed_1 = // - , - \\warning: unsupported type: 'Atomic' - \\ opaque {}; // - , + \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo + \\const struct_unnamed_1 = opaque {}; \\pub const Foo = struct_unnamed_1; \\const struct_unnamed_2 = extern struct { \\ bar: ?*Foo, @@ -43,8 +48,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); , - \\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); + \\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); \\} }); @@ -107,7 +112,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ int i1; \\} boom_t; \\#define FOO ((boom_t){1}) - , &[_][]const u8{ // TODO properly translate this + , &[_][]const u8{ \\pub const struct_Color = extern struct { \\ r: u8, \\ g: u8, @@ -127,7 +132,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; \\pub const boom_t = struct_boom_t; , - \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{ 1 }); + \\pub const FOO = @import("std").mem.zeroInit(boom_t, .{1}); }); cases.add("complex switch", @@ -142,14 +147,34 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} , &[_][]const u8{ // TODO properly translate this - \\pub const main = @compileError("unable to translate function"); + \\pub export fn main() c_int { + \\ var i: c_int = 2; + \\ @"switch": { + \\ case_1: { + \\ case: { + \\ switch (i) { + \\ @as(c_int, 0) => break :case, + \\ @as(c_int, 2) => break :case_1, + \\ else => break :@"switch", + \\ } + \\ } + \\ } + \\ { + \\ { + \\ i += @as(c_int, 2); + \\ } + \\ i += @as(c_int, 1); + \\ } + \\ } + \\ return 0; + \\} }); cases.add("correct semicolon after infixop", \\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0) , &[_][]const u8{ - \\pub fn __ferror_unlocked_body(_fp: anytype) callconv(.Inline) @TypeOf(((_fp.*._flags) & _IO_ERR_SEEN) != 0) { - \\ return ((_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; \\} }); @@ -194,7 +219,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ while (false) while (false) {}; \\ while (true) while (false) {}; \\ while (true) while (true) { - \\ if (!false) break; + \\ break; \\ }; \\} }); @@ -245,15 +270,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ volatile _Atomic int abufused[12]; \\}; , &[_][]const u8{ - \\pub const struct_arcan_shmif_page = // - , - \\warning: unsupported type: 'Atomic' - \\ opaque {}; // - , - \\ warning: struct demoted to opaque type - unable to translate type of field abufused - , // TODO should be `addr: *struct_arcan_shmif_page` + \\source.h:4:8: warning: struct demoted to opaque type - unable to translate type of field abufused + \\pub const struct_arcan_shmif_page = opaque {}; \\pub const struct_arcan_shmif_cont = extern struct { - \\ addr: [*c]struct_arcan_shmif_page, + \\ addr: ?*struct_arcan_shmif_page, \\}; }); @@ -514,8 +534,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a: c_int = undefined; \\ _ = @as(c_int, 1); \\ _ = "hey"; - \\ _ = (@as(c_int, 1) + @as(c_int, 1)); - \\ _ = (@as(c_int, 1) - @as(c_int, 1)); + \\ _ = @as(c_int, 1) + @as(c_int, 1); + \\ _ = @as(c_int, 1) - @as(c_int, 1); \\ a = 1; \\} }); @@ -634,9 +654,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a: c_int = undefined; \\ var b: c_int = undefined; \\ var c: c_int = undefined; - \\ c = (a + b); - \\ c = (a - b); - \\ c = (a * b); + \\ c = a + b; + \\ c = a - b; + \\ c = a * b; \\ c = @divTrunc(a, b); \\ c = @rem(a, b); \\ return 0; @@ -645,11 +665,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a: c_uint = undefined; \\ var b: c_uint = undefined; \\ var c: c_uint = undefined; - \\ c = (a +% b); - \\ c = (a -% b); - \\ c = (a *% b); - \\ c = (a / b); - \\ c = (a % b); + \\ c = a +% b; + \\ c = a -% b; + \\ c = a *% b; + \\ c = a / b; + \\ c = a % b; \\ return 0; \\} }); @@ -1639,7 +1659,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { cases.add("macro pointer cast", \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , &[_][]const u8{ - \\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE)); + \\pub const NRF_GPIO = @import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE); }); cases.add("basic macro function", @@ -1723,17 +1743,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export fn foo() c_int { - \\ _ = (blk: { + \\ _ = blk: { \\ _ = @as(c_int, 2); \\ break :blk @as(c_int, 4); - \\ }); - \\ return (blk: { - \\ _ = (blk_1: { + \\ }; + \\ return blk: { + \\ _ = blk_1: { \\ _ = @as(c_int, 2); \\ break :blk_1 @as(c_int, 4); - \\ }); + \\ }; \\ break :blk @as(c_int, 6); - \\ }); + \\ }; \\} }); @@ -1780,20 +1800,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ while (true) { \\ var a_1: c_int = 4; \\ a_1 = 9; - \\ return (blk: { + \\ return blk: { \\ _ = @as(c_int, 6); \\ break :blk a_1; - \\ }); + \\ }; \\ } \\ while (true) { \\ var a_1: c_int = 2; \\ a_1 = 12; - \\ if (!true) break; - \\ } - \\ while (true) { - \\ a = 7; - \\ if (!true) break; \\ } + \\ while (true) a = 7; \\ return 0; \\} }); @@ -1813,13 +1829,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; - \\ _ = (blk: { - \\ _ = (blk_1: { + \\ _ = 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))); @@ -1854,7 +1870,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export fn bar() c_int { - \\ if ((if (true) @as(c_int, 5) else (if (true) @as(c_int, 4) else @as(c_int, 6))) != 0) _ = @as(c_int, 2); + \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2); \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6); \\} }); @@ -1894,7 +1910,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\ res = 2; \\ } - \\ res = (@as(c_int, 3) * i); + \\ res = @as(c_int, 3) * i; \\ break :@"switch"; \\ } \\ res = 5; @@ -2043,12 +2059,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn foo() void { \\ var a: c_int = 2; \\ while (true) { - \\ a = (a - @as(c_int, 1)); + \\ a = a - @as(c_int, 1); \\ if (!(a != 0)) break; \\ } \\ var b: c_int = 2; \\ while (true) { - \\ b = (b - @as(c_int, 1)); + \\ b = b - @as(c_int, 1); \\ if (!(b != 0)) break; \\ } \\} @@ -2078,6 +2094,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); \\} , &[_][]const u8{ + \\pub const FooA = @enumToInt(enum_Foo.A); + \\pub const FooB = @enumToInt(enum_Foo.B); + \\pub const FooC = @enumToInt(enum_Foo.C); \\pub const enum_Foo = extern enum(c_int) { \\ A, \\ B, @@ -2090,19 +2109,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var b = arg_b; \\ var c = arg_c; \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); - \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); - \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); - \\ var g: c_int = @boolToInt(((a != 0) and (c != null))); - \\ var h: c_int = @boolToInt(((a != 0) or (b != 0))); - \\ var i: c_int = @boolToInt(((b != 0) or (c != null))); - \\ var j: c_int = @boolToInt(((a != 0) or (c != null))); - \\ var k: c_int = @boolToInt(((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0))); - \\ var l: c_int = @boolToInt(((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0))); - \\ var m: c_int = @boolToInt(((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0))); + \\ var e: c_int = @boolToInt((a != 0) and (b != 0)); + \\ var f: c_int = @boolToInt((b != 0) and (c != null)); + \\ var g: c_int = @boolToInt((a != 0) and (c != null)); + \\ var h: c_int = @boolToInt((a != 0) or (b != 0)); + \\ var i: c_int = @boolToInt((b != 0) or (c != null)); + \\ var j: c_int = @boolToInt((a != 0) or (c != null)); + \\ var k: c_int = @boolToInt((a != 0) or (@bitCast(c_int, @enumToInt(d)) != 0)); + \\ var l: c_int = @boolToInt((@bitCast(c_int, @enumToInt(d)) != 0) and (b != 0)); + \\ var m: c_int = @boolToInt((c != null) or (@bitCast(c_uint, @enumToInt(d)) != 0)); \\ var td: SomeTypedef = 44; - \\ var o: c_int = @boolToInt(((td != 0) or (b != 0))); - \\ var p: c_int = @boolToInt(((c != null) and (td != 0))); - \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); + \\ var o: c_int = @boolToInt((td != 0) or (b != 0)); + \\ var p: c_int = @boolToInt((c != null) and (td != 0)); + \\ return (((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p; \\} , \\pub const Foo = enum_Foo; @@ -2143,7 +2162,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn max(arg_a: c_int, arg_b: c_int) c_int { \\ var a = arg_a; \\ var b = arg_b; - \\ return ((a & b) ^ (a | b)); + \\ return (a & b) ^ (a | b); \\} }); @@ -2162,13 +2181,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int { \\ var a = arg_a; \\ var b = arg_b; - \\ var c: c_int = @boolToInt((a < b)); - \\ var d: c_int = @boolToInt((a > b)); - \\ var e: c_int = @boolToInt((a <= b)); - \\ var f: c_int = @boolToInt((a >= b)); - \\ var g: c_int = @boolToInt((c < d)); - \\ var h: c_int = @boolToInt((e < f)); - \\ var i: c_int = @boolToInt((g < h)); + \\ var c: c_int = @boolToInt(a < b); + \\ var d: c_int = @boolToInt(a > b); + \\ var e: c_int = @boolToInt(a <= b); + \\ var f: c_int = @boolToInt(a >= b); + \\ var g: c_int = @boolToInt(c < d); + \\ var h: c_int = @boolToInt(e < f); + \\ var i: c_int = @boolToInt(g < h); \\ return i; \\} }); @@ -2215,11 +2234,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export fn foo() c_int { - \\ return (blk: { + \\ return blk: { \\ var a: c_int = 1; \\ _ = a; \\ break :blk a; - \\ }); + \\ }; \\} }); @@ -2371,9 +2390,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a: c_int = 2; \\ } \\ if ((blk: { - \\ _ = @as(c_int, 2); - \\ break :blk @as(c_int, 5); - \\ }) != 0) { + \\ _ = @as(c_int, 2); + \\ break :blk @as(c_int, 5); + \\ }) != 0) { \\ var a: c_int = 2; \\ } \\} @@ -2484,10 +2503,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var f: ?fn () callconv(.C) void = foo; \\ var b: ?fn () callconv(.C) c_int = baz; \\ f.?(); - \\ (f).?(); + \\ f.?(); \\ foo(); \\ _ = b.?(); - \\ _ = (b).?(); + \\ _ = b.?(); \\ _ = baz(); \\} }); @@ -2513,26 +2532,26 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ i -= 1; \\ u +%= 1; \\ u -%= 1; - \\ i = (blk: { + \\ i = blk: { \\ const ref = &i; \\ ref.* += 1; \\ break :blk ref.*; - \\ }); - \\ i = (blk: { + \\ }; + \\ i = blk: { \\ const ref = &i; \\ ref.* -= 1; \\ break :blk ref.*; - \\ }); - \\ u = (blk: { + \\ }; + \\ u = blk: { \\ const ref = &u; \\ ref.* +%= 1; \\ break :blk ref.*; - \\ }); - \\ u = (blk: { + \\ }; + \\ u = blk: { \\ const ref = &u; \\ ref.* -%= 1; \\ break :blk ref.*; - \\ }); + \\ }; \\} }); @@ -2596,66 +2615,66 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn foo() void { \\ var a: c_int = 0; \\ var b: c_uint = @bitCast(c_uint, @as(c_int, 0)); - \\ a += (blk: { + \\ a += blk: { \\ const ref = &a; - \\ ref.* = ref.* + @as(c_int, 1); + \\ ref.* += @as(c_int, 1); + \\ break :blk ref.*; + \\ }; + \\ a -= blk: { + \\ const ref = &a; + \\ ref.* -= @as(c_int, 1); + \\ break :blk ref.*; + \\ }; + \\ a *= blk: { + \\ const ref = &a; + \\ ref.* *= @as(c_int, 1); + \\ break :blk ref.*; + \\ }; + \\ a &= blk: { + \\ const ref = &a; + \\ ref.* &= @as(c_int, 1); + \\ break :blk ref.*; + \\ }; + \\ a |= blk: { + \\ const ref = &a; + \\ ref.* |= @as(c_int, 1); + \\ break :blk ref.*; + \\ }; + \\ a ^= blk: { + \\ const ref = &a; + \\ ref.* ^= @as(c_int, 1); + \\ break :blk ref.*; + \\ }; + \\ a >>= @intCast(@import("std").math.Log2Int(c_int), blk: { + \\ const ref = &a; + \\ ref.* >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); \\ break :blk ref.*; \\ }); - \\ a -= (blk: { + \\ a <<= @intCast(@import("std").math.Log2Int(c_int), blk: { \\ const ref = &a; - \\ ref.* = ref.* - @as(c_int, 1); + \\ ref.* <<= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); \\ break :blk ref.*; \\ }); - \\ a *= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* * @as(c_int, 1); - \\ break :blk ref.*; - \\ }); - \\ a &= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* & @as(c_int, 1); - \\ break :blk ref.*; - \\ }); - \\ a |= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* | @as(c_int, 1); - \\ break :blk ref.*; - \\ }); - \\ a ^= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* ^ @as(c_int, 1); - \\ break :blk ref.*; - \\ }); - \\ a >>= @intCast(@import("std").math.Log2Int(c_int), (blk: { - \\ const ref = &a; - \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); - \\ break :blk ref.*; - \\ })); - \\ a <<= @intCast(@import("std").math.Log2Int(c_int), (blk: { - \\ const ref = &a; - \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); - \\ break :blk ref.*; - \\ })); - \\ a = @divTrunc(a, (blk: { + \\ a = @divTrunc(a, blk: { \\ const ref = &a; \\ ref.* = @divTrunc(ref.*, @as(c_int, 1)); \\ break :blk ref.*; - \\ })); - \\ a = @rem(a, (blk: { + \\ }); + \\ a = @rem(a, blk: { \\ const ref = &a; \\ ref.* = @rem(ref.*, @as(c_int, 1)); \\ break :blk ref.*; - \\ })); - \\ b /= (blk: { - \\ const ref = &b; - \\ ref.* = ref.* / @bitCast(c_uint, @as(c_int, 1)); - \\ break :blk ref.*; \\ }); - \\ b %= (blk: { + \\ b /= blk: { \\ const ref = &b; - \\ ref.* = ref.* % @bitCast(c_uint, @as(c_int, 1)); + \\ ref.* /= @bitCast(c_uint, @as(c_int, 1)); \\ break :blk ref.*; - \\ }); + \\ }; + \\ b %= blk: { + \\ const ref = &b; + \\ ref.* %= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; \\} }); @@ -2674,46 +2693,46 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0)); - \\ a +%= (blk: { + \\ a +%= blk: { \\ const ref = &a; - \\ ref.* = ref.* +% @bitCast(c_uint, @as(c_int, 1)); + \\ ref.* +%= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; + \\ a -%= blk: { + \\ const ref = &a; + \\ ref.* -%= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; + \\ a *%= blk: { + \\ const ref = &a; + \\ ref.* *%= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; + \\ a &= blk: { + \\ const ref = &a; + \\ ref.* &= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; + \\ a |= blk: { + \\ const ref = &a; + \\ ref.* |= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; + \\ a ^= blk: { + \\ const ref = &a; + \\ ref.* ^= @bitCast(c_uint, @as(c_int, 1)); + \\ break :blk ref.*; + \\ }; + \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), blk: { + \\ const ref = &a; + \\ ref.* >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); \\ break :blk ref.*; \\ }); - \\ a -%= (blk: { + \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), blk: { \\ const ref = &a; - \\ ref.* = ref.* -% @bitCast(c_uint, @as(c_int, 1)); + \\ ref.* <<= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); \\ break :blk ref.*; \\ }); - \\ a *%= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* *% @bitCast(c_uint, @as(c_int, 1)); - \\ break :blk ref.*; - \\ }); - \\ a &= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* & @bitCast(c_uint, @as(c_int, 1)); - \\ break :blk ref.*; - \\ }); - \\ a |= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* | @bitCast(c_uint, @as(c_int, 1)); - \\ break :blk ref.*; - \\ }); - \\ a ^= (blk: { - \\ const ref = &a; - \\ ref.* = ref.* ^ @bitCast(c_uint, @as(c_int, 1)); - \\ break :blk ref.*; - \\ }); - \\ a >>= @intCast(@import("std").math.Log2Int(c_uint), (blk: { - \\ const ref = &a; - \\ ref.* = ref.* >> @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); - \\ break :blk ref.*; - \\ })); - \\ a <<= @intCast(@import("std").math.Log2Int(c_uint), (blk: { - \\ const ref = &a; - \\ ref.* = ref.* << @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1)); - \\ break :blk ref.*; - \\ })); \\} }); @@ -2738,30 +2757,30 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ i -= 1; \\ u +%= 1; \\ u -%= 1; - \\ i = (blk: { + \\ i = blk: { \\ const ref = &i; \\ const tmp = ref.*; \\ ref.* += 1; \\ break :blk tmp; - \\ }); - \\ i = (blk: { + \\ }; + \\ i = blk: { \\ const ref = &i; \\ const tmp = ref.*; \\ ref.* -= 1; \\ break :blk tmp; - \\ }); - \\ u = (blk: { + \\ }; + \\ u = blk: { \\ const ref = &u; \\ const tmp = ref.*; \\ ref.* +%= 1; \\ break :blk tmp; - \\ }); - \\ u = (blk: { + \\ }; + \\ u = blk: { \\ const ref = &u; \\ const tmp = ref.*; \\ ref.* -%= 1; \\ break :blk tmp; - \\ }); + \\ }; \\} }); @@ -2872,13 +2891,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define BAR (void*) a \\#define BAZ (uint32_t)(2) , &[_][]const u8{ - \\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))); + \\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)); \\} , - \\pub const BAR = (@import("std").meta.cast(?*c_void, a)); + \\pub const BAR = @import("std").meta.cast(?*c_void, a); , - \\pub const BAZ = (@import("std").meta.cast(u32, 2)); + \\pub const BAZ = @import("std").meta.cast(u32, 2); }); cases.add("macro with cast to unsigned short, long, and long long", @@ -2886,9 +2905,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define CURLAUTH_BASIC ((unsigned long) 1) \\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1) , &[_][]const u8{ - \\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1)); - \\pub const CURLAUTH_BASIC = (@import("std").meta.cast(c_ulong, 1)); - \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = (@import("std").meta.cast(c_ulonglong, 1)); + \\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").meta.cast(c_ushort, 1); + \\pub const CURLAUTH_BASIC = @import("std").meta.cast(c_ulong, 1); + \\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").meta.cast(c_ulonglong, 1); }); cases.add("macro conditional operator", @@ -2905,7 +2924,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub fn foo() callconv(.C) void { \\ if (true) while (true) { - \\ if (!false) break; + \\ break; \\ }; \\} }); @@ -2923,27 +2942,27 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - // TODO: detect to use different block labels here - cases.add("nested assignment", - \\int foo(int *p, int x) { - \\ return *p++ = x; - \\} - , &[_][]const u8{ - \\pub export fn foo(arg_p: [*c]c_int, arg_x: c_int) c_int { - \\ var p = arg_p; - \\ var x = arg_x; - \\ return blk: { - \\ const tmp = x; - \\ (blk_1: { - \\ const ref = &p; - \\ const tmp_2 = ref.*; - \\ ref.* += 1; - \\ break :blk_1 tmp_2; - \\ }).?.* = tmp; - \\ break :blk tmp; - \\ }; - \\} - }); + // TODO fix zig fmt here + // cases.add("nested assignment", + // \\int foo(int *p, int x) { + // \\ return *p++ = x; + // \\} + // , &[_][]const u8{ + // \\pub export fn foo(arg_p: [*c]c_int, arg_x: c_int) c_int { + // \\ var p = arg_p; + // \\ var x = arg_x; + // \\ return blk: { + // \\ const tmp = x; + // \\ (blk_1: { + // \\ const ref = &p; + // \\ const tmp_2 = ref.*; + // \\ ref.* += 1; + // \\ break :blk_1 tmp_2; + // \\ }).?.* = tmp; + // \\ break :blk tmp; + // \\ }; + // \\} + // }); cases.add("widening and truncating integer casting to different signedness", \\unsigned long foo(void) { @@ -3033,10 +3052,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo(arg_x: bool) bool { \\ var x = arg_x; - \\ var a: bool = (@as(c_int, @boolToInt(x)) != @as(c_int, 1)); - \\ var b: bool = (@as(c_int, @boolToInt(a)) != @as(c_int, 0)); + \\ var a: bool = @as(c_int, @boolToInt(x)) != @as(c_int, 1); + \\ var b: bool = @as(c_int, @boolToInt(a)) != @as(c_int, 0); \\ var c: bool = @ptrToInt(foo) != 0; - \\ return foo((@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b)))); + \\ return foo(@as(c_int, @boolToInt(c)) != @as(c_int, @boolToInt(b))); \\} }); @@ -3106,8 +3125,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen) \\ , &[_][]const u8{ - \\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; + \\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; \\} }); @@ -3115,9 +3134,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\#define NULL ((void*)0) \\#define FOO ((int)0x8000) , &[_][]const u8{ - \\pub const NULL = (@import("std").meta.cast(?*c_void, 0)); + \\pub const NULL = @import("std").meta.cast(?*c_void, 0); , - \\pub const FOO = (@import("std").meta.cast(c_int, 0x8000)); + \\pub const FOO = @import("std").meta.cast(c_int, 0x8000); }); if (std.Target.current.abi == .msvc) { From 9a826ccbe0b10046e5dd7c482e9e87912c0fa95c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 16 Feb 2021 16:34:37 +0200 Subject: [PATCH 29/36] translate-c: elide some unecessary casts of literals --- src/clang.zig | 5 ++- src/translate_c.zig | 52 +++++++++++++++++++++--- src/zig_clang.cpp | 5 +++ src/zig_clang.h | 1 + test/translate_c.zig | 95 ++++++++++++++++++++++---------------------- 5 files changed, 104 insertions(+), 54 deletions(-) diff --git a/src/clang.zig b/src/clang.zig index 954cfee6b2..fbb955205b 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -127,6 +127,9 @@ pub const APSInt = opaque { pub const getNumWords = ZigClangAPSInt_getNumWords; extern fn ZigClangAPSInt_getNumWords(*const APSInt) c_uint; + + pub const lessThanEqual = ZigClangAPSInt_lessThanEqual; + extern fn ZigClangAPSInt_lessThanEqual(*const APSInt, rhs: u64) bool; }; pub const ASTContext = opaque { @@ -407,7 +410,7 @@ pub const Expr = opaque { pub const getBeginLoc = ZigClangExpr_getBeginLoc; extern fn ZigClangExpr_getBeginLoc(*const Expr) SourceLocation; - pub const EvaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr; + pub const evaluateAsConstantExpr = ZigClangExpr_EvaluateAsConstantExpr; extern fn ZigClangExpr_EvaluateAsConstantExpr(*const Expr, *ExprEvalResult, Expr_ConstExprUsage, *const ASTContext) bool; }; diff --git a/src/translate_c.zig b/src/translate_c.zig index fbd0ec6245..1fcac233fc 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1820,11 +1820,55 @@ fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: return transExprCoercing(c, scope, un_expr.getSubExpr(), used); } }, + .ImplicitCastExprClass => { + const cast_expr = @ptrCast(*const clang.ImplicitCastExpr, expr); + const sub_expr = cast_expr.getSubExpr(); + switch (@ptrCast(*const clang.Stmt, sub_expr).getStmtClass()) { + .IntegerLiteralClass, .CharacterLiteralClass => switch (cast_expr.getCastKind()) { + .IntegralToFloating => return transExprCoercing(c, scope, sub_expr, used), + .IntegralCast => { + const dest_type = getExprQualType(c, expr); + if (literalFitsInType(c, sub_expr, dest_type)) + return transExprCoercing(c, scope, sub_expr, used); + }, + else => {}, + }, + else => {}, + } + }, else => {}, } return transExpr(c, scope, expr, .used); } +fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) bool { + var width = qualTypeIntBitWidth(c, qt) catch 8; + if (width == 0) width = 8; // Byte is the smallest type. + const is_signed = cIsSignedInteger(qt); + const width_max_int= (@as(u64, 1) << math.lossyCast(u6, width - @boolToInt(is_signed))) - 1; + + switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { + .CharacterLiteralClass => { + const char_lit = @ptrCast(*const clang.CharacterLiteral, expr); + const val = char_lit.getValue(); + // If the val is less than the max int then it fits. + return val <= width_max_int; + }, + .IntegerLiteralClass => { + const int_lit = @ptrCast(*const clang.IntegerLiteral, expr); + var eval_result: clang.ExprEvalResult = undefined; + if (!int_lit.EvaluateAsInt(&eval_result, c.clang_context)) { + return false; + } + + const int = eval_result.Val.getInt(); + return int.lessThanEqual(width_max_int); + }, + else => unreachable, + } + +} + fn transInitListExprRecord( c: *Context, scope: *Scope, @@ -2331,7 +2375,7 @@ fn transDefault( fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { var result: clang.ExprEvalResult = undefined; - if (!expr.EvaluateAsConstantExpr(&result, .EvaluateForCodeGen, c.clang_context)) + if (!expr.evaluateAsConstantExpr(&result, .EvaluateForCodeGen, c.clang_context)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{}); switch (result.Val.getKind()) { @@ -3171,7 +3215,7 @@ fn qualTypeIsBoolean(qt: clang.QualType) bool { return qualTypeCanon(qt).isBooleanType(); } -fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !u32 { +fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 { const ty = qt.getTypePtr(); switch (ty.getTypeClass()) { @@ -3211,12 +3255,10 @@ fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType, source_loc: clang.Source }, else => return 0, } - - unreachable; } fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !Node { - const int_bit_width = try qualTypeIntBitWidth(c, qt, source_loc); + const int_bit_width = try qualTypeIntBitWidth(c, qt); if (int_bit_width != 0) { // we can perform the log2 now. diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 8dc6a0823b..d9e5e527ac 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -2244,6 +2244,11 @@ unsigned ZigClangAPSInt_getNumWords(const ZigClangAPSInt *self) { return casted->getNumWords(); } +bool ZigClangAPSInt_lessThanEqual(const ZigClangAPSInt *self, uint64_t rhs) { + auto casted = reinterpret_cast(self); + return casted->ule(rhs); +} + uint64_t ZigClangAPInt_getLimitedValue(const ZigClangAPInt *self, uint64_t limit) { auto casted = reinterpret_cast(self); return casted->getLimitedValue(limit); diff --git a/src/zig_clang.h b/src/zig_clang.h index 6fe1da0bc1..a697c58b4f 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -1097,6 +1097,7 @@ ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPSInt_negate(const struct Zig ZIG_EXTERN_C void ZigClangAPSInt_free(const struct ZigClangAPSInt *self); ZIG_EXTERN_C const uint64_t *ZigClangAPSInt_getRawData(const struct ZigClangAPSInt *self); ZIG_EXTERN_C unsigned ZigClangAPSInt_getNumWords(const struct ZigClangAPSInt *self); +ZIG_EXTERN_C bool ZigClangAPSInt_lessThanEqual(const struct ZigClangAPSInt *self, uint64_t rhs); ZIG_EXTERN_C uint64_t ZigClangAPInt_getLimitedValue(const struct ZigClangAPInt *self, uint64_t limit); diff --git a/test/translate_c.zig b/test/translate_c.zig index 6cbdbe931c..dda0e45144 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -313,22 +313,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub const uuid_t = [16]u8; \\pub const UUID_NULL: uuid_t = [16]u8{ - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 0))), + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\ 0, \\}; }); @@ -382,10 +382,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; \\pub export var ub: union_unnamed_1 = union_unnamed_1{ \\ .c = [4]u8{ - \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 'b'))), - \\ @bitCast(u8, @truncate(i8, @as(c_int, 'a'))), + \\ 'a', + \\ 'b', + \\ 'b', + \\ 'a', \\ }, \\}; }); @@ -512,7 +512,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var a: c_int = undefined; - \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123))); + \\ var b: u8 = 123; \\ const c: c_int = undefined; \\ const d: c_uint = @bitCast(c_uint, @as(c_int, 440)); \\ var e: c_int = 10; @@ -1468,7 +1468,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub fn foo() callconv(.C) void { \\ var arr: [10]u8 = [1]u8{ - \\ @bitCast(u8, @truncate(i8, @as(c_int, 1))), + \\ 1, \\ } ++ [1]u8{0} ** 9; \\ var arr1: [10][*c]u8 = [1][*c]u8{ \\ null, @@ -1721,13 +1721,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ unsigned d = 440; \\} , &[_][]const u8{ - \\pub var a: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2))); - \\pub var b: c_long = @bitCast(c_long, @as(c_long, @as(c_int, 2))); + \\pub var a: c_long = 2; + \\pub var b: c_long = 2; \\pub var c: c_int = 4; \\pub export fn foo(arg_c_1: u8) void { \\ var c_1 = arg_c_1; \\ var a_2: c_int = undefined; - \\ var b_3: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 123))); + \\ var b_3: u8 = 123; \\ b_3 = @bitCast(u8, @truncate(i8, a_2)); \\ { \\ var d: c_int = 5; @@ -1838,7 +1838,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ }; \\ } \\ } - \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, 2))); + \\ var i: u8 = 2; \\} }); @@ -1846,7 +1846,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\unsigned anyerror = 2; \\#define noreturn _Noreturn , &[_][]const u8{ - \\pub export var anyerror_1: c_uint = @bitCast(c_uint, @as(c_int, 2)); + \\pub export var anyerror_1: c_uint = 2; , \\pub const noreturn_2 = @compileError("unable to translate C expr: unexpected token .Keyword_noreturn"); }); @@ -1860,7 +1860,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export var a: f32 = @floatCast(f32, 3.1415); \\pub export var b: f64 = 3.1415; \\pub export var c: c_int = @floatToInt(c_int, 3.1415); - \\pub export var d: f64 = @intToFloat(f64, @as(c_int, 3)); + \\pub export var d: f64 = 3; }); cases.add("conditional operator", @@ -2009,7 +2009,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - // TODO translate-c should in theory be able to figure out to drop all these casts cases.add("escape sequences", \\const char *escapes() { \\char a = '\'', @@ -2028,17 +2027,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ , &[_][]const u8{ \\pub export fn escapes() [*c]const u8 { - \\ var a: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\''))); - \\ var b: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\\'))); - \\ var c: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x07'))); - \\ var d: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x08'))); - \\ var e: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0c'))); - \\ var f: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\n'))); - \\ var g: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\r'))); - \\ var h: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\t'))); - \\ var i: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x0b'))); - \\ var j: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\x00'))); - \\ var k: u8 = @bitCast(u8, @truncate(i8, @as(c_int, '\"'))); + \\ var a: u8 = '\''; + \\ var b: u8 = '\\'; + \\ var c: u8 = '\x07'; + \\ var d: u8 = '\x08'; + \\ var e: u8 = '\x0c'; + \\ var f: u8 = '\n'; + \\ var g: u8 = '\r'; + \\ var h: u8 = '\t'; + \\ var i: u8 = '\x0b'; + \\ var j: u8 = '\x00'; + \\ var k: u8 = '\"'; \\ return "\'\\\x07\x08\x0c\n\r\t\x0b\x00\""; \\} }); @@ -2308,8 +2307,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var a: [10]c_longlong = undefined; - \\ var i: c_longlong = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0))); - \\ a[@intCast(usize, i)] = @bitCast(c_longlong, @as(c_longlong, @as(c_int, 0))); + \\ var i: c_longlong = 0; + \\ a[@intCast(usize, i)] = 0; \\} }); @@ -2321,8 +2320,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var a: [10]c_uint = undefined; - \\ var i: c_uint = @bitCast(c_uint, @as(c_int, 0)); - \\ a[i] = @bitCast(c_uint, @as(c_int, 0)); + \\ var i: c_uint = 0; + \\ a[i] = 0; \\} }); @@ -2527,7 +2526,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var i: c_int = 0; - \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ var u: c_uint = 0; \\ i += 1; \\ i -= 1; \\ u +%= 1; @@ -2614,7 +2613,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var a: c_int = 0; - \\ var b: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ var b: c_uint = 0; \\ a += blk: { \\ const ref = &a; \\ ref.* += @as(c_int, 1); @@ -2692,7 +2691,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export fn foo() void { - \\ var a: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ var a: c_uint = 0; \\ a +%= blk: { \\ const ref = &a; \\ ref.* +%= @bitCast(c_uint, @as(c_int, 1)); @@ -2752,7 +2751,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub export fn foo() void { \\ var i: c_int = 0; - \\ var u: c_uint = @bitCast(c_uint, @as(c_int, 0)); + \\ var u: c_uint = 0; \\ i += 1; \\ i -= 1; \\ u +%= 1; From e2974759dd62e15f04e1aeb8babee65e6ffb3413 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Feb 2021 14:11:49 +0200 Subject: [PATCH 30/36] translate-c: demote untranslatable declarations to externs --- src/translate_c.zig | 38 +++++++++++++++++++++++++++----------- src/translate_c/ast.zig | 6 ++++++ test/translate_c.zig | 4 ++-- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 1fcac233fc..0ab72d7734 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -508,7 +508,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { decl_ctx.has_body = false; decl_ctx.storage_class = .Extern; decl_ctx.is_export = false; - try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{}); + try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to extern", .{}); } break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) { error.UnsupportedType => { @@ -543,8 +543,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { var param_id: c_uint = 0; for (proto_node.data.params) |*param, i| { - const param_name = param.name orelse - return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name}); + const param_name = param.name orelse { + proto_node.data.is_extern = true; + proto_node.data.is_export = false; + try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name}); + return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); + }; const c_param = fn_decl.getParamDecl(param_id); const qual_type = c_param.getOriginalType(); @@ -570,7 +574,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, - => return failDecl(c, fn_decl_loc, fn_name, "unable to translate function", .{}), + => { + proto_node.data.is_extern = true; + proto_node.data.is_export = false; + try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{}); + return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); + }, }; // add return statement if the function didn't have one blk: { @@ -598,7 +607,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, - => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}), + => { + proto_node.data.is_extern = true; + proto_node.data.is_export = false; + try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{}); + return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); + }, }; const ret = try Tag.@"return".create(c.arena, rhs); try block_scope.statements.append(ret); @@ -641,8 +655,8 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co // does the same as: // extern int foo; // int foo = 2; - const is_extern = storage_class == .Extern and !has_init; - const is_export = !is_extern and storage_class != .Static; + var is_extern = storage_class == .Extern and !has_init; + var is_export = !is_extern and storage_class != .Static; const type_node = transQualTypeMaybeInitialized(c, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType => { @@ -656,7 +670,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co // If the initialization expression is not present, initialize with undefined. // If it is an integer literal, we can skip the @as since it will be redundant // with the variable type. - if (has_init) { + if (has_init) trans_init: { if (decl_init) |expr| { const node_or_error = if (expr.getStmtClass() == .StringLiteralClass) transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0) @@ -666,7 +680,10 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co error.UnsupportedTranslation, error.UnsupportedType, => { - return failDecl(c, var_decl_loc, checked_name, "unable to translate initializer", .{}); + is_extern = true; + is_export = false; + try warn(c, scope, var_decl_loc, "unable to translate variable initializer, demoted to extern", .{}); + break :trans_init; }, error.OutOfMemory => |e| return e, }; @@ -1845,7 +1862,7 @@ fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) b var width = qualTypeIntBitWidth(c, qt) catch 8; if (width == 0) width = 8; // Byte is the smallest type. const is_signed = cIsSignedInteger(qt); - const width_max_int= (@as(u64, 1) << math.lossyCast(u6, width - @boolToInt(is_signed))) - 1; + const width_max_int = (@as(u64, 1) << math.lossyCast(u6, width - @boolToInt(is_signed))) - 1; switch (@ptrCast(*const clang.Stmt, expr).getStmtClass()) { .CharacterLiteralClass => { @@ -1866,7 +1883,6 @@ fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) b }, else => unreachable, } - } fn transInitListExprRecord( diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 8f3d605d8e..6d22c7a270 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1866,6 +1866,12 @@ fn addSemicolonIfNeeded(c: *Context, node: Node) !void { fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { switch (node.tag()) { .block, .empty_block, .block_single, => {}, + .@"if" => { + const payload = node.castTag(.@"if").?.data; + if (payload.@"else") |some| + return addSemicolonIfNotBlock(c, some); + return addSemicolonIfNotBlock(c, payload.then); + }, else => _ = try c.addToken(.semicolon, ";"), } } diff --git a/test/translate_c.zig b/test/translate_c.zig index dda0e45144..79361bf9bf 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -11,12 +11,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("variadic function demoted to prototype", + cases.add("variadic function demoted to extern", \\int foo(int bar, ...) { \\ return 1; \\} , &[_][]const u8{ - \\warning: TODO unable to translate variadic function, demoted to declaration + \\warning: TODO unable to translate variadic function, demoted to extern \\pub extern fn foo(bar: c_int, ...) c_int; }); From d5fecbd0bacd986fc02c8a98aea07ac42303f0ce Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Feb 2021 16:26:11 +0200 Subject: [PATCH 31/36] translate-c: support scoped typedef, enum and record decls Closes #5256 --- src/translate_c.zig | 290 +++++++++++++++++++++------------------- src/translate_c/ast.zig | 15 ++- test/translate_c.zig | 182 +++++++++++++++++++++---- 3 files changed, 318 insertions(+), 169 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 0ab72d7734..5ac60bffae 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -433,13 +433,13 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { return visitFnDecl(c, @ptrCast(*const clang.FunctionDecl, decl)); }, .Typedef => { - _ = try transTypeDef(c, @ptrCast(*const clang.TypedefNameDecl, decl), true); + try transTypeDef(c, &c.global_scope.base, @ptrCast(*const clang.TypedefNameDecl, decl)); }, .Enum => { - _ = try transEnumDecl(c, @ptrCast(*const clang.EnumDecl, decl)); + try transEnumDecl(c, &c.global_scope.base, @ptrCast(*const clang.EnumDecl, decl)); }, .Record => { - _ = try transRecordDecl(c, @ptrCast(*const clang.RecordDecl, decl)); + try transRecordDecl(c, &c.global_scope.base, @ptrCast(*const clang.RecordDecl, decl)); }, .Var => { return visitVarDecl(c, @ptrCast(*const clang.VarDecl, decl), null); @@ -622,11 +622,11 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); } -fn transQualTypeMaybeInitialized(c: *Context, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node { +fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node { return if (decl_init) |init_expr| - transQualTypeInitialized(c, qt, init_expr, loc) + transQualTypeInitialized(c, scope, qt, init_expr, loc) else - transQualType(c, qt, loc); + transQualType(c, scope, qt, loc); } /// if mangled_name is not null, this var decl was declared in a block scope. @@ -658,7 +658,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co var is_extern = storage_class == .Extern and !has_init; var is_export = !is_extern and storage_class != .Static; - const type_node = transQualTypeMaybeInitialized(c, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { + const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) { error.UnsupportedTranslation, error.UnsupportedType => { return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{}); }, @@ -733,11 +733,6 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co return addTopLevelDecl(c, checked_name, node); } -fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !Node { - _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name); - return Tag.identifier.create(c.arena, builtin_name); -} - const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ .{ "uint8_t", "u8" }, .{ "int8_t", "i8" }, @@ -753,42 +748,28 @@ const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{ .{ "size_t", "usize" }, }); -fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?Node { +fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNameDecl) Error!void { if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name| - return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice + return; // Avoid processing this decl twice + const toplevel = scope.id == .root; + const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; - const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); + const bare_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name; - if (builtin_typedef_map.get(checked_name)) |builtin| { - _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin); - return try Tag.identifier.create(c.arena, builtin); + var name: []const u8 = if (isZigPrimitiveType(bare_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ bare_name, c.getMangle() }) else bare_name; + if (builtin_typedef_map.get(name)) |builtin| { + return c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin); } + if (!toplevel) name = try bs.makeMangledName(c, name); + try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name); - if (!top_level_visit) { - return try Tag.identifier.create(c.arena, checked_name); - } - - _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name); - const node = (try transCreateNodeTypedef(c, typedef_decl, true, checked_name)) orelse return null; - try addTopLevelDecl(c, checked_name, node); - return try Tag.identifier.create(c.arena, checked_name); -} - -fn transCreateNodeTypedef( - c: *Context, - typedef_decl: *const clang.TypedefNameDecl, - toplevel: bool, - checked_name: []const u8, -) Error!?Node { const child_qt = typedef_decl.getUnderlyingType(); const typedef_loc = typedef_decl.getLocation(); - const init_node = transQualType(c, child_qt, typedef_loc) catch |err| switch (err) { + const init_node = transQualType(c, scope, child_qt, typedef_loc) catch |err| switch (err) { error.UnsupportedType => { - try failDecl(c, typedef_loc, checked_name, "unable to resolve typedef child type", .{}); - return null; + return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{}); }, error.OutOfMemory => |e| return e, }; @@ -797,17 +778,25 @@ fn transCreateNodeTypedef( payload.* = .{ .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@boolToInt(toplevel)] }, .data = .{ - .name = checked_name, + .name = name, .init = init_node, }, }; - return Node.initPayload(&payload.base); + const node = Node.initPayload(&payload.base); + + if (toplevel) { + try addTopLevelDecl(c, name, node); + } else { + try scope.appendNode(node); + } } -fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Node { +fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void { if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name| - return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice + return; // Avoid processing this decl twice const record_loc = record_decl.getLocation(); + const toplevel = scope.id == .root; + const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()); var is_unnamed = false; @@ -826,14 +815,15 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod } else if (record_decl.isStruct()) { container_kind_name = "struct"; } else { - try warn(c, &c.global_scope.base, record_loc, "record {s} is not a struct or union", .{bare_name}); - return null; + try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), bare_name); + return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name}); } - const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); - _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); + var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); + if (!toplevel) name = try bs.makeMangledName(c, name); + try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); - const is_pub = !is_unnamed; + const is_pub = toplevel and !is_unnamed; const init_node = blk: { const record_def = record_decl.getDefinition() orelse { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); @@ -854,13 +844,13 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod if (field_decl.isBitField()) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, &c.global_scope.base, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); + try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); break :blk Tag.opaque_literal.init(); } if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, &c.global_scope.base, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); + try warn(c, scope, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); break :blk Tag.opaque_literal.init(); } @@ -872,10 +862,10 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod unnamed_field_count += 1; is_anon = true; } - const field_type = transQualType(c, field_qt, field_loc) catch |err| switch (err) { + const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) { error.UnsupportedType => { _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); - try warn(c, &c.global_scope.base, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name }); + try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name }); break :blk Tag.opaque_literal.init(); }, else => |e| return e, @@ -891,7 +881,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod }; if (is_anon) { - _ = try c.decl_table.put(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); + try c.decl_table.putNoClobber(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name); } try fields.append(.{ @@ -921,16 +911,21 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Nod }, }; - try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); - if (!is_unnamed) - try c.alias_list.append(.{ .alias = bare_name, .name = name }); - return try Tag.identifier.create(c.arena, name); + if (toplevel) { + try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); + if (!is_unnamed) + try c.alias_list.append(.{ .alias = bare_name, .name = name }); + } else { + try scope.appendNode(Node.initPayload(&payload.base)); + } } -fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { +fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) Error!void { if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name| - return try Tag.identifier.create(c.arena, name); // Avoid processing this decl twice + return; // Avoid processing this decl twice const enum_loc = enum_decl.getLocation(); + const toplevel = scope.id == .root; + const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined; var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); var is_unnamed = false; @@ -939,10 +934,13 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { is_unnamed = true; } - const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); - _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); + var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); + if (!toplevel) _ = try bs.makeMangledName(c, name); + try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); - const is_pub = !is_unnamed; + const is_pub = toplevel and !is_unnamed; + var redecls = std.ArrayList(Tag.enum_redecl.Data()).init(c.gpa); + defer redecls.deinit(); const init_node = if (enum_decl.getDefinition()) |enum_def| blk: { var pure_enum = true; @@ -968,10 +966,9 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { const init_arg_expr = if (int_type.ptr != null and !isCBuiltinType(int_type, .UInt) and !isCBuiltinType(int_type, .Int)) - transQualType(c, int_type, enum_loc) catch |err| switch (err) { + transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) { error.UnsupportedType => { - try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); - return null; + return failDecl(c, enum_loc, name, "unable to translate enum tag type", .{}); }, else => |e| return e, } @@ -1001,11 +998,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { // In C each enum value is in the global namespace. So we put them there too. // At this point we can rely on the enum emitting successfully. - try addTopLevelDecl(c, field_name, try Tag.enum_redecl.create(c.arena, .{ + try redecls.append(.{ .enum_val_name = enum_val_name, .field_name = field_name, .enum_name = name, - })); + }); } break :blk try Tag.@"enum".create(c.arena, .{ @@ -1026,10 +1023,25 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node { }, }; - try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); - if (!is_unnamed) - try c.alias_list.append(.{ .alias = bare_name, .name = name }); - return try Tag.identifier.create(c.arena, name); + if (toplevel) { + try addTopLevelDecl(c, name, Node.initPayload(&payload.base)); + if (!is_unnamed) + try c.alias_list.append(.{ .alias = bare_name, .name = name }); + } else { + try scope.appendNode(Node.initPayload(&payload.base)); + } + + for (redecls.items) |redecl| { + if (toplevel) { + try addTopLevelDecl(c, redecl.field_name, try Tag.pub_enum_redecl.create(c.arena, redecl)); + } else { + try scope.appendNode(try Tag.enum_redecl.create(c.arena, .{ + .enum_val_name = try bs.makeMangledName(c, redecl.enum_val_name), + .field_name = redecl.field_name, + .enum_name = redecl.enum_name, + })); + } + } } const ResultUsed = enum { @@ -1251,6 +1263,7 @@ fn transCompoundStmtInline( const end_it = stmt.body_end(); while (it != end_it) : (it += 1) { const result = try transStmt(c, parent_scope, it[0], .unused); + if (result.tag() == .declaration) continue; try block.statements.append(result); } } @@ -1285,7 +1298,7 @@ fn transDeclStmtOne( scope: *Scope, decl: *const clang.Decl, block_scope: *Scope.Block, -) TransError!Node { +) TransError!void { switch (decl.getKind()) { .Var => { const var_decl = @ptrCast(*const clang.VarDecl, decl); @@ -1299,8 +1312,7 @@ fn transDeclStmtOne( .Extern, .Static => { // This is actually a global variable, put it in the global scope and reference it. // `_ = mangled_name;` - try visitVarDecl(c, var_decl, mangled_name); - return try maybeSuppressResult(c, scope, .unused, try Tag.identifier.create(c.arena, mangled_name)); + return visitVarDecl(c, var_decl, mangled_name); }, else => {}, } @@ -1308,7 +1320,7 @@ fn transDeclStmtOne( const is_const = qual_type.isConstQualified(); const loc = decl.getLocation(); - const type_node = try transQualTypeMaybeInitialized(c, qual_type, decl_init, loc); + const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc); var init_node = if (decl_init) |expr| if (expr.getStmtClass() == .StringLiteralClass) @@ -1320,7 +1332,7 @@ fn transDeclStmtOne( if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { init_node = try Tag.bool_to_int.create(c.arena, init_node); } - return Tag.var_decl.create(c.arena, .{ + const node = try Tag.var_decl.create(c.arena, .{ .is_pub = false, .is_const = is_const, .is_extern = false, @@ -1332,18 +1344,16 @@ fn transDeclStmtOne( .type = type_node, .init = init_node, }); + try block_scope.statements.append(node); }, .Typedef => { - const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl); - const name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin()); - - const underlying_qual = typedef_decl.getUnderlyingType(); - const underlying_type = underlying_qual.getTypePtr(); - - const mangled_name = try block_scope.makeMangledName(c, name); - const node = (try transCreateNodeTypedef(c, typedef_decl, false, mangled_name)) orelse - return error.UnsupportedTranslation; - return node; + try transTypeDef(c, scope, @ptrCast(*const clang.TypedefNameDecl, decl)); + }, + .Record => { + try transRecordDecl(c, scope, @ptrCast(*const clang.RecordDecl, decl)); + }, + .Enum => { + try transEnumDecl(c, scope, @ptrCast(*const clang.EnumDecl, decl)); }, else => |kind| return fail( c, @@ -1356,21 +1366,14 @@ fn transDeclStmtOne( } fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node { - const block_scope = scope.findBlockScope(c) catch unreachable; + const block_scope = try scope.findBlockScope(c); var it = stmt.decl_begin(); const end_it = stmt.decl_end(); - assert(it != end_it); - while (true) : (it += 1) { - const node = try transDeclStmtOne(c, scope, it[0], block_scope); - - if (it + 1 == end_it) { - return node; - } else { - try block_scope.statements.append(node); - } + while (it != end_it) : (it += 1) { + try transDeclStmtOne(c, scope, it[0], block_scope); } - unreachable; + return Tag.declaration.init(); } fn transDeclRefExpr( @@ -1619,7 +1622,7 @@ fn transIntegerLiteral( // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, expr); - const ty_node = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()); + const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()); const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt()); const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs }); return maybeSuppressResult(c, scope, result_used, as); @@ -1697,7 +1700,7 @@ fn transStringLiteralAsArray( const ty = expr_base.getType().getTypePtr(); const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, ty); - const elem_type = try transQualType(c, const_arr_ty.getElementType(), expr_base.getBeginLoc()); + const elem_type = try transQualType(c, scope, const_arr_ty.getElementType(), expr_base.getBeginLoc()); const arr_type = try Tag.array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_type }); const init_list = try c.arena.alloc(Node, array_size); @@ -1744,9 +1747,9 @@ fn transCCast( if (qualTypeCanon(dst_type).isVoidType()) return expr; if (dst_type.eq(src_type)) return expr; if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type)) - return transCPtrCast(c, loc, dst_type, src_type, expr); + return transCPtrCast(c, scope, loc, dst_type, src_type, expr); - const dst_node = try transQualType(c, dst_type, loc); + const dst_node = try transQualType(c, scope, dst_type, loc); if (cIsInteger(dst_type) and (cIsInteger(src_type) or cIsEnum(src_type))) { // 1. If src_type is an enum, determine the underlying signed int type // 2. Extend or truncate without changing signed-ness. @@ -1903,7 +1906,7 @@ fn transInitListExprRecord( const record_def = record_decl.getDefinition() orelse unreachable; - const ty_node = try transType(c, ty, loc); + const ty_node = try transType(c, scope, ty, loc); const init_count = expr.getNumInits(); var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa); defer field_inits.deinit(); @@ -1952,7 +1955,7 @@ fn transInitListExprArray( ) TransError!Node { const arr_type = ty.getAsArrayTypeUnsafe(); const child_qt = arr_type.getElementType(); - const child_type = try transQualType(c, child_qt, loc); + const child_type = try transQualType(c, scope, child_qt, loc); const init_count = expr.getNumInits(); assert(@ptrCast(*const clang.Type, arr_type).isConstantArrayType()); const const_arr_ty = @ptrCast(*const clang.ConstantArrayType, arr_type); @@ -2217,7 +2220,7 @@ fn transForLoop( block_scope = try Scope.Block.init(c, scope, false); loop_scope.parent = &block_scope.?.base; const init_node = try transStmt(c, &block_scope.?.base, init, .unused); - try block_scope.?.statements.append(init_node); + if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node); } var cond_scope = Scope.Condition{ .base = .{ @@ -2328,7 +2331,7 @@ fn transCase( scope: *Scope, stmt: *const clang.CaseStmt, ) TransError!Node { - const block_scope = scope.findBlockScope(c) catch unreachable; + const block_scope = try scope.findBlockScope(c); const switch_scope = scope.getSwitch(); const label = try block_scope.makeMangledName(c, "case"); @@ -2366,7 +2369,7 @@ fn transDefault( scope: *Scope, stmt: *const clang.DefaultStmt, ) TransError!Node { - const block_scope = scope.findBlockScope(c) catch unreachable; + const block_scope = try scope.findBlockScope(c); const switch_scope = scope.getSwitch(); switch_scope.default_label = try block_scope.makeMangledName(c, "default"); @@ -2400,7 +2403,7 @@ fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, expr); const as_node = try Tag.as.create(c.arena, .{ - .lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()), + .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()), .rhs = try transCreateNodeAPInt(c, result.Val.getInt()), }); return maybeSuppressResult(c, scope, used, as_node); @@ -2446,7 +2449,7 @@ fn transCharLiteral( // @as(T, x) const expr_base = @ptrCast(*const clang.Expr, stmt); const as_node = try Tag.as.create(c.arena, .{ - .lhs = try transQualType(c, expr_base.getType(), expr_base.getBeginLoc()), + .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()), .rhs = int_lit_node, }); return maybeSuppressResult(c, scope, result_used, as_node); @@ -2464,6 +2467,7 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: const end_it = comp.body_end(); while (it != end_it - 1) : (it += 1) { const result = try transStmt(c, &block_scope.base, it[0], .unused); + if (result.tag() == .declaration) continue; try block_scope.statements.append(result); } const break_node = try Tag.break_val.create(c.arena, .{ @@ -2657,7 +2661,7 @@ fn transUnaryExprOrTypeTraitExpr( result_used: ResultUsed, ) TransError!Node { const loc = stmt.getBeginLoc(); - const type_node = try transQualType(c, stmt.getTypeOfArgument(), loc); + const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc); const kind = stmt.getKind(); switch (kind) { @@ -2917,9 +2921,9 @@ fn transCreateCompoundAssign( if (is_shift or requires_int_cast) { // @intCast(rhs) const cast_to_type = if (is_shift) - try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc) + try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc) else - try transQualType(c, getExprQualType(c, lhs), loc); + try transQualType(c, scope, getExprQualType(c, lhs), loc); rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } @@ -2960,9 +2964,9 @@ fn transCreateCompoundAssign( if (is_shift or requires_int_cast) { // @intCast(rhs) const cast_to_type = if (is_shift) - try qualTypeToLog2IntRef(c, getExprQualType(c, rhs), loc) + try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc) else - try transQualType(c, getExprQualType(c, lhs), loc); + try transQualType(c, scope, getExprQualType(c, lhs), loc); rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node }); } @@ -2981,6 +2985,7 @@ fn transCreateCompoundAssign( fn transCPtrCast( c: *Context, + scope: *Scope, loc: clang.SourceLocation, dst_type: clang.QualType, src_type: clang.QualType, @@ -2990,7 +2995,7 @@ fn transCPtrCast( const child_type = ty.getPointeeType(); const src_ty = src_type.getTypePtr(); const src_child_type = src_ty.getPointeeType(); - const dst_type_node = try transType(c, ty, loc); + const dst_type_node = try transType(c, scope, ty, loc); if ((src_child_type.isConstQualified() and !child_type.isConstQualified()) or @@ -3011,7 +3016,7 @@ fn transCPtrCast( // For opaque types a ptrCast is enough expr else blk: { - const child_type_node = try transQualType(c, child_type, loc); + const child_type_node = try transQualType(c, scope, child_type, loc); const alignof = try Tag.alignof.create(c.arena, child_type_node); const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr }); break :blk align_cast; @@ -3160,6 +3165,7 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { /// by the size of the initializer fn transQualTypeInitialized( c: *Context, + scope: *Scope, qt: clang.QualType, decl_init: *const clang.Expr, source_loc: clang.SourceLocation, @@ -3167,7 +3173,7 @@ fn transQualTypeInitialized( const ty = qt.getTypePtr(); if (ty.getTypeClass() == .IncompleteArray) { const incomplete_array_ty = @ptrCast(*const clang.IncompleteArrayType, ty); - const elem_ty = try transType(c, incomplete_array_ty.getElementType().getTypePtr(), source_loc); + const elem_ty = try transType(c, scope, incomplete_array_ty.getElementType().getTypePtr(), source_loc); switch (decl_init.getStmtClass()) { .StringLiteralClass => { @@ -3184,11 +3190,11 @@ fn transQualTypeInitialized( else => {}, } } - return transQualType(c, qt, source_loc); + return transQualType(c, scope, qt, source_loc); } -fn transQualType(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node { - return transType(c, qt.getTypePtr(), source_loc); +fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node { + return transType(c, scope, qt.getTypePtr(), source_loc); } /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness. @@ -3273,7 +3279,7 @@ fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 { } } -fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.SourceLocation) !Node { +fn qualTypeToLog2IntRef(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) !Node { const int_bit_width = try qualTypeIntBitWidth(c, qt); if (int_bit_width != 0) { @@ -3282,7 +3288,7 @@ fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.Sourc return Tag.log2_int_type.create(c.arena, cast_bit_width); } - const zig_type = try transQualType(c, qt, source_loc); + const zig_type = try transQualType(c, scope, qt, source_loc); return Tag.std_math_Log2Int.create(c.arena, zig_type); } @@ -3641,14 +3647,14 @@ fn transCreateNodeShiftOp( const lhs = try transExpr(c, scope, lhs_expr, .used); - const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location); + const rhs_type = try qualTypeToLog2IntRef(c, scope, stmt.getType(), rhs_location); const rhs = try transExprCoercing(c, scope, rhs_expr, .used); const rhs_casted = try Tag.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs }); return transCreateNodeInfixOp(c, scope, op, lhs, rhs_casted, used); } -fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { +fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node { switch (ty.getTypeClass()) { .Builtin => { const builtin_ty = @ptrCast(*const clang.BuiltinType, ty); @@ -3687,16 +3693,16 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio }, .Paren => { const paren_ty = @ptrCast(*const clang.ParenType, ty); - return transQualType(c, paren_ty.getInnerType(), source_loc); + return transQualType(c, scope, paren_ty.getInnerType(), source_loc); }, .Pointer => { const child_qt = ty.getPointeeType(); if (qualTypeChildIsFnProto(child_qt)) { - return Tag.optional_type.create(c.arena, try transQualType(c, child_qt, source_loc)); + return Tag.optional_type.create(c.arena, try transQualType(c, scope, child_qt, source_loc)); } const is_const = child_qt.isConstQualified(); const is_volatile = child_qt.isVolatileQualified(); - const elem_type = try transQualType(c, child_qt, source_loc); + const elem_type = try transQualType(c, scope, child_qt, source_loc); if (typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or qualTypeWasDemotedToOpaque(c, child_qt)) { const ptr = try Tag.single_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); return Tag.optional_type.create(c.arena, ptr); @@ -3709,7 +3715,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const size_ap_int = const_arr_ty.getSize(); const size = size_ap_int.getLimitedValue(math.maxInt(usize)); - const elem_type = try transType(c, const_arr_ty.getElementType().getTypePtr(), source_loc); + const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc); return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type }); }, @@ -3719,7 +3725,7 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const child_qt = incomplete_array_ty.getElementType(); const is_const = child_qt.isConstQualified(); const is_volatile = child_qt.isVolatileQualified(); - const elem_type = try transQualType(c, child_qt, source_loc); + const elem_type = try transQualType(c, scope, child_qt, source_loc); return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type }); }, @@ -3727,38 +3733,41 @@ fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocatio const typedef_ty = @ptrCast(*const clang.TypedefType, ty); const typedef_decl = typedef_ty.getDecl(); - return (try transTypeDef(c, typedef_decl, false)) orelse - fail(c, error.UnsupportedType, source_loc, "unable to translate typedef declaration", .{}); + try transTypeDef(c, scope, typedef_decl); + const name = c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl())).?; + return Tag.identifier.create(c.arena, name); }, .Record => { const record_ty = @ptrCast(*const clang.RecordType, ty); const record_decl = record_ty.getDecl(); - return (try transRecordDecl(c, record_decl)) orelse - fail(c, error.UnsupportedType, source_loc, "unable to resolve record declaration", .{}); + try transRecordDecl(c, scope, record_decl); + const name = c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl())).?; + return Tag.identifier.create(c.arena, name); }, .Enum => { const enum_ty = @ptrCast(*const clang.EnumType, ty); const enum_decl = enum_ty.getDecl(); - return (try transEnumDecl(c, enum_decl)) orelse - fail(c, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{}); + try transEnumDecl(c, scope, enum_decl); + const name = c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl())).?; + return Tag.identifier.create(c.arena, name); }, .Elaborated => { const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty); - return transQualType(c, elaborated_ty.getNamedType(), source_loc); + return transQualType(c, scope, elaborated_ty.getNamedType(), source_loc); }, .Decayed => { const decayed_ty = @ptrCast(*const clang.DecayedType, ty); - return transQualType(c, decayed_ty.getDecayedType(), source_loc); + return transQualType(c, scope, decayed_ty.getDecayedType(), source_loc); }, .Attributed => { const attributed_ty = @ptrCast(*const clang.AttributedType, ty); - return transQualType(c, attributed_ty.getEquivalentType(), source_loc); + return transQualType(c, scope, attributed_ty.getEquivalentType(), source_loc); }, .MacroQualified => { const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, ty); - return transQualType(c, macroqualified_ty.getModifiedType(), source_loc); + return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc); }, else => { const type_name = c.str(ty.getTypeClassName()); @@ -3890,6 +3899,7 @@ fn finishTransFnProto( ) !*ast.Payload.Func { const is_export = if (fn_decl_context) |ctx| ctx.is_export else false; const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false; + const scope = &c.global_scope.base; // TODO check for always_inline attribute // TODO check for align attribute @@ -3914,7 +3924,7 @@ fn finishTransFnProto( break :blk param_name; } else null; - const type_node = try transQualType(c, param_qt, source_loc); + const type_node = try transQualType(c, scope, param_qt, source_loc); fn_params.addOneAssumeCapacity().* = .{ .is_noalias = is_noalias, @@ -3955,9 +3965,9 @@ fn finishTransFnProto( // convert primitive c_void to actual void (only for return type) break :blk Tag.void_type.init(); } else { - break :blk transQualType(c, return_qt, source_loc) catch |err| switch (err) { + break :blk transQualType(c, scope, return_qt, source_loc) catch |err| switch (err) { error.UnsupportedType => { - try warn(c, &c.global_scope.base, source_loc, "unsupported function proto return type", .{}); + try warn(c, scope, source_loc, "unsupported function proto return type", .{}); return err; }, error.OutOfMemory => |e| return e, diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 6d22c7a270..25cbe1bf3f 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -14,6 +14,8 @@ pub const Node = extern union { ptr_otherwise: *Payload, pub const Tag = enum { + /// Declarations add themselves to the correct scopes and should not be emitted as this tag. + declaration, null_literal, undefined_literal, /// opaque {} @@ -186,6 +188,7 @@ pub const Node = extern union { /// pub const name = init; pub_var_simple, /// pub const enum_field_name = @enumToInt(enum_name.field_name); + pub_enum_redecl, enum_redecl, /// pub inline fn name(params) return_type body @@ -201,6 +204,7 @@ pub const Node = extern union { pub fn Type(comptime t: Tag) type { return switch (t) { + .declaration, .null_literal, .undefined_literal, .opaque_literal, @@ -325,7 +329,7 @@ pub const Node = extern union { .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, .log2_int_type => Payload.Log2IntType, .var_simple, .pub_var_simple => Payload.SimpleVarDecl, - .enum_redecl => Payload.EnumRedecl, + .pub_enum_redecl, .enum_redecl => Payload.EnumRedecl, .array_filler => Payload.ArrayFiller, .pub_inline_fn => Payload.PubInlineFn, .field_access => Payload.FieldAccess, @@ -742,6 +746,7 @@ fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { switch (node.tag()) { + .declaration => unreachable, .warning => { const payload = node.castTag(.warning).?.data; try c.buf.appendSlice(payload); @@ -1585,9 +1590,9 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, }); }, - .enum_redecl => { - const payload = node.castTag(.enum_redecl).?.data; - _ = try c.addToken(.keyword_pub, "pub"); + .pub_enum_redecl, .enum_redecl => { + const payload = @fieldParentPtr(Payload.EnumRedecl, "base", node.ptr_otherwise).data; + if (node.tag() == .pub_enum_redecl) _ = try c.addToken(.keyword_pub, "pub"); const const_tok = try c.addToken(.keyword_const, "const"); _ = try c.addIdentifier(payload.enum_val_name); _ = try c.addToken(.equal, "="); @@ -1878,6 +1883,7 @@ fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { switch (node.tag()) { + .declaration => unreachable, .null_literal, .undefined_literal, .true_literal, @@ -1991,6 +1997,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { .alias, .var_simple, .pub_var_simple, + .pub_enum_redecl, .enum_redecl, .@"while", .@"switch", diff --git a/test/translate_c.zig b/test/translate_c.zig index 79361bf9bf..26d35a4c2e 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,136 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("scoped enum", + \\void foo() { + \\ enum Foo { + \\ A, + \\ B, + \\ C, + \\ }; + \\ enum Foo a = B; + \\ { + \\ enum Foo { + \\ A, + \\ B, + \\ C, + \\ }; + \\ enum Foo a = B; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ const enum_Foo = extern enum(c_int) { + \\ A, + \\ B, + \\ C, + \\ _, + \\ }; + \\ const A = @enumToInt(enum_Foo.A); + \\ const B = @enumToInt(enum_Foo.B); + \\ const C = @enumToInt(enum_Foo.C); + \\ var a: enum_Foo = @intToEnum(enum_Foo, B); + \\ { + \\ const enum_Foo = extern enum(c_int) { + \\ A, + \\ B, + \\ C, + \\ _, + \\ }; + \\ const A_2 = @enumToInt(enum_Foo.A); + \\ const B_3 = @enumToInt(enum_Foo.B); + \\ const C_4 = @enumToInt(enum_Foo.C); + \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3); + \\ } + \\} + }); + + cases.add("scoped record", + \\void foo() { + \\ struct Foo { + \\ int A; + \\ int B; + \\ int C; + \\ }; + \\ struct Foo a = {0}; + \\ { + \\ struct Foo { + \\ int A; + \\ int B; + \\ int C; + \\ }; + \\ struct Foo a = {0}; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ const struct_Foo = extern struct { + \\ A: c_int, + \\ B: c_int, + \\ C: c_int, + \\ }; + \\ var a: struct_Foo = struct_Foo{ + \\ .A = @as(c_int, 0), + \\ .B = 0, + \\ .C = 0, + \\ }; + \\ { + \\ const struct_Foo_1 = extern struct { + \\ A: c_int, + \\ B: c_int, + \\ C: c_int, + \\ }; + \\ var a_2: struct_Foo_1 = struct_Foo_1{ + \\ .A = @as(c_int, 0), + \\ .B = 0, + \\ .C = 0, + \\ }; + \\ } + \\} + }); + + cases.add("scoped typedef", + \\void foo() { + \\ typedef union { + \\ int A; + \\ int B; + \\ int C; + \\ } Foo; + \\ Foo a = {0}; + \\ { + \\ typedef union { + \\ int A; + \\ int B; + \\ int C; + \\ } Foo; + \\ Foo a = {0}; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ const union_unnamed_1 = extern union { + \\ A: c_int, + \\ B: c_int, + \\ C: c_int, + \\ }; + \\ const Foo = union_unnamed_1; + \\ var a: Foo = Foo{ + \\ .A = @as(c_int, 0), + \\ }; + \\ { + \\ const union_unnamed_2 = extern union { + \\ A: c_int, + \\ B: c_int, + \\ C: c_int, + \\ }; + \\ const Foo_1 = union_unnamed_2; + \\ var a_2: Foo_1 = Foo_1{ + \\ .A = @as(c_int, 0), + \\ }; + \\ } + \\} + }); + cases.add("use cast param as macro fn return type", \\#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED) , &[_][]const u8{ @@ -62,7 +192,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export var bar: f32 = @import("std").mem.zeroes(f32); \\threadlocal var bar_1: c_int = 2; \\pub export fn foo() c_int { - \\ _ = bar_1; \\ return 0; \\} }); @@ -579,9 +708,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ '2', \\ 0, \\}; - \\pub export fn foo() void { - \\ _ = v2; - \\} + \\pub export fn foo() void {} }); cases.add("simple function definition", @@ -1355,11 +1482,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\extern enum enum_ty my_enum; \\enum enum_ty { FOO }; , &[_][]const u8{ - \\pub const FOO = @enumToInt(enum_enum_ty.FOO); \\pub const enum_enum_ty = extern enum(c_int) { \\ FOO, \\ _, \\}; + \\pub const FOO = @enumToInt(enum_enum_ty.FOO); \\pub extern var my_enum: enum_enum_ty; }); @@ -1501,48 +1628,48 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ p, \\}; , &[_][]const u8{ - \\pub const a = @enumToInt(enum_unnamed_1.a); - \\pub const b = @enumToInt(enum_unnamed_1.b); - \\pub const c = @enumToInt(enum_unnamed_1.c); \\const enum_unnamed_1 = extern enum(c_int) { \\ a, \\ b, \\ c, \\ _, \\}; + \\pub const a = @enumToInt(enum_unnamed_1.a); + \\pub const b = @enumToInt(enum_unnamed_1.b); + \\pub const c = @enumToInt(enum_unnamed_1.c); \\pub const d = enum_unnamed_1; - \\pub const e = @enumToInt(enum_unnamed_2.e); - \\pub const f = @enumToInt(enum_unnamed_2.f); - \\pub const g = @enumToInt(enum_unnamed_2.g); \\const enum_unnamed_2 = extern enum(c_int) { \\ e = 0, \\ f = 4, \\ g = 5, \\ _, \\}; + \\pub const e = @enumToInt(enum_unnamed_2.e); + \\pub const f = @enumToInt(enum_unnamed_2.f); + \\pub const g = @enumToInt(enum_unnamed_2.g); \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e); - \\pub const i = @enumToInt(enum_unnamed_3.i); - \\pub const j = @enumToInt(enum_unnamed_3.j); - \\pub const k = @enumToInt(enum_unnamed_3.k); \\const enum_unnamed_3 = extern enum(c_int) { \\ i, \\ j, \\ k, \\ _, \\}; + \\pub const i = @enumToInt(enum_unnamed_3.i); + \\pub const j = @enumToInt(enum_unnamed_3.j); + \\pub const k = @enumToInt(enum_unnamed_3.k); \\pub const struct_Baz = extern struct { \\ l: enum_unnamed_3, \\ m: d, \\}; - \\pub const n = @enumToInt(enum_i.n); - \\pub const o = @enumToInt(enum_i.o); - \\pub const p = @enumToInt(enum_i.p); \\pub const enum_i = extern enum(c_int) { \\ n, \\ o, \\ p, \\ _, \\}; + \\pub const n = @enumToInt(enum_i.n); + \\pub const o = @enumToInt(enum_i.o); + \\pub const p = @enumToInt(enum_i.p); , \\pub const Baz = struct_Baz; }); @@ -1989,13 +2116,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ Two, \\}; , &[_][]const u8{ - \\pub const One = @enumToInt(enum_unnamed_1.One); - \\pub const Two = @enumToInt(enum_unnamed_1.Two); \\const enum_unnamed_1 = extern enum(c_int) { \\ One, \\ Two, \\ _, \\}; + \\pub const One = @enumToInt(enum_unnamed_1.One); + \\pub const Two = @enumToInt(enum_unnamed_1.Two); }); cases.add("c style cast", @@ -2093,15 +2220,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); \\} , &[_][]const u8{ - \\pub const FooA = @enumToInt(enum_Foo.A); - \\pub const FooB = @enumToInt(enum_Foo.B); - \\pub const FooC = @enumToInt(enum_Foo.C); \\pub const enum_Foo = extern enum(c_int) { \\ A, \\ B, \\ C, \\ _, \\}; + \\pub const FooA = @enumToInt(enum_Foo.A); + \\pub const FooB = @enumToInt(enum_Foo.B); + \\pub const FooC = @enumToInt(enum_Foo.C); \\pub const SomeTypedef = c_int; \\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void) c_int { \\ var a = arg_a; @@ -2147,6 +2274,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ B, \\ _, \\}; + \\pub const BarA = @enumToInt(enum_Bar.A); + \\pub const BarB = @enumToInt(enum_Bar.B); \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; , \\pub const Foo = struct_Foo; @@ -2413,6 +2542,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ C, \\ _, \\}; + \\pub const A = @enumToInt(enum_SomeEnum.A); + \\pub const B = @enumToInt(enum_SomeEnum.B); + \\pub const C = @enumToInt(enum_SomeEnum.C); \\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*c_void, arg_d: enum_SomeEnum) c_int { \\ var a = arg_a; \\ var b = arg_b; @@ -2872,15 +3004,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ Foo1, \\}; , &[_][]const u8{ - \\pub const FooA = @enumToInt(enum_Foo.A); - \\pub const FooB = @enumToInt(enum_Foo.B); - \\pub const Foo1 = @enumToInt(enum_Foo.@"1"); \\pub const enum_Foo = extern enum(c_int) { \\ A = 2, \\ B = 5, \\ @"1" = 6, \\ _, \\}; + \\pub const FooA = @enumToInt(enum_Foo.A); + \\pub const FooB = @enumToInt(enum_Foo.B); + \\pub const Foo1 = @enumToInt(enum_Foo.@"1"); , \\pub const Foo = enum_Foo; }); From 3717bedb4e3198fe2ded167b41f9b0441e817b9c Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sat, 13 Feb 2021 16:24:02 -0800 Subject: [PATCH 32/36] translate-c: Add test for using correct block label for StmtExpr The previous iteration of translate-c used an incorrect block label in the break statement for a translated C statement expression. This adds a test to ensure the correct label is used in the new intermediate AST version of translate-c. --- test/run_translated_c.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index b8af201e36..04095d3b71 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -922,4 +922,13 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Use correct break label for statement expression in nested scope", + \\#include + \\int main(void) { + \\ int x = ({1, ({2; 3;});}); + \\ if (x != 3) abort(); + \\ return 0; + \\} + , ""); } From 7ca53bdfaab59e61c38d0bedb6b16739904f7519 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Feb 2021 22:11:26 +0200 Subject: [PATCH 33/36] translate-c: improve switch translation --- src/clang.zig | 6 +- src/translate_c.zig | 363 ++++++++++++++++++++-------------------- src/translate_c/ast.zig | 111 ++++++++---- test/translate_c.zig | 76 ++++----- 4 files changed, 295 insertions(+), 261 deletions(-) diff --git a/src/clang.zig b/src/clang.zig index fbb955205b..5adb858b90 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -273,12 +273,12 @@ pub const CompoundAssignOperator = opaque { pub const CompoundStmt = opaque { pub const body_begin = ZigClangCompoundStmt_body_begin; - extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) const_body_iterator; + extern fn ZigClangCompoundStmt_body_begin(*const CompoundStmt) ConstBodyIterator; pub const body_end = ZigClangCompoundStmt_body_end; - extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) const_body_iterator; + extern fn ZigClangCompoundStmt_body_end(*const CompoundStmt) ConstBodyIterator; - pub const const_body_iterator = [*]const *Stmt; + pub const ConstBodyIterator = [*]const *Stmt; }; pub const ConditionalOperator = opaque {}; diff --git a/src/translate_c.zig b/src/translate_c.zig index 5ac60bffae..47c601677e 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -31,7 +31,6 @@ const Scope = struct { parent: ?*Scope, const Id = enum { - @"switch", block, root, condition, @@ -39,17 +38,6 @@ const Scope = struct { do_loop, }; - /// Represents an in-progress Node.Switch. This struct is stack-allocated. - /// When it is deinitialized, it produces an Node.Switch which is allocated - /// into the main arena. - const Switch = struct { - base: Scope, - pending_block: Block, - cases: std.ArrayList(Node), - switch_label: ?[]const u8, - default_label: ?[]const u8, - }; - /// Used for the scope of condition expressions, for example `if (cond)`. /// The block is lazily initialised because it is only needed for rare /// cases of comma operators being used. @@ -230,7 +218,7 @@ const Scope = struct { return switch (scope.id) { .root => return name, .block => @fieldParentPtr(Block, "base", scope).getAlias(name), - .@"switch", .loop, .do_loop, .condition => scope.parent.?.getAlias(name), + .loop, .do_loop, .condition => scope.parent.?.getAlias(name), }; } @@ -238,7 +226,7 @@ const Scope = struct { return switch (scope.id) { .root => @fieldParentPtr(Root, "base", scope).contains(name), .block => @fieldParentPtr(Block, "base", scope).contains(name), - .@"switch", .loop, .do_loop, .condition => scope.parent.?.contains(name), + .loop, .do_loop, .condition => scope.parent.?.contains(name), }; } @@ -247,24 +235,12 @@ const Scope = struct { while (true) { switch (scope.id) { .root => unreachable, - .@"switch" => return scope, .loop, .do_loop => return scope, else => scope = scope.parent.?, } } } - fn getSwitch(inner: *Scope) *Scope.Switch { - var scope = inner; - while (true) { - switch (scope.id) { - .root => unreachable, - .@"switch" => return @fieldParentPtr(Switch, "base", scope), - else => scope = scope.parent.?, - } - } - } - /// Appends a node to the first block scope if inside a function, or to the root tree if not. fn appendNode(inner: *Scope, node: Node) !void { var scope = inner; @@ -570,7 +546,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { } const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt); - transCompoundStmtInline(c, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) { + transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) { error.OutOfMemory => |e| return e, error.UnsupportedTranslation, error.UnsupportedType, @@ -583,24 +559,10 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }; // add return statement if the function didn't have one blk: { - if (fn_ty.getNoReturnAttr()) break :blk; - if (isCVoid(return_qt)) break :blk; - - if (block_scope.statements.items.len > 0) { - var last = block_scope.statements.items[block_scope.statements.items.len - 1]; - while (true) { - switch (last.tag()) { - .block => { - const block = last.castTag(.block).?; - if (block.data.stmts.len == 0) break; - - last = block.data.stmts[block.data.stmts.len - 1]; - }, - // no extra return needed - .@"return", .return_void => break :blk, - else => break, - } - } + const maybe_body = try block_scope.complete(c); + if (fn_ty.getNoReturnAttr() or isCVoid(return_qt) or maybe_body.isNoreturn(false)) { + proto_node.data.body = maybe_body; + break :blk; } const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) { @@ -616,9 +578,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }; const ret = try Tag.@"return".create(c.arena, rhs); try block_scope.statements.append(ret); + proto_node.data.body = try block_scope.complete(c); } - proto_node.data.body = try block_scope.complete(c); return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); } @@ -1079,7 +1041,7 @@ fn transStmt( return Tag.empty_block.init(); }, .ContinueStmtClass => return Tag.@"continue".init(), - .BreakStmtClass => return transBreak(c, scope), + .BreakStmtClass => return Tag.@"break".init(), .ForStmtClass => return transForLoop(c, scope, @ptrCast(*const clang.ForStmt, stmt)), .FloatingLiteralClass => return transFloatingLiteral(c, scope, @ptrCast(*const clang.FloatingLiteral, stmt), result_used), .ConditionalOperatorClass => { @@ -1089,8 +1051,9 @@ fn transStmt( return transBinaryConditionalOperator(c, scope, @ptrCast(*const clang.BinaryConditionalOperator, stmt), result_used); }, .SwitchStmtClass => return transSwitch(c, scope, @ptrCast(*const clang.SwitchStmt, stmt)), - .CaseStmtClass => return transCase(c, scope, @ptrCast(*const clang.CaseStmt, stmt)), - .DefaultStmtClass => return transDefault(c, scope, @ptrCast(*const clang.DefaultStmt, stmt)), + .CaseStmtClass, .DefaultStmtClass => { + return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{}); + }, .ConstantExprClass => return transConstantExpr(c, scope, @ptrCast(*const clang.Expr, stmt), result_used), .PredefinedExprClass => return transPredefinedExpr(c, scope, @ptrCast(*const clang.PredefinedExpr, stmt), result_used), .CharacterLiteralClass => return transCharLiteral(c, scope, @ptrCast(*const clang.CharacterLiteral, stmt), result_used, .with_as), @@ -1107,13 +1070,7 @@ fn transStmt( return maybeSuppressResult(c, scope, result_used, expr); }, else => { - return fail( - c, - error.UnsupportedTranslation, - stmt.getBeginLoc(), - "TODO implement translation of stmt class {s}", - .{@tagName(sc)}, - ); + return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); }, } } @@ -1255,14 +1212,13 @@ fn transBinaryOperator( fn transCompoundStmtInline( c: *Context, - parent_scope: *Scope, stmt: *const clang.CompoundStmt, block: *Scope.Block, ) TransError!void { var it = stmt.body_begin(); const end_it = stmt.body_end(); while (it != end_it) : (it += 1) { - const result = try transStmt(c, parent_scope, it[0], .unused); + const result = try transStmt(c, &block.base, it[0], .unused); if (result.tag() == .declaration) continue; try block.statements.append(result); } @@ -1271,7 +1227,7 @@ fn transCompoundStmtInline( fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node { var block_scope = try Scope.Block.init(c, scope, false); defer block_scope.deinit(); - try transCompoundStmtInline(c, &block_scope.base, stmt, &block_scope); + try transCompoundStmtInline(c, stmt, &block_scope); return try block_scope.complete(c); } @@ -2162,7 +2118,7 @@ fn transDoWhileLoop( defer cond_scope.deinit(); const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); const if_not_break = switch (cond.tag()) { - .false_literal => try Tag.@"break".create(c.arena, null), + .false_literal => Tag.@"break".init(), .true_literal => { const body_node = try transStmt(c, scope, stmt.getBody(), .unused); return Tag.while_true.create(c.arena, body_node); @@ -2263,133 +2219,189 @@ fn transSwitch( }; defer cond_scope.deinit(); const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used); - const switch_node = try c.arena.create(ast.Payload.Switch); - switch_node.* = .{ - .base = .{ .tag = .@"switch" }, - .data = .{ - .cond = switch_expr, - .cases = undefined, // set later - }, - }; - var switch_scope = Scope.Switch{ - .base = .{ - .id = .@"switch", - .parent = scope, - }, - .cases = std.ArrayList(Node).init(c.gpa), - .pending_block = undefined, - .default_label = null, - .switch_label = null, - }; - defer switch_scope.cases.deinit(); + var cases = std.ArrayList(Node).init(c.gpa); + defer cases.deinit(); + var has_default = false; - // tmp block that all statements will go before being picked up by a case or default - var block_scope = try Scope.Block.init(c, &switch_scope.base, false); - defer block_scope.deinit(); + const body = stmt.getBody(); + assert(body.getStmtClass() == .CompoundStmtClass); + const compound_stmt = @ptrCast(*const clang.CompoundStmt, body); + var it = compound_stmt.body_begin(); + const end_it = compound_stmt.body_end(); + // Iterate over switch body and collect all cases. + // Fallthrough is handled by duplicating statements. + while (it != end_it) : (it += 1) { + switch (it[0].getStmtClass()) { + .CaseStmtClass => { + var items = std.ArrayList(Node).init(c.gpa); + defer items.deinit(); + const sub = try transCaseStmt(c, scope, it[0], &items); + const res = try transSwitchProngStmt(c, scope, sub, it, end_it); - // Note that we do not defer a deinit here; the switch_scope.pending_block field - // has its own memory management. This resource is freed inside `transCase` and - // then the final pending_block is freed at the bottom of this function with - // pending_block.deinit(). - switch_scope.pending_block = try Scope.Block.init(c, scope, false); - try switch_scope.pending_block.statements.append(Node.initPayload(&switch_node.base)); + if (items.items.len == 0) { + has_default = true; + const switch_else = try Tag.switch_else.create(c.arena, res); + try cases.append(switch_else); + } else { + const switch_prong = try Tag.switch_prong.create(c.arena, .{ + .cases = try c.arena.dupe(Node, items.items), + .cond = res, + }); + try cases.append(switch_prong); + } + }, + .DefaultStmtClass => { + has_default = true; + const default_stmt = @ptrCast(*const clang.DefaultStmt, it[0]); - const last = try transStmt(c, &block_scope.base, stmt.getBody(), .unused); + var sub = default_stmt.getSubStmt(); + while (true) switch (sub.getStmtClass()) { + .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(), + .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(), + else => break, + }; - // take all pending statements - const last_block_stmts = last.castTag(.block).?.data.stmts; - try switch_scope.pending_block.statements.ensureCapacity( - switch_scope.pending_block.statements.items.len + last_block_stmts.len, - ); - for (last_block_stmts) |n| { - switch_scope.pending_block.statements.appendAssumeCapacity(n); + const res = try transSwitchProngStmt(c, scope, sub, it, end_it); + + const switch_else = try Tag.switch_else.create(c.arena, res); + try cases.append(switch_else); + }, + else => {}, // collected in transSwitchProngStmt + } } - if (switch_scope.default_label == null) { - switch_scope.switch_label = try block_scope.makeMangledName(c, "switch"); - } - if (switch_scope.switch_label) |l| { - switch_scope.pending_block.label = l; - } - if (switch_scope.default_label == null) { - const else_prong = try Tag.switch_else.create( - c.arena, - try Tag.@"break".create(c.arena, switch_scope.switch_label.?), - ); - try switch_scope.cases.append(else_prong); + if (!has_default) { + const else_prong = try Tag.switch_else.create(c.arena, Tag.@"break".init()); + try cases.append(else_prong); } - switch_node.data.cases = try c.arena.dupe(Node, switch_scope.cases.items); - const result_node = try switch_scope.pending_block.complete(c); - switch_scope.pending_block.deinit(); - return result_node; -} - -fn transCase( - c: *Context, - scope: *Scope, - stmt: *const clang.CaseStmt, -) TransError!Node { - const block_scope = try scope.findBlockScope(c); - const switch_scope = scope.getSwitch(); - const label = try block_scope.makeMangledName(c, "case"); - - const expr = if (stmt.getRHS()) |rhs| blk: { - const lhs_node = try transExpr(c, scope, stmt.getLHS(), .used); - const rhs_node = try transExpr(c, scope, rhs, .used); - - break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); - } else - try transExpr(c, scope, stmt.getLHS(), .used); - - const switch_prong = try Tag.switch_prong.create(c.arena, .{ - .lhs = expr, - .rhs = try Tag.@"break".create(c.arena, label), + return Tag.@"switch".create(c.arena, .{ + .cond = switch_expr, + .cases = try c.arena.dupe(Node, cases.items), }); - try switch_scope.cases.append(switch_prong); - - switch_scope.pending_block.label = label; - - // take all pending statements - try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); - block_scope.statements.shrinkAndFree(0); - - const pending_node = try switch_scope.pending_block.complete(c); - switch_scope.pending_block.deinit(); - switch_scope.pending_block = try Scope.Block.init(c, scope, false); - - try switch_scope.pending_block.statements.append(pending_node); - - return transStmt(c, scope, stmt.getSubStmt(), .unused); } -fn transDefault( +/// Collects all items for this case, returns the first statement after the labels. +/// If items ends up empty, the prong should be translated as an else. +fn transCaseStmt(c: *Context, scope: *Scope, stmt: *const clang.Stmt, items: *std.ArrayList(Node)) TransError!*const clang.Stmt { + var sub = stmt; + var seen_default = false; + while (true) { + switch (sub.getStmtClass()) { + .DefaultStmtClass => { + seen_default = true; + items.items.len = 0; + const default_stmt = @ptrCast(*const clang.DefaultStmt, sub); + sub = default_stmt.getSubStmt(); + }, + .CaseStmtClass => { + const case_stmt = @ptrCast(*const clang.CaseStmt, sub); + + if (seen_default) { + items.items.len = 0; + sub = case_stmt.getSubStmt(); + continue; + } + + const expr = if (case_stmt.getRHS()) |rhs| blk: { + const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used); + const rhs_node = try transExprCoercing(c, scope, rhs, .used); + + break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node }); + } else + try transExprCoercing(c, scope, case_stmt.getLHS(), .used); + + try items.append(expr); + sub = case_stmt.getSubStmt(); + }, + else => return sub, + } + } +} + +/// Collects all statements seen by this case into a block. +/// Avoids creating a block if the first statement is a break or return. +fn transSwitchProngStmt( c: *Context, scope: *Scope, - stmt: *const clang.DefaultStmt, + stmt: *const clang.Stmt, + parent_it: clang.CompoundStmt.ConstBodyIterator, + parent_end_it: clang.CompoundStmt.ConstBodyIterator, ) TransError!Node { - const block_scope = try scope.findBlockScope(c); - const switch_scope = scope.getSwitch(); - switch_scope.default_label = try block_scope.makeMangledName(c, "default"); + switch (stmt.getStmtClass()) { + .BreakStmtClass => return Tag.empty_block.init(), + .ReturnStmtClass => return transStmt(c, scope, stmt, .unused), + .CaseStmtClass, .DefaultStmtClass => unreachable, + else => { + var block_scope = try Scope.Block.init(c, scope, false); + defer block_scope.deinit(); - const else_prong = try Tag.switch_else.create( - c.arena, - try Tag.@"break".create(c.arena, switch_scope.default_label.?), - ); - try switch_scope.cases.append(else_prong); - switch_scope.pending_block.label = switch_scope.default_label.?; + // we do not need to translate `stmt` since it is the first stmt of `parent_it` + try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it); + return try block_scope.complete(c); + }, + } +} - // take all pending statements - try switch_scope.pending_block.statements.appendSlice(block_scope.statements.items); - block_scope.statements.shrinkAndFree(0); +/// Collects all statements seen by this case into a block. +fn transSwitchProngStmtInline( + c: *Context, + block: *Scope.Block, + start_it: clang.CompoundStmt.ConstBodyIterator, + end_it: clang.CompoundStmt.ConstBodyIterator, +) TransError!void { + var it = start_it; + while (it != end_it) : (it += 1) { + switch (it[0].getStmtClass()) { + .ReturnStmtClass => { + const result = try transStmt(c, &block.base, it[0], .unused); + try block.statements.append(result); + return; + }, + .BreakStmtClass => return, + .CaseStmtClass => { + var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt(); + while (true) switch (sub.getStmtClass()) { + .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(), + .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(), + else => break, + }; + const result = try transStmt(c, &block.base, sub, .unused); + assert(result.tag() != .declaration); + try block.statements.append(result); + }, + .DefaultStmtClass => { + var sub = @ptrCast(*const clang.DefaultStmt, it[0]).getSubStmt(); + while (true) switch (sub.getStmtClass()) { + .CaseStmtClass => sub = @ptrCast(*const clang.CaseStmt, sub).getSubStmt(), + .DefaultStmtClass => sub = @ptrCast(*const clang.DefaultStmt, sub).getSubStmt(), + else => break, + }; + const result = try transStmt(c, &block.base, sub, .unused); + assert(result.tag() != .declaration); + try block.statements.append(result); + }, + .CompoundStmtClass => { + const compound_stmt = @ptrCast(*const clang.CompoundStmt, it[0]); + var child_block = try Scope.Block.init(c, &block.base, false); + defer child_block.deinit(); - const pending_node = try switch_scope.pending_block.complete(c); - switch_scope.pending_block.deinit(); - switch_scope.pending_block = try Scope.Block.init(c, scope, false); - try switch_scope.pending_block.statements.append(pending_node); - - return transStmt(c, scope, stmt.getSubStmt(), .unused); + try transCompoundStmtInline(c, compound_stmt, &child_block); + const result = try child_block.complete(c); + try block.statements.append(result); + if (result.isNoreturn(true)) { + return; + } + }, + else => { + const result = try transStmt(c, &block.base, it[0], .unused); + if (result.tag() == .declaration) continue; + try block.statements.append(result); + }, + } + } + return; } fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node { @@ -3025,19 +3037,6 @@ fn transCPtrCast( } } -fn transBreak(c: *Context, scope: *Scope) TransError!Node { - const break_scope = scope.getBreakableScope(); - const label_text: ?[]const u8 = if (break_scope.id == .@"switch") blk: { - const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope); - const block_scope = try scope.findBlockScope(c); - swtch.switch_label = try block_scope.makeMangledName(c, "switch"); - break :blk swtch.switch_label; - } else - null; - - return Tag.@"break".create(c.arena, label_text); -} - fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node { // TODO use something more accurate var dbl = stmt.getValueAsApproximateDouble(); diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 25cbe1bf3f..2306d1c36f 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -30,6 +30,7 @@ pub const Node = extern union { noreturn_type, @"anytype", @"continue", + @"break", /// pub usingnamespace @import("std").c.builtins; usingnamespace_builtins, // After this, the tag requires a payload. @@ -48,9 +49,8 @@ pub const Node = extern union { @"switch", /// else => operand, switch_else, - /// lhs => rhs, + /// items => body, switch_prong, - @"break", break_val, @"return", field_access, @@ -219,6 +219,7 @@ pub const Node = extern union { .noreturn_type, .@"anytype", .@"continue", + .@"break", => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), .std_mem_zeroes, @@ -294,7 +295,6 @@ pub const Node = extern union { .int_to_ptr, .array_cat, .ellipsis3, - .switch_prong, .assign, .align_cast, .array_access, @@ -312,8 +312,7 @@ pub const Node = extern union { => Payload.Value, .@"if" => Payload.If, .@"while" => Payload.While, - .@"switch", .array_init => Payload.Switch, - .@"break" => Payload.Break, + .@"switch", .array_init,.switch_prong => Payload.Switch, .break_val => Payload.BreakVal, .call => Payload.Call, .var_decl => Payload.VarDecl, @@ -377,6 +376,37 @@ pub const Node = extern union { std.debug.assert(@enumToInt(payload.tag) >= Tag.no_payload_count); return .{ .ptr_otherwise = payload }; } + + pub fn isNoreturn(node: Node, break_counts: bool) bool { + switch (node.tag()) { + .block => { + const block_node = node.castTag(.block).?; + if (block_node.data.stmts.len == 0) return false; + + const last = block_node.data.stmts[block_node.data.stmts.len - 1]; + return last.isNoreturn(break_counts); + }, + .@"switch" => { + const switch_node = node.castTag(.@"switch").?; + + for (switch_node.data.cases) |case| { + const body = if (case.castTag(.switch_else)) |some| + some.data + else if (case.castTag(.switch_prong)) |some| + some.data.cond + else unreachable; + + if (!body.isNoreturn(break_counts)) return false; + } + return true; + }, + .@"return", .return_void => return true, + .break_val, .@"break" => if (break_counts) return true, + else => {}, + } + return false; + } + }; pub const Payload = struct { @@ -434,11 +464,6 @@ pub const Payload = struct { }, }; - pub const Break = struct { - base: Payload, - data: ?[]const u8, - }; - pub const BreakVal = struct { base: Payload, data: struct { @@ -855,22 +880,14 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { .rhs = undefined, }, }), - .@"break" => { - const payload = node.castTag(.@"break").?.data; - const tok = try c.addToken(.keyword_break, "break"); - const break_label = if (payload) |some| blk: { - _ = try c.addToken(.colon, ":"); - break :blk try c.addIdentifier(some); - } else 0; - return c.addNode(.{ - .tag = .@"break", - .main_token = tok, - .data = .{ - .lhs = break_label, - .rhs = 0, - }, - }); - }, + .@"break" => return c.addNode(.{ + .tag = .@"break", + .main_token = try c.addToken(.keyword_break, "break"), + .data = .{ + .lhs = 0, + .rhs = 0, + }, + }), .break_val => { const payload = node.castTag(.break_val).?.data; const tok = try c.addToken(.keyword_break, "break"); @@ -1447,15 +1464,37 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .switch_prong => { const payload = node.castTag(.switch_prong).?.data; - const item = try renderNode(c, payload.lhs); - return c.addNode(.{ - .tag = .switch_case_one, - .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), - .data = .{ - .lhs = item, - .rhs = try renderNode(c, payload.rhs), - }, - }); + var items = try c.gpa.alloc(NodeIndex, std.math.max(payload.cases.len, 1)); + defer c.gpa.free(items); + items[0] = 0; + for (payload.cases) |item, i| { + if (i != 0) _ = try c.addToken(.comma, ","); + items[i] = try renderNode(c, item); + } + _ = try c.addToken(.r_brace, "}"); + if (items.len < 2) { + return c.addNode(.{ + .tag = .switch_case_one, + .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), + .data = .{ + .lhs = items[0], + .rhs = try renderNode(c, payload.cond), + }, + }); + } else { + const span = try c.listToSpan(items); + return c.addNode(.{ + .tag = .switch_case, + .main_token = try c.addToken(.equal_angle_bracket_right, "=>"), + .data = .{ + .lhs = try c.addExtra(NodeSubRange{ + .start = span.start, + .end = span.end, + }), + .rhs = try renderNode(c, payload.cond), + }, + }); + } }, .opaque_literal => { const opaque_tok = try c.addToken(.keyword_opaque, "opaque"); @@ -1870,7 +1909,7 @@ fn addSemicolonIfNeeded(c: *Context, node: Node) !void { fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { switch (node.tag()) { - .block, .empty_block, .block_single, => {}, + .block, .empty_block, .block_single => {}, .@"if" => { const payload = node.castTag(.@"if").?.data; if (payload.@"else") |some| diff --git a/test/translate_c.zig b/test/translate_c.zig index 26d35a4c2e..a2d1bf417e 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -276,27 +276,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} , &[_][]const u8{ // TODO properly translate this - \\pub export fn main() c_int { - \\ var i: c_int = 2; - \\ @"switch": { - \\ case_1: { - \\ case: { - \\ switch (i) { - \\ @as(c_int, 0) => break :case, - \\ @as(c_int, 2) => break :case_1, - \\ else => break :@"switch", - \\ } - \\ } - \\ } - \\ { - \\ { - \\ i += @as(c_int, 2); - \\ } - \\ i += @as(c_int, 1); - \\ } - \\ } - \\ return 0; - \\} + \\source.h:5:13: warning: TODO complex switch + , + \\source.h:1:5: warning: unable to translate function, demoted to extern + \\pub extern fn main() c_int; }); cases.add("correct semicolon after infixop", @@ -2013,34 +1996,47 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ default: \\ res = 3 * i; \\ break; + \\ break; \\ case 4: + \\ case 5: + \\ res = 69; + \\ { \\ res = 5; + \\ return; + \\ } + \\ case 6: + \\ res = 1; + \\ return; \\ } \\} , &[_][]const u8{ \\pub export fn switch_fn(arg_i: c_int) void { \\ var i = arg_i; \\ var res: c_int = 0; - \\ @"switch": { - \\ case_2: { - \\ default: { - \\ case_1: { - \\ case: { - \\ switch (i) { - \\ @as(c_int, 0) => break :case, - \\ @as(c_int, 1)...@as(c_int, 3) => break :case_1, - \\ else => break :default, - \\ @as(c_int, 4) => break :case_2, - \\ } - \\ } - \\ res = 1; - \\ } - \\ res = 2; - \\ } + \\ switch (i) { + \\ @as(c_int, 0) => { + \\ res = 1; + \\ res = 2; \\ res = @as(c_int, 3) * i; - \\ break :@"switch"; - \\ } - \\ res = 5; + \\ }, + \\ @as(c_int, 1)...@as(c_int, 3) => { + \\ res = 2; + \\ res = @as(c_int, 3) * i; + \\ }, + \\ else => { + \\ res = @as(c_int, 3) * i; + \\ }, + \\ @as(c_int, 4), @as(c_int, 5) => { + \\ res = 69; + \\ { + \\ res = 5; + \\ return; + \\ } + \\ }, + \\ @as(c_int, 6) => { + \\ res = 1; + \\ return; + \\ }, \\ } \\} }); From df5a8120df640de900667624ad8390394f99521f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 18 Feb 2021 21:34:31 +0200 Subject: [PATCH 34/36] translate-c: small miscellaneous improvements --- src/translate_c.zig | 38 ++++++++++++++-------- src/translate_c/ast.zig | 19 +++++++---- test/translate_c.zig | 72 +++++++++++++++++++++++++++++++++-------- 3 files changed, 95 insertions(+), 34 deletions(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 47c601677e..541b854da7 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1219,8 +1219,10 @@ fn transCompoundStmtInline( const end_it = stmt.body_end(); while (it != end_it) : (it += 1) { const result = try transStmt(c, &block.base, it[0], .unused); - if (result.tag() == .declaration) continue; - try block.statements.append(result); + switch (result.tag()) { + .declaration, .empty_block => {}, + else => try block.statements.append(result), + } } } @@ -1395,6 +1397,10 @@ fn transImplicitCastExpr( .BuiltinFnToFnPtr => { return transExpr(c, scope, sub_expr, result_used); }, + .ToVoid => { + // Should only appear in the rhs and lhs of a ConditionalOperator + return transExpr(c, scope, sub_expr, .unused); + }, else => |kind| return fail( c, error.UnsupportedTranslation, @@ -2032,10 +2038,8 @@ fn transZeroInitExpr( typedef_decl.getUnderlyingType().getTypePtr(), ); }, - else => {}, + else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)), } - - return fail(c, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}); } fn transImplicitValueInitExpr( @@ -2118,7 +2122,7 @@ fn transDoWhileLoop( defer cond_scope.deinit(); const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); const if_not_break = switch (cond.tag()) { - .false_literal => Tag.@"break".init(), + .false_literal => return transStmt(c, scope, stmt.getBody(), .unused), .true_literal => { const body_node = try transStmt(c, scope, stmt.getBody(), .unused); return Tag.while_true.create(c.arena, body_node); @@ -2396,8 +2400,10 @@ fn transSwitchProngStmtInline( }, else => { const result = try transStmt(c, &block.base, it[0], .unused); - if (result.tag() == .declaration) continue; - try block.statements.append(result); + switch (result.tag()) { + .declaration, .empty_block => {}, + else => try block.statements.append(result), + } }, } } @@ -2479,8 +2485,10 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: const end_it = comp.body_end(); while (it != end_it - 1) : (it += 1) { const result = try transStmt(c, &block_scope.base, it[0], .unused); - if (result.tag() == .declaration) continue; - try block_scope.statements.append(result); + switch (result.tag()) { + .declaration, .empty_block => {}, + else => try block_scope.statements.append(result), + } } const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, @@ -3126,12 +3134,12 @@ fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.Condi const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); - var then_body = try transExpr(c, scope, true_expr, .used); + var then_body = try transExpr(c, scope, true_expr, used); if (!res_is_bool and isBoolRes(then_body)) { then_body = try Tag.bool_to_int.create(c.arena, then_body); } - var else_body = try transExpr(c, scope, false_expr, .used); + var else_body = try transExpr(c, scope, false_expr, used); if (!res_is_bool and isBoolRes(else_body)) { else_body = try Tag.bool_to_int.create(c.arena, else_body); } @@ -3141,7 +3149,8 @@ fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.Condi .then = then_body, .@"else" = else_body, }); - return maybeSuppressResult(c, scope, used, if_node); + // Clang inserts ImplicitCast(ToVoid)'s to both rhs and lhs so we don't need to supress the result here. + return if_node; } fn maybeSuppressResult( @@ -4794,7 +4803,8 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (true) { switch (m.next().?) { .Asterisk => { - if (m.peek().? == .RParen) { + const next = m.peek().?; + if (next == .RParen or next == .Nl or next == .Eof) { // type *) // last token of `node` diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 2306d1c36f..be331f39dc 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1891,30 +1891,35 @@ fn addSemicolonIfNeeded(c: *Context, node: Node) !void { .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .block_single, .@"switch" => {}, .while_true => { const payload = node.castTag(.while_true).?.data; - return addSemicolonIfNotBlock(c, payload); + return addSemicolonIfNotBlock(c, payload, .yes_if); }, .@"while" => { const payload = node.castTag(.@"while").?.data; - return addSemicolonIfNotBlock(c, payload.body); + return addSemicolonIfNotBlock(c, payload.body, .yes_if); }, .@"if" => { const payload = node.castTag(.@"if").?.data; if (payload.@"else") |some| - return addSemicolonIfNotBlock(c, some); - return addSemicolonIfNotBlock(c, payload.then); + return addSemicolonIfNotBlock(c, some, .no_if); + return addSemicolonIfNotBlock(c, payload.then, .no_if); }, else => _ = try c.addToken(.semicolon, ";"), } } -fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { +fn addSemicolonIfNotBlock(c: *Context, node: Node, if_needs_semicolon: enum{ yes_if, no_if}) !void { switch (node.tag()) { .block, .empty_block, .block_single => {}, .@"if" => { + if (if_needs_semicolon == .yes_if) { + _ = try c.addToken(.semicolon, ";"); + return; + } + const payload = node.castTag(.@"if").?.data; if (payload.@"else") |some| - return addSemicolonIfNotBlock(c, some); - return addSemicolonIfNotBlock(c, payload.then); + return addSemicolonIfNotBlock(c, some, .no_if); + return addSemicolonIfNotBlock(c, payload.then, .no_if); }, else => _ = try c.addToken(.semicolon, ";"), } diff --git a/test/translate_c.zig b/test/translate_c.zig index a2d1bf417e..34e6897c94 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,62 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("if as while stmt has semicolon", + \\void foo() { + \\ while (1) if (1) { + \\ int a = 1; + \\ } else { + \\ int b = 2; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ while (true) if (true) { + \\ var a: c_int = 1; + \\ } else { + \\ var b: c_int = 2; + \\ }; + \\} + }); + + cases.add("conditional operator cast to void", + \\int bar(); + \\void foo() { + \\ int a; + \\ a ? a = 2 : bar(); + \\} + , &[_][]const u8{ + \\pub extern fn bar(...) c_int; + \\pub export fn foo() void { + \\ var a: c_int = undefined; + \\ if (a != 0) a = 2 else _ = bar(); + \\} + }); + + cases.add("struct in struct init to zero", + \\struct Foo { + \\ int a; + \\ struct Bar { + \\ int a; + \\ } b; + \\} a = {}; + \\#define PTR void * + , &[_][]const u8{ + \\pub const struct_Bar = extern struct { + \\ a: c_int, + \\}; + \\pub const struct_Foo = extern struct { + \\ a: c_int, + \\ b: struct_Bar, + \\}; + \\pub export var a: struct_Foo = struct_Foo{ + \\ .a = 0, + \\ .b = @import("std").mem.zeroes(struct_Bar), + \\}; + , + \\pub const PTR = ?*c_void; + }); + cases.add("scoped enum", \\void foo() { \\ enum Foo { @@ -330,9 +386,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn foo() void { \\ while (false) while (false) {}; \\ while (true) while (false) {}; - \\ while (true) while (true) { - \\ break; - \\ }; + \\ while (true) {} \\} }); @@ -1044,13 +1098,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ ;;;;; \\} , &[_][]const u8{ - \\pub export fn foo() void { - \\ {} - \\ {} - \\ {} - \\ {} - \\ {} - \\} + \\pub export fn foo() void {} }); if (std.Target.current.os.tag != .windows) { @@ -3050,9 +3098,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub fn foo() callconv(.C) void { - \\ if (true) while (true) { - \\ break; - \\ }; + \\ if (true) {} \\} }); From 3aba1fa04fe633f89e05a7adc4c170d400705bf9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 19 Feb 2021 01:52:27 +0200 Subject: [PATCH 35/36] translate-c: ensure failed macros don't get defined multiple times --- src/translate_c.zig | 2 +- test/run_translated_c.zig | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/translate_c.zig b/src/translate_c.zig index 541b854da7..f29dfccfa3 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -4025,7 +4025,7 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti // location // pub const name = @compileError(msg); const fail_msg = try std.fmt.allocPrint(c.arena, format, args); - try c.global_scope.nodes.append(try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg })); + try addTopLevelDecl(c, name, try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg })); const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)}); try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment)); } diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 04095d3b71..2db80c9c6a 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -3,6 +3,12 @@ const tests = @import("tests.zig"); const nl = std.cstr.line_sep; pub fn addCases(cases: *tests.RunTranslatedCContext) void { + cases.add("failed macros are only declared once", + \\#define FOO = + \\#define FOO = + \\int main(void) {} + , ""); + cases.add("parenthesized string literal", \\void foo(const char *s) {} \\int main(void) { From 974a1c55255560318f1d99003d353374b94b63e1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 19 Feb 2021 12:50:42 +0200 Subject: [PATCH 36/36] translate-c: small fixes to avoid generating invalid code for macros --- src/translate_c/ast.zig | 19 +++++++++++++++---- test/run_translated_c.zig | 2 ++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index be331f39dc..34498e7315 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -677,7 +677,19 @@ pub fn render(gpa: *Allocator, nodes: []const Node) !std.zig.ast.Tree { .rhs = undefined, }, }); - const root_members = try renderNodes(&ctx, nodes); + + const root_members = blk: { + var result = std.ArrayList(NodeIndex).init(gpa); + defer result.deinit(); + + for (nodes) |node| { + const res = try renderNode(&ctx, node); + if (node.tag() == .warning) continue; + try result.append(res); + } + break :blk try ctx.listToSpan(result.items); + }; + ctx.nodes.items(.data)[0] = .{ .lhs = root_members.start, .rhs = root_members.end, @@ -762,7 +774,6 @@ fn renderNodes(c: *Context, nodes: []const Node) Allocator.Error!NodeSubRange { for (nodes) |node| { const res = try renderNode(c, node); if (node.tag() == .warning) continue; - if (c.nodes.items(.tag)[res] == .identifier) continue; // TODO remove try result.append(res); } @@ -1176,7 +1187,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }; if (payload.is_const) _ = try c.addToken(.keyword_const, "const"); if (payload.is_volatile) _ = try c.addToken(.keyword_volatile, "volatile"); - const elem_type = try renderNode(c, payload.elem_type); + const elem_type = try renderNodeGrouped(c, payload.elem_type); return c.addNode(.{ .tag = .ptr_type_aligned, @@ -2447,7 +2458,7 @@ fn renderMacroFunc(c: *Context, node: Node) !NodeIndex { _ = try c.addToken(.r_paren, ")"); break :blk res; }; - const return_type_expr = try renderNode(c, payload.return_type); + const return_type_expr = try renderNodeGrouped(c, payload.return_type); const fn_proto = try blk: { if (params.items.len < 2) diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 2db80c9c6a..0b72ed2926 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -6,6 +6,8 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { cases.add("failed macros are only declared once", \\#define FOO = \\#define FOO = + \\#define PtrToPtr64(p) ((void *POINTER_64) p) + \\#define STRUC_ALIGNED_STACK_COPY(t,s) ((CONST t *)(s)) \\int main(void) {} , "");