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.

transExpr

Translator.transExpr
fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode

File

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

Code

fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
    const qt = expr.qt(t.tree);
    return t.maybeSuppressResult(used, switch (expr.get(t.tree)) {
        .paren_expr => |paren_expr| {
            return t.transExpr(scope, paren_expr.operand, used);
        },
        .cast => |cast| return t.transCastExpr(scope, cast, cast.qt, used, .with_as),
        .decl_ref_expr => |decl_ref| try t.transDeclRefExpr(scope, decl_ref),
        .enumeration_ref => |enum_ref| try t.transDeclRefExpr(scope, enum_ref),
        .addr_of_expr => |addr_of_expr| try ZigTag.address_of.create(t.arena, try t.transExpr(scope, addr_of_expr.operand, .used)),
        .deref_expr => |deref_expr| res: {
            if (t.typeWasDemotedToOpaque(qt))
                return t.fail(error.UnsupportedTranslation, deref_expr.op_tok, "cannot dereference opaque type", .{});

            // Dereferencing a function pointer is a no-op.
            if (qt.is(t.comp, .func)) return t.transExpr(scope, deref_expr.operand, used);

            break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used));
        },
        .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)),
        .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, op: {
            const operand = try t.transExpr(scope, bit_not_expr.operand, .used);
            if (!operand.isBoolRes()) break :op operand;

            const casted = try ZigTag.int_from_bool.create(t.arena, operand);
            const ty = try t.transType(scope, bit_not_expr.qt, bit_not_expr.op_tok);
            break :op try ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = casted });
        }),
        .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used),
        .negate_expr => |negate_expr| res: {
            const operand_qt = negate_expr.operand.qt(t.tree);
            if (!t.typeHasWrappingOverflow(operand_qt)) {
                const sub_expr_node = try t.transExpr(scope, negate_expr.operand, .used);
                const to_negate = if (sub_expr_node.isBoolRes()) blk: {
                    const ty_node = try ZigTag.type.create(t.arena, "c_int");
                    const int_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
                    break :blk try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_node });
                } else sub_expr_node;

                break :res try ZigTag.negate.create(t.arena, to_negate);
            } else if (t.signedness(operand_qt) == .unsigned) {
                // use -% x for unsigned integers
                break :res try ZigTag.negate_wrap.create(t.arena, try t.transExpr(scope, negate_expr.operand, .used));
            } else return t.fail(error.UnsupportedTranslation, negate_expr.op_tok, "C negation with non float non integer", .{});
        },
        .div_expr => |div_expr| res: {
            if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
                // signed integer division uses @divTrunc
                const lhs = try t.transExpr(scope, div_expr.lhs, .used);
                const rhs = try t.transExpr(scope, div_expr.rhs, .used);
                break :res try ZigTag.div_trunc.create(t.arena, .{ .lhs = lhs, .rhs = rhs });
            }
            // unsigned/float division uses the operator
            break :res try t.transBinExpr(scope, div_expr, .div);
        },
        .mod_expr => |mod_expr| res: {
            if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
                // signed integer remainder uses __helpers.signedRemainder
                const lhs = try t.transExpr(scope, mod_expr.lhs, .used);
                const rhs = try t.transExpr(scope, mod_expr.rhs, .used);
                break :res try t.createHelperCallNode(.signedRemainder, &.{ lhs, rhs });
            }
            // unsigned/float division uses the operator
            break :res try t.transBinExpr(scope, mod_expr, .mod);
        },
        .add_expr => |add_expr| res: {
            // `ptr + idx` and `idx + ptr` -> ptr + @as(usize, @bitCast(@as(isize, @intCast(idx))))
            const lhs_qt = add_expr.lhs.qt(t.tree);
            const rhs_qt = add_expr.rhs.qt(t.tree);
            if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
                t.signedness(rhs_qt) == .signed))
            {
                break :res try t.transPointerArithmeticSignedOp(scope, add_expr, .add);
            }

            if (t.signedness(qt) == .unsigned) {
                break :res try t.transBinExpr(scope, add_expr, .add_wrap);
            } else {
                break :res try t.transBinExpr(scope, add_expr, .add);
            }
        },
        .sub_expr => |sub_expr| res: {
            // `ptr - idx` -> ptr - @as(usize, @bitCast(@as(isize, @intCast(idx))))
            const lhs_qt = sub_expr.lhs.qt(t.tree);
            const rhs_qt = sub_expr.rhs.qt(t.tree);
            if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
                t.signedness(rhs_qt) == .signed))
            {
                break :res try t.transPointerArithmeticSignedOp(scope, sub_expr, .sub);
            }

            if (sub_expr.lhs.qt(t.tree).isPointer(t.comp) and sub_expr.rhs.qt(t.tree).isPointer(t.comp)) {
                break :res try t.transPtrDiffExpr(scope, sub_expr);
            } else if (t.signedness(qt) == .unsigned) {
                break :res try t.transBinExpr(scope, sub_expr, .sub_wrap);
            } else {
                break :res try t.transBinExpr(scope, sub_expr, .sub);
            }
        },
        .mul_expr => |mul_expr| if (t.signedness(qt) == .unsigned)
            try t.transBinExpr(scope, mul_expr, .mul_wrap)
        else
            try t.transBinExpr(scope, mul_expr, .mul),

        .less_than_expr => |lt| try t.transBinExpr(scope, lt, .less_than),
        .greater_than_expr => |gt| try t.transBinExpr(scope, gt, .greater_than),
        .less_than_equal_expr => |lte| try t.transBinExpr(scope, lte, .less_than_equal),
        .greater_than_equal_expr => |gte| try t.transBinExpr(scope, gte, .greater_than_equal),
        .equal_expr => |equal_expr| try t.transBinExpr(scope, equal_expr, .equal),
        .not_equal_expr => |not_equal_expr| try t.transBinExpr(scope, not_equal_expr, .not_equal),

        .bool_and_expr => |bool_and_expr| try t.transBoolBinExpr(scope, bool_and_expr, .@"and"),
        .bool_or_expr => |bool_or_expr| try t.transBoolBinExpr(scope, bool_or_expr, .@"or"),

        .bit_and_expr => |bit_and_expr| try t.transBinExpr(scope, bit_and_expr, .bit_and),
        .bit_or_expr => |bit_or_expr| try t.transBinExpr(scope, bit_or_expr, .bit_or),
        .bit_xor_expr => |bit_xor_expr| try t.transBinExpr(scope, bit_xor_expr, .bit_xor),

        .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl),
        .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr),

        .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null, .accessor),
        .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null, .accessor),
        .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null),

        .builtin_ref => unreachable,
        .builtin_call_expr => |call| return t.transBuiltinCall(scope, call, used),
        .call_expr => |call| return t.transCall(scope, call, used),

        .builtin_types_compatible_p => |compatible| blk: {
            const lhs = try t.transType(scope, compatible.lhs, compatible.builtin_tok);
            const rhs = try t.transType(scope, compatible.rhs, compatible.builtin_tok);

            break :blk try ZigTag.equal.create(t.arena, .{
                .lhs = lhs,
                .rhs = rhs,
            });
        },
        .builtin_choose_expr => |choose| return t.transCondExpr(scope, choose, used),
        .cond_expr => |cond_expr| return t.transCondExpr(scope, cond_expr, used),
        .binary_cond_expr => |conditional| return t.transBinaryCondExpr(scope, conditional, used),
        .cond_dummy_expr => unreachable,

        .assign_expr => |assign| return t.transAssignExpr(scope, assign, used),
        .add_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .sub_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .mul_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .div_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .mod_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .shl_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .shr_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .bit_and_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .bit_xor_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .bit_or_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
        .compound_assign_dummy_expr => {
            assert(used == .used);
            return t.compound_assign_dummy.?;
        },

        .comma_expr => |comma_expr| return t.transCommaExpr(scope, comma_expr, used),
        .pre_inc_expr => |un| return t.transIncDecExpr(scope, un, .pre, .inc, used),
        .pre_dec_expr => |un| return t.transIncDecExpr(scope, un, .pre, .dec, used),
        .post_inc_expr => |un| return t.transIncDecExpr(scope, un, .post, .inc, used),
        .post_dec_expr => |un| return t.transIncDecExpr(scope, un, .post, .dec, used),

        .int_literal => return t.transIntLiteral(scope, expr, used, .with_as),
        .char_literal => return t.transCharLiteral(scope, expr, used, .with_as),
        .float_literal => return t.transFloatLiteral(scope, expr, used, .with_as),
        .string_literal_expr => |literal| try t.transStringLiteral(scope, expr, literal),
        .bool_literal => res: {
            const val = t.tree.value_map.get(expr).?;
            break :res if (val.toBool(t.comp))
                ZigTag.true_literal.init()
            else
                ZigTag.false_literal.init();
        },
        .nullptr_literal => ZigTag.null_literal.init(),
        .imaginary_literal => |literal| {
            return t.fail(error.UnsupportedTranslation, literal.op_tok, "TODO complex numbers", .{});
        },
        .compound_literal_expr => |literal| return t.transCompoundLiteral(scope, literal, used),

        .default_init_expr => |default_init| return t.transDefaultInit(scope, default_init, used, .with_as),
        .array_init_expr => |array_init| return t.transArrayInit(scope, array_init, used),
        .union_init_expr => |union_init| return t.transUnionInit(scope, union_init, used),
        .struct_init_expr => |struct_init| return t.transStructInit(scope, struct_init, used),
        .array_filler_expr => unreachable,

        .sizeof_expr => |sizeof| try t.transTypeInfo(scope, .sizeof, sizeof),
        .alignof_expr => |alignof| try t.transTypeInfo(scope, .alignof, alignof),

        .imag_expr, .real_expr => |un| {
            return t.fail(error.UnsupportedTranslation, un.op_tok, "TODO complex numbers", .{});
        },
        .addr_of_label => |addr_of_label| {
            return t.fail(error.UnsupportedTranslation, addr_of_label.label_tok, "TODO computed goto", .{});
        },

        .generic_expr => |generic| return t.transExpr(scope, generic.chosen, used),
        .generic_association_expr => |generic| return t.transExpr(scope, generic.expr, used),
        .generic_default_expr => |generic| return t.transExpr(scope, generic.expr, used),

        .stmt_expr => |stmt_expr| return t.transStmtExpr(scope, stmt_expr, used),

        .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector),
        .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector),

        .builtin_va_arg_pack, .builtin_va_arg_pack_len => |va_arg_pack| {
            return t.fail(error.UnsupportedTranslation, va_arg_pack.builtin_tok, "TODO va arg pack", .{});
        },

        .compound_stmt,
        .static_assert,
        .return_stmt,
        .null_stmt,
        .if_stmt,
        .while_stmt,
        .do_while_stmt,
        .for_stmt,
        .continue_stmt,
        .break_stmt,
        .labeled_stmt,
        .switch_stmt,
        .case_stmt,
        .default_stmt,
        .goto_stmt,
        .computed_goto_stmt,
        .asm_stmt,
        .global_asm,
        .typedef,
        .struct_decl,
        .union_decl,
        .enum_decl,
        .function,
        .param,
        .variable,
        .enum_field,
        .record_field,
        .struct_forward_decl,
        .union_forward_decl,
        .enum_forward_decl,
        .empty_decl,
        => unreachable, // not an expression
    });
}