Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

transCastExpr

Translator.transCastExpr
fn transCastExpr(
    t: *Translator,
    scope: *Scope,
    cast: Node.Cast,
    dest_qt: QualType,
    used: ResultUsed,
    suppress_as: SuppressCast,
) TransError!ZigNode

File

lib/compiler/translate-c/Translator.zig:2463

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 => {},
            }

            // Flexible array members are translated as member functions returning
            // [*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);

            // Special case function pointers as @intFromPtr(expr) != 0
            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;
}