Skip to content

[Detail Bug] Compiler: if conditions optimized to constants drop side effects and produce mis-indented Bash #1050

@detail-app

Description

@detail-app

Summary

  • Context: IfCondition::translate in src/modules/condition/ifcond.rs is responsible for translating if statements into Bash code, including an optimization for conditions known at compile-time.
  • Bug: The translation process for statically known conditions (true or false) completely skips translating the condition expression itself, which causes all side effects within that expression to be lost.
  • Actual vs. expected: Statically optimized if conditions lose side effects like command executions and function calls, whereas they should be preserved even if the condition's value is constant.
  • Impact: Valid Amber code that relies on side effects within an if condition will produce incorrect Bash scripts where those side effects never occur.

Code with bug

        match self.expr.analyze_control_flow() {
            Some(true) => self
                .true_block
                .as_ref()
                .map(|b| b.translate(meta)) // <-- BUG 🔴 [Loses side effects of self.expr and has wrong indentation]
                .unwrap_or(FragmentKind::Empty),
            Some(false) => self
                .false_block
                .as_ref()
                .map(|b| b.translate(meta)) // <-- BUG 🔴 [Loses side effects of self.expr and has wrong indentation]
                .unwrap_or(FragmentKind::Empty),

Evidence

Side effect loss

When compiling the following Amber code:

fun my_func(): Bool {
    echo "Side effect"
    return false
}

if my_func() or true {
    echo "Done"
}

The compiler determines that my_func() or true is statically true (since or true is always true). It then skips the translation of the expression, resulting in Bash code where my_func is defined but never called:

my_func__0_v0() {
    echo "Side effect"
    ret_my_func0_v0=0
    return 0
}
# The call to my_func__0_v0 is missing here!
echo "Done"

The expected behavior is that my_func should be called before echo "Done".

Indentation bug

Additionally, the optimization returns the translated block directly, which results in incorrect indentation in the generated Bash. For example:

if true {
    echo "Done"
}

Generates:

    echo "Done"

Instead of:

echo "Done"

The leading spaces are preserved because the block was translated with increase_indent: true, but the if statement that would have justified the indentation was removed.

Why has this bug gone undetected?

This bug primarily affects expressions that combine compile-time constants with side-effect-ful operations (like commands or functions) in a way that allows the compiler to determine the result statically. In typical scripts, if conditions are either fully dynamic or simple literals, making this edge case rare but critical for correctness.

Recommended fix

The translate method should always call self.expr.translate(meta) to ensure side effects are pushed to the statement queue, even if the result is ignored. Furthermore, when unwrapping a block due to static optimization, its indentation should be adjusted.

    fn translate(&self, meta: &mut TranslateMetadata) -> FragmentKind {
        let _ = self.expr.translate(meta); // Preserve side effects 🟢
        match self.expr.analyze_control_flow() {
            Some(true) => {
                if let Some(true_block) = &self.true_block {
                    let mut frag = true_block.translate(meta);
                    if let FragmentKind::Block(ref mut block) = frag {
                        block.increase_indent = false; // Fix indentation 🟢
                    }
                    frag
                } else {
                    FragmentKind::Empty
                }
            }
            // ... similar for Some(false) ...

Related bugs

Similar aggressive optimizations that lose side effects are also present in src/modules/condition/ifchain.rs and src/modules/expression/ternop/ternary.rs.

History

This bug was introduced in commit 09af993 (@Ph0enixKM, 2025-12-19, PR #940). The change added control flow analysis to optimize statically known conditions, but it incorrectly skipped calling translate() on the condition expression, losing its side effects, and failed to handle block indentation when unwrapping optimized branches.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdetail

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions