Skip to content

Non-boolean variable used as a ternary condition crashes LLVM directly #2012

Description

@ahmadsamehh

Part of #1999soroban-emit-multifuzz-v2 campaign tracking issue

Solang version: confirmed on public v0.3.5 CLI (solang compile --target soroban)
Target: Soroban
Crash type: LLVM assertion failure (BranchInst::AssertOK), process aborts

Summary

Using a plain non-boolean variable (e.g. a uint32 parameter) as the condition of a ternary (?:) expression crashes with a raw LLVM assertion rather than a Rust-side panic or a clean diagnostic. The equivalent if statement with the same non-boolean variable is correctly caught during semantic analysis — the gap is specific to the ternary operator with a non-literal condition.

Minimal repro

contract Test {
    function f(uint32 a, uint32 b) public returns (uint32) {
        return a ? b : a;
    }
}

Crash output

warning: function can be declared 'pure'
solang: llvm/lib/IR/Instructions.cpp:1367: void llvm::BranchInst::AssertOK(): Assertion `getCondition()->getType()->isIntegerTy(1) && "May only branch on boolean predicates!"' failed.
Aborted

Controls (confirm the gap is specific to ternary + non-literal condition)

if statement with the same non-bool variable — correctly rejected:

contract Test {
    function f(uint32 a) public {
        if (a) {
        }
    }
}

Clean error: conversion from uint32 to bool not possible.

Integer literal as ternary condition — correctly rejected:

contract Test {
    function f(uint32 a, uint32 b) public returns (uint32) {
        return 1 ? b : a;
    }
}

Clean error: expected 'bool', found integer.

So if conditions are checked properly, and literal ternary conditions are checked properly — it's specifically a non-literal expression used as a ternary condition that slips through with zero validation and goes straight to LLVM, hitting LLVMBuildCondBr with a non-i1 value.

Related, previously filed reports

Sibling gaps in the same "ternary condition must be boolean" check:

  • A rational/decimal literal used as a condition crashes with a different panic (internal error: entered unreachable code) in Solang's own emit code — see sibling sub-issue.
  • A void-returning call used as a condition crashes constant folding with a Poison-related panic — see sibling sub-issue.

Found via fuzzing

Discovered via AFL++ fuzzing campaign (soroban-emit-multifuzz-v2), found while building controls for the rational-literal ternary-condition report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions