Commit c616141241
Changed files (5)
lib
std
src
translate_c
test
lib/std/zig/c_translation.zig
@@ -40,6 +40,17 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
.Fn => {
return castInt(DestType, @ptrToInt(&target));
},
+ .Bool => {
+ return @boolToInt(target);
+ },
+ else => {},
+ }
+ },
+ .Float => {
+ switch (@typeInfo(SourceType)) {
+ .Int => return @intToFloat(DestType, target),
+ .Float => return @floatCast(DestType, target),
+ .Bool => return @intToFloat(DestType, @boolToInt(target)),
else => {},
}
},
@@ -446,6 +457,121 @@ pub const Macros = struct {
}
};
+/// Integer promotion described in C11 6.3.1.1.2
+fn PromotedIntType(comptime T: type) type {
+ return switch (T) {
+ bool, u8, i8, c_short => c_int,
+ c_ushort => if (@sizeOf(c_ushort) == @sizeOf(c_int)) c_uint else c_int,
+ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong => T,
+ else => if (T == comptime_int) {
+ @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a fixed-size number type is required");
+ } else if (@typeInfo(T) == .Int) {
+ @compileError("Cannot promote `" ++ @typeName(T) ++ "`; a C ABI type is required");
+ } else {
+ @compileError("Attempted to promote invalid type `" ++ @typeName(T) ++ "`");
+ },
+ };
+}
+
+/// C11 6.3.1.1.1
+fn integerRank(comptime T: type) u8 {
+ return switch (T) {
+ bool => 0,
+ u8, i8 => 1,
+ c_short, c_ushort => 2,
+ c_int, c_uint => 3,
+ c_long, c_ulong => 4,
+ c_longlong, c_ulonglong => 5,
+ else => @compileError("integer rank not supported for `" ++ @typeName(T) ++ "`"),
+ };
+}
+
+fn ToUnsigned(comptime T: type) type {
+ return switch (T) {
+ c_int => c_uint,
+ c_long => c_ulong,
+ c_longlong => c_ulonglong,
+ else => @compileError("Cannot convert `" ++ @typeName(T) ++ "` to unsigned"),
+ };
+}
+
+/// "Usual arithmetic conversions" from C11 standard 6.3.1.8
+fn ArithmeticConversion(comptime A: type, comptime B: type) type {
+ if (A == c_longdouble or B == c_longdouble) return c_longdouble;
+ if (A == f80 or B == f80) return f80;
+ if (A == f64 or B == f64) return f64;
+ if (A == f32 or B == f32) return f32;
+
+ const A_Promoted = PromotedIntType(A);
+ const B_Promoted = PromotedIntType(B);
+ comptime {
+ std.debug.assert(integerRank(A_Promoted) >= integerRank(c_int));
+ std.debug.assert(integerRank(B_Promoted) >= integerRank(c_int));
+ }
+
+ if (A_Promoted == B_Promoted) return A_Promoted;
+
+ const a_signed = @typeInfo(A_Promoted).Int.signedness == .signed;
+ const b_signed = @typeInfo(B_Promoted).Int.signedness == .signed;
+
+ if (a_signed == b_signed) {
+ return if (integerRank(A_Promoted) > integerRank(B_Promoted)) A_Promoted else B_Promoted;
+ }
+
+ const SignedType = if (a_signed) A_Promoted else B_Promoted;
+ const UnsignedType = if (!a_signed) A_Promoted else B_Promoted;
+
+ if (integerRank(UnsignedType) >= integerRank(SignedType)) return UnsignedType;
+
+ if (std.math.maxInt(SignedType) >= std.math.maxInt(UnsignedType)) return SignedType;
+
+ return ToUnsigned(SignedType);
+}
+
+test "ArithmeticConversion" {
+ // Promotions not necessarily the same for other platforms
+ if (builtin.target.cpu.arch != .x86_64 or builtin.target.os.tag != .linux) return error.SkipZigTest;
+
+ const Test = struct {
+ /// Order of operands should not matter for arithmetic conversions
+ fn checkPromotion(comptime A: type, comptime B: type, comptime Expected: type) !void {
+ try std.testing.expect(ArithmeticConversion(A, B) == Expected);
+ try std.testing.expect(ArithmeticConversion(B, A) == Expected);
+ }
+ };
+
+ try Test.checkPromotion(c_longdouble, c_int, c_longdouble);
+ try Test.checkPromotion(c_int, f64, f64);
+ try Test.checkPromotion(f32, bool, f32);
+
+ try Test.checkPromotion(bool, c_short, c_int);
+ try Test.checkPromotion(c_int, c_int, c_int);
+ try Test.checkPromotion(c_short, c_int, c_int);
+
+ try Test.checkPromotion(c_int, c_long, c_long);
+
+ try Test.checkPromotion(c_ulonglong, c_uint, c_ulonglong);
+
+ try Test.checkPromotion(c_uint, c_int, c_uint);
+
+ try Test.checkPromotion(c_uint, c_long, c_long);
+
+ try Test.checkPromotion(c_ulong, c_longlong, c_ulonglong);
+}
+
+pub const MacroArithmetic = struct {
+ pub fn div(a: anytype, b: anytype) ArithmeticConversion(@TypeOf(a), @TypeOf(b)) {
+ const ResType = ArithmeticConversion(@TypeOf(a), @TypeOf(b));
+ const a_casted = cast(ResType, a);
+ const b_casted = cast(ResType, b);
+ switch (@typeInfo(ResType)) {
+ .Float => return a_casted / b_casted,
+ .Int => return @divTrunc(a_casted, b_casted),
+ else => unreachable,
+ }
+ }
+};
+
test "Macro suffix functions" {
try testing.expect(@TypeOf(Macros.F_SUFFIX(1)) == f32);
src/translate_c/ast.zig
@@ -159,6 +159,9 @@ pub const Node = extern union {
/// @shuffle(type, a, b, mask)
shuffle,
+ /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)
+ macro_arithmetic,
+
asm_simple,
negate,
@@ -370,6 +373,7 @@ pub const Node = extern union {
.field_access => Payload.FieldAccess,
.string_slice => Payload.StringSlice,
.shuffle => Payload.Shuffle,
+ .macro_arithmetic => Payload.MacroArithmetic,
};
}
@@ -713,6 +717,19 @@ pub const Payload = struct {
mask_vector: Node,
},
};
+
+ pub const MacroArithmetic = struct {
+ base: Payload,
+ data: struct {
+ op: Operator,
+ lhs: Node,
+ rhs: Node,
+ },
+
+ pub const Operator = enum {
+ div,
+ };
+ };
};
/// Converts the nodes into a Zig Ast.
@@ -1408,6 +1425,12 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
payload.mask_vector,
});
},
+ .macro_arithmetic => {
+ const payload = node.castTag(.macro_arithmetic).?.data;
+ const op = @tagName(payload.op);
+ const import_node = try renderStdImport(c, &.{ "zig", "c_translation", "MacroArithmetic", op });
+ return renderCall(c, import_node, &.{ payload.lhs, payload.rhs });
+ },
.alignof => {
const payload = node.castTag(.alignof).?.data;
return renderBuiltinCall(c, "@alignOf", &.{payload});
@@ -2349,6 +2372,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.shuffle,
.static_local_var,
.mut_str,
+ .macro_arithmetic,
=> {
// no grouping needed
return renderNode(c, node);
src/translate_c.zig
@@ -6232,7 +6232,7 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
.Slash => {
const lhs = try macroBoolToInt(c, node);
const rhs = try macroBoolToInt(c, try parseCCastExpr(c, m, scope));
- node = try Tag.div.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
+ node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .div, .lhs = lhs, .rhs = rhs });
},
.Percent => {
const lhs = try macroBoolToInt(c, node);
test/behavior/translate_c_macros.h
@@ -53,3 +53,6 @@ typedef _Bool uintptr_t;
#define LARGE_INT 18446744073709550592
#define EMBEDDED_TAB "hello "
+
+#define DIVIDE_CONSTANT(version) (version / 1000)
+#define DIVIDE_ARGS(A, B) (A / B)
test/behavior/translate_c_macros.zig
@@ -147,3 +147,37 @@ test "string and char literals that are not UTF-8 encoded. Issue #12784" {
try expectEqual(@as(u8, '\xA9'), latin1.UNPRINTABLE_CHAR);
try expectEqualStrings("\xA9\xA9\xA9", latin1.UNPRINTABLE_STRING);
}
+
+test "Macro that uses division operator. Issue #13162" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ try expectEqual(@as(c_int, 42), h.DIVIDE_CONSTANT(@as(c_int, 42_000)));
+ try expectEqual(@as(c_uint, 42), h.DIVIDE_CONSTANT(@as(c_uint, 42_000)));
+
+ try expectEqual(
+ @as(f64, 42.0),
+ h.DIVIDE_ARGS(
+ @as(f64, 42.0),
+ true,
+ ),
+ );
+ try expectEqual(
+ @as(c_int, 21),
+ h.DIVIDE_ARGS(
+ @as(i8, 42),
+ @as(i8, 2),
+ ),
+ );
+
+ try expectEqual(
+ @as(c_int, 21),
+ h.DIVIDE_ARGS(
+ @as(c_ushort, 42),
+ @as(c_ushort, 2),
+ ),
+ );
+}