Add boolean and, boolean or binary ops in translate-c-2

This commit is contained in:
Merlyn Morgan-Graham 2019-12-16 01:33:27 -08:00
parent acff2d407b
commit 89ef635b35
3 changed files with 503 additions and 31 deletions

View File

@ -967,6 +967,16 @@ pub extern fn ZigClangDeclRefExpr_getDecl(*const ZigClangDeclRefExpr) *const Zig
pub extern fn ZigClangParenType_getInnerType(*const ZigClangParenType) ZigClangQualType;
pub extern fn ZigClangElaboratedType_getNamedType(*const ZigClangElaboratedType) ZigClangQualType;
pub extern fn ZigClangElaboratedType_getKeyword(*const ZigClangElaboratedType) ZigClangElaboratedTypeKeyword;
pub const ZigClangElaboratedTypeKeyword = extern enum {
Struct,
Interface,
Union,
Class,
Enum,
Typename,
None,
};
pub extern fn ZigClangAttributedType_getEquivalentType(*const ZigClangAttributedType) ZigClangQualType;

View File

@ -592,14 +592,14 @@ fn transCreateNodeShiftOp(
const rhs_location = ZigClangExpr_getBeginLoc(rhs_expr);
// lhs >> u5(rh)
const lhs = try transExpr(rp, scope, lhs_expr, .used, .l_value);
const lhs = try transExpr(rp, scope, lhs_expr, .used, .r_value);
const op_token = try appendToken(rp.c, op_tok_id, bytes);
const as_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
const rhs_type = try qualTypeToLog2IntRef(rp, ZigClangBinaryOperator_getType(stmt), rhs_location);
try as_node.params.push(rhs_type);
_ = try appendToken(rp.c, .Comma, ",");
const rhs = try transExpr(rp, scope, rhs_expr, .used, .l_value);
const rhs = try transExpr(rp, scope, rhs_expr, .used, .r_value);
try as_node.params.push(rhs.node);
as_node.rparen_token = try appendToken(rp.c, .RParen, ")");
@ -672,7 +672,7 @@ fn transBinaryOperator(
if (!cIsUnsignedInteger(qt)) {
// signed integer division uses @divTrunc
const div_trunc_node = try transCreateNodeBuiltinFnCall(rp.c, "@divTrunc");
const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value);
try div_trunc_node.params.push(lhs.node);
_ = try appendToken(rp.c, .Comma, ",");
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
@ -697,7 +697,7 @@ fn transBinaryOperator(
if (!cIsUnsignedInteger(qt)) {
// signed integer division uses @rem
const rem_node = try transCreateNodeBuiltinFnCall(rp.c, "@rem");
const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value);
try rem_node.params.push(lhs.node);
_ = try appendToken(rp.c, .Comma, ",");
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
@ -806,10 +806,23 @@ fn transBinaryOperator(
.node_scope = scope,
});
},
.LAnd,
.LOr,
.Comma,
=> return revertAndWarn(
.LAnd => {
const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolAnd, .Keyword_and, "and");
return maybeSuppressResult(rp, scope, result_used, TransResult{
.node = node,
.child_scope = scope,
.node_scope = scope,
});
},
.LOr => {
const node = try transCreateNodeBoolInfixOp(rp, scope, stmt, .BoolOr, .Keyword_or, "or");
return maybeSuppressResult(rp, scope, result_used, TransResult{
.node = node,
.child_scope = scope,
.node_scope = scope,
});
},
.Comma => return revertAndWarn(
rp,
error.UnsupportedTranslation,
ZigClangBinaryOperator_getBeginLoc(stmt),
@ -1040,6 +1053,321 @@ fn transImplicitCastExpr(
}
}
fn toEnumZeroCmp(
rp: RestorePoint,
scope: *Scope,
expr: *ast.Node,
generate_enum_node: fn (RestorePoint, *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node,
enum_ty: *const struct_ZigClangType,
enum_source_loc: ZigClangSourceLocation,
) !*ast.Node {
// expr != @bitCast(EnumType, @as(@TagType(EnumType), 0))
// @bitCast(Enum,
const bitcast = try transCreateNodeBuiltinFnCall(rp.c, "@bitCast");
const bitcast_enum_identifier = try generate_enum_node(rp, enum_ty, enum_source_loc);
try bitcast.params.push(bitcast_enum_identifier);
_ = try appendToken(rp.c, .Comma, ",");
// @as(
const cast_node = try transCreateNodeBuiltinFnCall(rp.c, "@as");
// @TagType(Enum),
const tag_type = try transCreateNodeBuiltinFnCall(rp.c, "@TagType");
const tag_type_enum_identifier = try generate_enum_node(rp, enum_ty, enum_source_loc);
try tag_type.params.push(tag_type_enum_identifier);
tag_type.rparen_token = try appendToken(rp.c, .RParen, ")");
try cast_node.params.push(&tag_type.base);
_ = try appendToken(rp.c, .Comma, ",");
// 0)
const zero = try transCreateNodeInt(rp.c, 0);
try cast_node.params.push(zero);
cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
try bitcast.params.push(&cast_node.base);
bitcast.rparen_token = try appendToken(rp.c, .RParen, ")");
// expr != @bitCast(EnumType, @as(@TagType(EnumType), 0))
return transCreateNodeNotEqual(rp, scope, expr, &bitcast.base);
}
fn transBoolExpr(
rp: RestorePoint,
scope: *Scope,
expr: *const ZigClangExpr,
used: ResultUsed,
lrvalue: LRValue,
) !*ast.Node {
var res = try transExpr(rp, scope, expr, used, lrvalue);
switch (res.node.id) {
.InfixOp => switch (@ptrCast(*const ast.Node.InfixOp, &res.node).op) {
.BoolOr,
.BoolAnd,
.EqualEqual,
.BangEqual,
.LessThan,
.GreaterThan,
.LessOrEqual,
.GreaterOrEqual,
=> return res.node,
else => {},
},
.PrefixOp => switch (@ptrCast(*const ast.Node.PrefixOp, &res.node).op) {
.BoolNot => return res.node,
else => {},
},
.BoolLiteral => return res.node,
else => {},
}
const ty = ZigClangQualType_getTypePtr(getExprQualTypeBeforeImplicitCast(rp.c, expr));
switch (ZigClangType_getTypeClass(ty)) {
.Builtin => {
const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
switch (ZigClangBuiltinType_getKind(builtin_ty)) {
.Bool,
.Char_U,
.UChar,
.Char_S,
.SChar,
.UShort,
.UInt,
.ULong,
.ULongLong,
.Short,
.Int,
.Long,
.LongLong,
.UInt128,
.Int128,
.Float,
.Double,
.Float128,
.LongDouble,
.WChar_U,
.Char8,
.Char16,
.Char32,
.WChar_S,
.Float16,
=> return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeInt(rp.c, 0)),
.NullPtr => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)),
.Void,
.Half,
.ObjCId,
.ObjCClass,
.ObjCSel,
.OMPArraySection,
.Dependent,
.Overload,
.BoundMember,
.PseudoObject,
.UnknownAny,
.BuiltinFn,
.ARCUnbridgedCast,
.OCLImage1dRO,
.OCLImage1dArrayRO,
.OCLImage1dBufferRO,
.OCLImage2dRO,
.OCLImage2dArrayRO,
.OCLImage2dDepthRO,
.OCLImage2dArrayDepthRO,
.OCLImage2dMSAARO,
.OCLImage2dArrayMSAARO,
.OCLImage2dMSAADepthRO,
.OCLImage2dArrayMSAADepthRO,
.OCLImage3dRO,
.OCLImage1dWO,
.OCLImage1dArrayWO,
.OCLImage1dBufferWO,
.OCLImage2dWO,
.OCLImage2dArrayWO,
.OCLImage2dDepthWO,
.OCLImage2dArrayDepthWO,
.OCLImage2dMSAAWO,
.OCLImage2dArrayMSAAWO,
.OCLImage2dMSAADepthWO,
.OCLImage2dArrayMSAADepthWO,
.OCLImage3dWO,
.OCLImage1dRW,
.OCLImage1dArrayRW,
.OCLImage1dBufferRW,
.OCLImage2dRW,
.OCLImage2dArrayRW,
.OCLImage2dDepthRW,
.OCLImage2dArrayDepthRW,
.OCLImage2dMSAARW,
.OCLImage2dArrayMSAARW,
.OCLImage2dMSAADepthRW,
.OCLImage2dArrayMSAADepthRW,
.OCLImage3dRW,
.OCLSampler,
.OCLEvent,
.OCLClkEvent,
.OCLQueue,
.OCLReserveID,
.ShortAccum,
.Accum,
.LongAccum,
.UShortAccum,
.UAccum,
.ULongAccum,
.ShortFract,
.Fract,
.LongFract,
.UShortFract,
.UFract,
.ULongFract,
.SatShortAccum,
.SatAccum,
.SatLongAccum,
.SatUShortAccum,
.SatUAccum,
.SatULongAccum,
.SatShortFract,
.SatFract,
.SatLongFract,
.SatUShortFract,
.SatUFract,
.SatULongFract,
.OCLIntelSubgroupAVCMcePayload,
.OCLIntelSubgroupAVCImePayload,
.OCLIntelSubgroupAVCRefPayload,
.OCLIntelSubgroupAVCSicPayload,
.OCLIntelSubgroupAVCMceResult,
.OCLIntelSubgroupAVCImeResult,
.OCLIntelSubgroupAVCRefResult,
.OCLIntelSubgroupAVCSicResult,
.OCLIntelSubgroupAVCImeResultSingleRefStreamout,
.OCLIntelSubgroupAVCImeResultDualRefStreamout,
.OCLIntelSubgroupAVCImeSingleRefStreamin,
.OCLIntelSubgroupAVCImeDualRefStreamin,
=> return res.node,
}
},
.Pointer => return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeNullLiteral(rp.c)),
.Typedef => {
return transCreateNodeNotEqual(rp, scope, res.node, try transCreateNodeInt(rp.c, 0)); // TODO currently assuming it is like an int/char/bool builtin type. Coerce the type and recurse? Add a toTypedefZeroCmp function?
// TODO This is the code that was in translate-c, but it seems like it is giving wrong results! It just prints the typedef name instead of the value
// const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
// const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
// const typedef_name_decl = ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl);
// const typedef_name = if (rp.c.decl_table.get(@ptrToInt(typedef_name_decl))) |existing_entry|
// existing_entry.value
// else
// try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, typedef_name_decl)));
// return transCreateNodeIdentifier(rp.c, typedef_name);
},
.Enum => {
const gen_enum_decl_node = struct {
// Have to use a callback because node must be generated inline in order to avoid weird AST printing behavior,
// and the code to generate the nodes is a little different for each case
fn generate_node(inner_rp: RestorePoint, enum_ty: *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node {
const actual_enum_ty = @ptrCast(*const ZigClangEnumType, enum_ty);
const enum_decl = ZigClangEnumType_getDecl(actual_enum_ty);
const enum_type = (try transEnumDecl(inner_rp.c, enum_decl)) orelse {
return revertAndWarn(inner_rp, error.UnsupportedType, source_loc, "unable to translate enum declaration", .{});
};
return enum_type;
}
};
return toEnumZeroCmp(rp, scope, res.node, gen_enum_decl_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr));
},
.Elaborated => {
const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty);
switch (ZigClangElaboratedType_getKeyword(elaborated_ty)) {
.Enum => {
// Have to use a callback because node must be generated inline in order to avoid weird AST printing behavior,
// and the code to generate the nodes is a little different for each case
const gen_enum_type_node = struct {
fn generate_node(inner_rp: RestorePoint, enum_ty: *const struct_ZigClangType, source_loc: ZigClangSourceLocation) TransError!*ast.Node {
const inner_elaborated_ty = @ptrCast(*const ZigClangElaboratedType, enum_ty);
const enum_type = try transQualType(inner_rp, ZigClangElaboratedType_getNamedType(inner_elaborated_ty), source_loc);
return enum_type;
}
};
return toEnumZeroCmp(rp, scope, res.node, gen_enum_type_node.generate_node, ty, ZigClangExpr_getBeginLoc(expr));
},
.Struct,
.Union,
.Interface,
.Class,
.Typename,
.None,
=> return res.node,
}
},
.FunctionProto,
.Record,
.ConstantArray,
.Paren,
.Decayed,
.Attributed,
.IncompleteArray,
.BlockPointer,
.LValueReference,
.RValueReference,
.MemberPointer,
.VariableArray,
.DependentSizedArray,
.DependentSizedExtVector,
.Vector,
.ExtVector,
.FunctionNoProto,
.UnresolvedUsing,
.Adjusted,
.TypeOfExpr,
.TypeOf,
.Decltype,
.UnaryTransform,
.TemplateTypeParm,
.SubstTemplateTypeParm,
.SubstTemplateTypeParmPack,
.TemplateSpecialization,
.Auto,
.InjectedClassName,
.DependentName,
.DependentTemplateSpecialization,
.PackExpansion,
.ObjCObject,
.ObjCInterface,
.Complex,
.ObjCObjectPointer,
.Atomic,
.Pipe,
.ObjCTypeParam,
.DeducedTemplateSpecialization,
.DependentAddressSpace,
.DependentVector,
.MacroQualified,
=> return res.node,
}
unreachable;
}
fn transIntegerLiteral(
rp: RestorePoint,
scope: *Scope,
@ -1848,6 +2176,14 @@ fn getExprQualType(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
return ZigClangExpr_getType(expr);
}
fn getExprQualTypeBeforeImplicitCast(c: *Context, expr: *const ZigClangExpr) ZigClangQualType {
if (ZigClangExpr_getStmtClass(expr) == .ImplicitCastExprClass) {
const cast_expr = @ptrCast(*const ZigClangImplicitCastExpr, expr);
return getExprQualType(c, ZigClangImplicitCastExpr_getSubExpr(cast_expr));
}
return ZigClangExpr_getType(expr);
}
fn typeIsOpaque(c: *Context, ty: *const ZigClangType, loc: ZigClangSourceLocation) bool {
switch (ZigClangType_getTypeClass(ty)) {
.Builtin => {
@ -2000,25 +2336,24 @@ fn transCreateNodePrefixOp(
return node;
}
fn transCreateNodeInfixOp(
fn transCreateNodeInfixOpImpl(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangBinaryOperator,
lhs_node: *ast.Node,
rhs_node: *ast.Node,
op: ast.Node.InfixOp.Op,
op_tok_id: std.zig.Token.Id,
bytes: []const u8,
grouped: bool,
) !*ast.Node {
const lparen = if (grouped) try appendToken(rp.c, .LParen, "(") else undefined;
const lhs = try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .l_value);
const op_token = try appendToken(rp.c, op_tok_id, bytes);
const rhs = try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
const node = try rp.c.a().create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.op_token = op_token,
.lhs = lhs.node,
.lhs = lhs_node,
.op = op,
.rhs = rhs.node,
.rhs = rhs_node,
};
if (!grouped) return &node.base;
const rparen = try appendToken(rp.c, .RParen, ")");
@ -2031,6 +2366,60 @@ fn transCreateNodeInfixOp(
return &grouped_expr.base;
}
fn transCreateNodeInfixOp(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangBinaryOperator,
op: ast.Node.InfixOp.Op,
op_tok_id: std.zig.Token.Id,
bytes: []const u8,
grouped: bool,
) !*ast.Node {
return transCreateNodeInfixOpImpl(
rp,
scope,
(try transExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value)).node,
(try transExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value)).node,
op,
op_tok_id,
bytes,
grouped,
);
}
fn transCreateNodeNotEqual(
rp: RestorePoint,
scope: *Scope,
lhs_node: *ast.Node,
rhs_node: *ast.Node,
) !*ast.Node {
return transCreateNodeInfixOpImpl(rp, scope, lhs_node, rhs_node, .BangEqual, .BangEqual, "!=", true);
}
fn transCreateNodeBoolInfixOp(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangBinaryOperator,
comptime op: ast.Node.InfixOp.Op,
comptime op_tok_id: std.zig.Token.Id,
comptime bytes: []const u8,
) !*ast.Node {
if (!(op == .BoolAnd or op == .BoolOr)) {
@compileError("op must be either .BoolAnd or .BoolOr");
}
return transCreateNodeInfixOpImpl(
rp,
scope,
try transBoolExpr(rp, scope, ZigClangBinaryOperator_getLHS(stmt), .used, .r_value),
try transBoolExpr(rp, scope, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value),
op,
op_tok_id,
bytes,
true,
);
}
fn transCreateNodePtrType(
c: *Context,
is_const: bool,

View File

@ -949,28 +949,101 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.addC("logical and, logical or on none bool values",
\\int and_or_none_bool(int a, float b, void *c) {
\\ if (a && b) return 0;
\\ if (b && c) return 1;
\\ if (a && c) return 2;
\\ if (a || b) return 3;
\\ if (b || c) return 4;
\\ if (a || c) return 5;
\\ return 6;
cases.addC("logical and, logical or, on non-bool values", // Note this gets cut off by extra C symbols being injected in middle: `pub const Foo = enum_Foo;`
\\enum Foo {
\\ FooA,
\\ FooB,
\\ FooC,
\\};
\\int and_or_non_bool(int a, float b, void *c) {
\\ enum Foo d = FooA;
\\ int e = (a && b);
\\ int f = (b && c);
\\ int g = (a && c);
\\ int h = (a || b);
\\ int i = (b || c);
\\ int j = (a || c);
\\ int k = (a || d);
\\ int l = (d && b);
\\ int m = (c || d);
\\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m;
\\}
, &[_][]const u8{
\\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0;
\\ if ((b != 0) and (c != null)) return 1;
\\ if ((a != 0) and (c != null)) return 2;
\\ if ((a != 0) or (b != 0)) return 3;
\\ if ((b != 0) or (c != null)) return 4;
\\ if ((a != 0) or (c != null)) return 5;
\\ return 6;
\\pub const FooA = enum_Foo.A;
\\pub const FooB = enum_Foo.B;
\\pub const FooC = enum_Foo.C;
\\pub const enum_Foo = extern enum {
\\ A,
\\ B,
\\ C,
\\};
\\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ var d: enum_Foo = @as(enum_Foo, FooA);
\\ var e: c_int = (a != 0) and (b != 0);
\\ var f: c_int = (b != 0) and (c != null);
\\ var g: c_int = (a != 0) and (c != null);
\\ var h: c_int = (a != 0) or (b != 0);
\\ var i: c_int = (b != 0) or (c != null);
\\ var j: c_int = (a != 0) or (c != null);
\\ var k: c_int = (a != 0) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)));
\\ var l: c_int = (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0);
\\ var m: c_int = (c != null) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0)));
\\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m;
\\}
});
cases.add_2("logical and, logical or, on non-bool values, extra parens",
\\enum Foo {
\\ FooA,
\\ FooB,
\\ FooC,
\\};
\\typedef int SomeTypedef;
\\int and_or_non_bool(int a, float b, void *c) {
\\ enum Foo d = FooA;
\\ int e = (a && b);
\\ int f = (b && c);
\\ int g = (a && c);
\\ int h = (a || b);
\\ int i = (b || c);
\\ int j = (a || c);
\\ int k = (a || d);
\\ int l = (d && b);
\\ int m = (c || d);
\\ SomeTypedef td = 44;
\\ int o = (td || b);
\\ int p = (c && td);
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
\\}
, &[_][]const u8{
\\pub const FooA = enum_Foo.A;
\\pub const FooB = enum_Foo.B;
\\pub const FooC = enum_Foo.C;
\\pub const enum_Foo = extern enum {
\\ A,
\\ B,
\\ C,
\\};
\\pub const SomeTypedef = c_int;
\\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ var d: enum_Foo = @as(enum_Foo, FooA);
\\ var e: c_int = ((a != 0) and (b != 0));
\\ var f: c_int = ((b != 0) and (c != null));
\\ var g: c_int = ((a != 0) and (c != null));
\\ var h: c_int = ((a != 0) or (b != 0));
\\ var i: c_int = ((b != 0) or (c != null));
\\ var j: c_int = ((a != 0) or (c != null));
\\ var k: c_int = ((a != 0) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))));
\\ var l: c_int = ((@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0));
\\ var m: c_int = ((c != null) or (@as(c_int, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))));
\\ var td: SomeTypedef = 44;
\\ var o: c_int = ((td != 0) or (b != 0));
\\ var p: c_int = ((c != null) and (td != 0));
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
\\}
\\pub const Foo = enum_Foo;
});
cases.addC("assign",
\\int max(int a) {
\\ int tmp;