feature. See also
. The project being documented here (as the example) is the Zig library itself.
Translator.transCastExpr
fn transCastExpr(
t: *Translator,
scope: *Scope,
cast: Node.Cast,
dest_qt: QualType,
used: ResultUsed,
suppress_as: SuppressCast,
) TransError!ZigNode
File
Code
fn transCastExpr(
t: *Translator,
scope: *Scope,
cast: Node.Cast,
dest_qt: QualType,
used: ResultUsed,
suppress_as: SuppressCast,
) TransError!ZigNode {
const operand = switch (cast.kind) {
.no_op => {
const operand = cast.operand.get(t.tree);
if (operand == .cast) {
return t.transCastExpr(scope, operand.cast, cast.qt, used, suppress_as);
}
return t.transExpr(scope, cast.operand, used);
},
.lval_to_rval, .function_to_pointer => {
return t.transExpr(scope, cast.operand, used);
},
.int_cast => int_cast: {
const src_qt = cast.operand.qt(t.tree);
if (cast.implicit) {
if (t.tree.value_map.get(cast.operand)) |val| {
const max_int = try aro.Value.maxInt(dest_qt, t.comp);
const min_int = try aro.Value.minInt(dest_qt, t.comp);
if (val.compare(.lte, max_int, t.comp) and val.compare(.gte, min_int, t.comp)) {
break :int_cast try t.transExprCoercing(scope, cast.operand, .used);
}
}
}
const operand = try t.transExpr(scope, cast.operand, .used);
break :int_cast try t.transIntCast(operand, src_qt, dest_qt);
},
.to_void => {
assert(used == .unused);
return try t.transExpr(scope, cast.operand, .unused);
},
.null_to_pointer => ZigTag.null_literal.init(),
.array_to_pointer => array_to_pointer: {
const child_qt = dest_qt.childType(t.comp);
loop: switch (cast.operand.get(t.tree)) {
.string_literal_expr => |literal| {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const ref = if (literal.kind == .utf8 or literal.kind == .ascii)
sub_expr_node
else
try ZigTag.address_of.create(t.arena, sub_expr_node);
const casted = if (child_qt.@"const")
ref
else
try ZigTag.const_cast.create(t.arena, sub_expr_node);
return t.maybeSuppressResult(used, casted);
},
.paren_expr => |paren_expr| {
continue :loop paren_expr.operand.get(t.tree);
},
.generic_expr => |generic| {
continue :loop generic.chosen.get(t.tree);
},
.generic_association_expr => |generic| {
continue :loop generic.expr.get(t.tree);
},
.generic_default_expr => |generic| {
continue :loop generic.expr.get(t.tree);
},
else => {},
}
// [*c]T, so no address-of + @ptrCast wrapping is needed.
flexible: {
if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) {
return try t.transExpr(scope, cast.operand, used);
}
const member_index, const base_qt = switch (cast.operand.get(t.tree)) {
.member_access_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree) },
.member_access_ptr_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree).childType(t.comp) },
else => break :flexible,
};
const record = base_qt.getRecord(t.comp) orelse break :flexible;
if (member_index != record.fields.len - 1 and base_qt.base(t.comp).type != .@"union") break :flexible;
const array_ty = record.fields[member_index].qt.get(t.comp, .array) orelse break :flexible;
if (t.isFlexibleArrayLen(array_ty.len)) return try t.transExpr(scope, cast.operand, used);
}
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const ref = try ZigTag.address_of.create(t.arena, sub_expr_node);
const align_cast = try ZigTag.align_cast.create(t.arena, ref);
break :array_to_pointer try ZigTag.ptr_cast.create(t.arena, align_cast);
},
.int_to_pointer => int_to_pointer: {
var sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const operand_qt = cast.operand.qt(t.tree);
if (t.signedness(operand_qt) == .signed or operand_qt.bitSizeof(t.comp) > t.comp.target.ptrBitWidth()) {
sub_expr_node = try ZigTag.as.create(t.arena, .{
.lhs = try ZigTag.type.create(t.arena, "usize"),
.rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node),
});
} else if (sub_expr_node.isBoolRes()) {
sub_expr_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
}
break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);
},
.int_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
if (sub_expr_node.isBoolRes()) return sub_expr_node;
if (cast.operand.qt(t.tree).is(t.comp, .bool)) return sub_expr_node;
const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
return t.maybeSuppressResult(used, cmp_node);
},
.float_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
return t.maybeSuppressResult(used, cmp_node);
},
.pointer_to_bool => {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
if (cast.operand.qt(t.tree).get(t.comp, .pointer)) |ptr_ty| if (ptr_ty.child.is(t.comp, .func)) {
const ptr_node = if (sub_expr_node.tag() == .identifier)
try ZigTag.address_of.create(t.arena, sub_expr_node)
else
sub_expr_node;
const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, ptr_node);
const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
return t.maybeSuppressResult(used, cmp_node);
};
const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.null_literal.init() });
return t.maybeSuppressResult(used, cmp_node);
},
.bool_to_int => bool_to_int: {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
break :bool_to_int try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
},
.bool_to_float => bool_to_float: {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
break :bool_to_float try ZigTag.float_from_int.create(t.arena, int_from_bool);
},
.bool_to_pointer => bool_to_pointer: {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
break :bool_to_pointer try ZigTag.ptr_from_int.create(t.arena, int_from_bool);
},
.float_cast => float_cast: {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
break :float_cast try ZigTag.float_cast.create(t.arena, sub_expr_node);
},
.int_to_float => int_to_float: {
const sub_expr_node = try t.transExpr(scope, cast.operand, used);
const int_node = if (sub_expr_node.isBoolRes())
try ZigTag.int_from_bool.create(t.arena, sub_expr_node)
else
sub_expr_node;
break :int_to_float try ZigTag.float_from_int.create(t.arena, int_node);
},
.float_to_int => float_to_int: {
const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
break :float_to_int try ZigTag.int_from_float.create(t.arena, sub_expr_node);
},
.pointer_to_int => pointer_to_int: {
const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
const ptr_node = try ZigTag.int_from_ptr.create(t.arena, sub_expr_node);
break :pointer_to_int try ZigTag.int_cast.create(t.arena, ptr_node);
},
.bitcast => bitcast: {
const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
const operand_qt = cast.operand.qt(t.tree);
if (dest_qt.isPointer(t.comp) and operand_qt.isPointer(t.comp)) {
var casted = try ZigTag.align_cast.create(t.arena, sub_expr_node);
casted = try ZigTag.ptr_cast.create(t.arena, casted);
const src_elem = operand_qt.childType(t.comp);
const dest_elem = dest_qt.childType(t.comp);
if ((src_elem.@"const" or src_elem.is(t.comp, .func)) and !dest_elem.@"const") {
casted = try ZigTag.const_cast.create(t.arena, casted);
}
if (src_elem.@"volatile" and !dest_elem.@"volatile") {
casted = try ZigTag.volatile_cast.create(t.arena, casted);
}
break :bitcast casted;
}
break :bitcast try ZigTag.bit_cast.create(t.arena, sub_expr_node);
},
.union_cast => union_cast: {
const union_type = try t.transType(scope, dest_qt, cast.l_paren);
const operand_qt = cast.operand.qt(t.tree);
const union_base = dest_qt.base(t.comp);
const field = for (union_base.type.@"union".fields) |field| {
if (field.qt.eql(operand_qt, t.comp)) break field;
} else unreachable;
const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
.parent = union_base.qt,
.field = field.qt,
}).? else field.name.lookup(t.comp);
const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
field_init.* = .{
.name = field_name,
.value = try t.transExpr(scope, cast.operand, .used),
};
break :union_cast try ZigTag.container_init.create(t.arena, .{
.lhs = union_type,
.inits = field_init[0..1],
});
},
else => return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)}),
};
if (suppress_as == .no_as) return t.maybeSuppressResult(used, operand);
if (used == .unused) return t.maybeSuppressResult(used, operand);
const as = try ZigTag.as.create(t.arena, .{
.lhs = try t.transType(scope, dest_qt, cast.l_paren),
.rhs = operand,
});
return as;
}