1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use cairo_lang_defs::diagnostic_utils::StableLocationOption;
use cairo_lang_semantic as semantic;
use cairo_lang_semantic::corelib;

use super::block_builder::BlockBuilder;
use super::context::{LoweredExpr, LoweringContext, LoweringResult, VarRequest};
use super::generators::{self, StructConstruct};
use super::{create_subscope_with_bound_refs, lower_expr};
use crate::{MatchArm, MatchEnumInfo, MatchInfo, VariableId};

/// Creates a bool variable with the given variant.
pub fn create_bool(
    ctx: &mut LoweringContext<'_, '_>,
    builder: &mut BlockBuilder,
    variant: semantic::ConcreteVariant,
    location: StableLocationOption,
) -> VariableId {
    let semantic_db = ctx.db.upcast();

    let unit = StructConstruct { inputs: vec![], ty: corelib::unit_ty(semantic_db), location }
        .add(ctx, &mut builder.statements);

    generators::EnumConstruct { input: unit, variant, location }.add(ctx, &mut builder.statements)
}

/// Lowers an expression of type [semantic::ExprLogicalOperator].
pub fn lower_logical_op(
    ctx: &mut LoweringContext<'_, '_>,
    builder: &mut BlockBuilder,
    expr: &semantic::ExprLogicalOperator,
) -> LoweringResult<LoweredExpr> {
    let location = ctx.get_location(expr.stable_ptr.untyped());

    let semantic_db = ctx.db.upcast();

    let unit_ty = corelib::unit_ty(semantic_db);
    let lhs_var = lower_expr(ctx, builder, expr.lhs)?.var(ctx, builder)?;

    let mut subscope_lhs_true = create_subscope_with_bound_refs(ctx, builder);
    let lhs_true_block_id = subscope_lhs_true.block_id;

    let (sealed_block_lhs_true, lhs_false_block_id, sealed_block_lhs_false) = match expr.op {
        // Lowers `lhs && rhs` to `if lhs { rhs } else { false }`.
        semantic::LogicalOperator::AndAnd => {
            let rhs_var = lower_expr(ctx, &mut subscope_lhs_true, expr.rhs)?
                .var(ctx, &mut subscope_lhs_true)?;

            let sealed_block_lhs_true = subscope_lhs_true.goto_callsite(Some(rhs_var));
            let mut subscope_lhs_false = create_subscope_with_bound_refs(ctx, builder);
            let lhs_false_block_id = subscope_lhs_false.block_id;
            let false_var = create_bool(
                ctx,
                &mut subscope_lhs_false,
                corelib::false_variant(semantic_db),
                location,
            );
            let sealed_block_lhs_false = subscope_lhs_false.goto_callsite(Some(false_var));
            (sealed_block_lhs_true, lhs_false_block_id, sealed_block_lhs_false)
        }

        // Lowers `lhs || rhs` to `if lhs { true } else { rhs }`.
        semantic::LogicalOperator::OrOr => {
            let true_var = create_bool(
                ctx,
                &mut subscope_lhs_true,
                corelib::true_variant(semantic_db),
                location,
            );
            let sealed_block_lhs_true = subscope_lhs_true.goto_callsite(Some(true_var));
            let mut subscope_lhs_false = create_subscope_with_bound_refs(ctx, builder);
            let lhs_false_block_id = subscope_lhs_false.block_id;
            let rhs_var = lower_expr(ctx, &mut subscope_lhs_false, expr.rhs)?
                .var(ctx, &mut subscope_lhs_false)?;

            let sealed_block_lhs_false = subscope_lhs_false.goto_callsite(Some(rhs_var));
            (sealed_block_lhs_true, lhs_false_block_id, sealed_block_lhs_false)
        }
    };

    let match_info = MatchInfo::Enum(MatchEnumInfo {
        concrete_enum_id: corelib::core_bool_enum(semantic_db),
        input: lhs_var,
        arms: vec![
            MatchArm {
                variant_id: corelib::false_variant(semantic_db),
                block_id: lhs_false_block_id,
                var_ids: vec![ctx.new_var(VarRequest { ty: unit_ty, location })],
            },
            MatchArm {
                variant_id: corelib::true_variant(semantic_db),
                block_id: lhs_true_block_id,
                var_ids: vec![ctx.new_var(VarRequest { ty: unit_ty, location })],
            },
        ],
    });
    builder.merge_and_end_with_match(
        ctx,
        match_info,
        vec![sealed_block_lhs_false, sealed_block_lhs_true],
        location,
    )
}