Skip to content

Fix non-contiguous circuit input diagnostics - #10366

Open
zakazaka95 wants to merge 4 commits into
starkware-libs:mainfrom
zakazaka95:fix/10138-circuit-input-indices
Open

Fix non-contiguous circuit input diagnostics#10366
zakazaka95 wants to merge 4 commits into
starkware-libs:mainfrom
zakazaka95:fix/10138-circuit-input-indices

Conversation

@zakazaka95

@zakazaka95 zakazaka95 commented Sep 2, 2026

Copy link
Copy Markdown

Summary

  • detect malformed core::circuit::Circuit types while they still have a source location
  • report the first missing transitive input index at each malformed use instead of reaching Sierra specialization
  • cover gaps, non-zero starts, output subsets, and valid reuse of one input

Type of change

  • Bug fix (fixes incorrect behavior)
  • New feature
  • Performance improvement
  • Documentation change with concrete technical impact
  • Style, wording, formatting, or typo-only change

Why is this change needed?

Sierra circuit specialization assumes that the input indices reachable from a circuit's outputs are exactly 0..N. A gap currently reaches an infallible specialization path and crashes the compiler instead of producing an actionable source diagnostic.


What was the behavior or documentation before?

Circuits using inputs such as {0, 2}, starting at a non-zero index, or exposing only input 1 as an output caused an internal compiler panic.


What is the behavior or documentation after?

Semantic analysis emits E2204 at the circuit expression, identifying the expected and actual index. Reusing a contiguous input remains valid.


Related issue or discussion (if any)

Closes #10138.


Additional context

Latest helper refactor (636760d): the focused circuit regression, strict all-target/all-feature Clippy, workspace formatting and diff checks pass.

Earlier implementation validation (September 3):

  • focused expr_diagnostics::circuit regression: 1 passed
  • cargo test -p cairo-lang-semantic: 111 passed
  • strict all-target/all-feature Clippy for cairo-lang-semantic
  • nightly Rust formatting check and git diff --check

@cursor

cursor Bot commented Sep 2, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Scoped semantic validation and diagnostics for circuit types; no changes to auth, runtime, or Sierra codegen paths beyond failing earlier with a source error.

Overview
Fixes compiler internal panics when circuit code uses malformed CircuitInput index sets by validating during semantic analysis instead of at Sierra specialization.

add_value_type_based_diagnostics now walks nested types for core::circuit::Circuit, collects CircuitInput indices reachable from the circuit’s output type, and reports E2204 when that set is not exactly 0..n (gaps, starting above zero, or outputs that only use a non-zero input). Reusing a single contiguous input stays allowed.

Adds ModuleHelper::extern_type_id, wires the new CircuitInputIndicesNotContiguous diagnostic kind, and extends expr diagnostic tests under circuit for gap, non-zero start, output-subset, and valid reuse cases.

Reviewed by Cursor Bugbot for commit 636760d. Bugbot is set up for automated code reviews on this repo. Configure here.

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 7 comments.
Reviewable status: 0 of 4 files reviewed, 7 unresolved discussions (waiting on zakazaka95).


crates/cairo-lang-semantic/src/diagnostic.rs line 75 at r1 (raw file):

    /// Reports a non-contiguous circuit input set once per concrete circuit type.
    pub(crate) fn report_circuit_input_indices(

no reason for this to exist as a function - write it inline at callsite.


crates/cairo-lang-semantic/src/types.rs line 966 at r1 (raw file):

}

/// The first gap in a circuit's sorted input indices.

move this section to after the array violation section for a more consistent ordering.


crates/cairo-lang-semantic/src/types.rs line 984 at r1 (raw file):

    ty: TypeId<'db>,
) -> Option<CircuitInputIndexViolation<'db>> {
    let circuit_module = core_submodule(db, SmolStrId::from(db, "circuit"));

this would require adding extern_type_id at the helper - but this still is more sensible.

Suggestion:

    let circuit_module = ModuleHelper::core(db).submodule("circuit").extern_type_id("Circuit");
    let circuit_extern_id = circuit_module.extern_type_id("Circuit");
    let circuit_input_extern_id = circuit_module.extern_type_id("CircuitInput");

crates/cairo-lang-semantic/src/types.rs line 1006 at r1 (raw file):

                    GenericArgumentId::Type(ty) => Some(*ty),
                    _ => None,
                }));

Suggestion:

                let ConcreteExternTypeLongId { extern_type_id, generic_args } = extrn.long(db);
                if extern_type_id != circuit_extern_id {
                        stack.extend(generic_args.iter().filter_map(|arg| match arg {
                        GenericArgumentId::Type(ty) => Some(*ty),
                        _ => None,
                    }));
                } else if let [GenericArgumentId::Type(outputs)] = generic_args[..]
                    && let Some((expected, actual)) =
                        non_contiguous_circuit_input(db, outputs, circuit_module)
                {
                    return Some(CircuitInputIndexViolation { circuit: ty, expected, actual });
                }

crates/cairo-lang-semantic/src/types.rs line 1033 at r1 (raw file):

    db: &'db dyn Database,
    outputs: TypeId<'db>,
    circuit_module: ModuleId<'db>,

Suggestion:

    circuit_input_extern_id: ModuleId<'db>,

crates/cairo-lang-semantic/src/types.rs line 1048 at r1 (raw file):

                if extern_type_id.parent_module(db) == circuit_module
                    && extern_type_id.name(db).long(db).as_str() == "CircuitInput"
                {

Suggestion:

                let ConcreteExternTypeLongId { extern_type_id, generic_args } = extrn.long(db);
                if extern_type_id == circuit_input_extern_id {

crates/cairo-lang-semantic/src/types.rs line 1054 at r1 (raw file):

                    {
                        input_indices.insert(index);
                    }

so that we would also catch very large values if relevant.

Suggestion:

                    if let [GenericArgumentId::Constant(index)] = generic_args[..]
                        && let Some(index) = index.to_int(db)
                        && let Some(index) = index.to_usize()
                    {
                        input_indices.insert(index);
                    }

@zakazaka95

Copy link
Copy Markdown
Author

Addressed all seven review points in 239afb2: reporting is inlined, circuit validation now follows array validation, ModuleHelper::extern_type_id resolves the exact circuit extern types, traversal receives the CircuitInput ID directly, and constants use to_int(db).to_usize(). The focused regression and full cairo-lang-semantic suite pass (111/111), and strict clippy passes.

@zakazaka95
zakazaka95 requested a review from orizi September 2, 2026 20:17

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 2 files and all commit messages, made 2 comments, and resolved 6 discussions.
Reviewable status: 2 of 5 files reviewed, 3 unresolved discussions (waiting on zakazaka95).


crates/cairo-lang-semantic/src/diagnostic.rs line 46 at r2 (raw file):

    builder: DiagnosticsBuilder<'db, SemanticDiagnostic<'db>>,
    context_module: ModuleId<'db>,
    pub(crate) diagnosed_circuits: OrderedHashSet<semantic::TypeId<'db>>,

this shouldn't appear here.
if we have the same problematic type twice, we can report it twice.

Code quote:

    pub(crate) diagnosed_circuits: OrderedHashSet<semantic::TypeId<'db>>,

crates/cairo-lang-semantic/src/types.rs line 962 at r2 (raw file):

    } else if let Some(CircuitInputIndexViolation { circuit, expected, actual }) =
        circuit_input_index_violation(db, ty)
        && diagnostics.diagnosed_circuits.insert(circuit)

this shouldn't be required.

Code quote:

diagnostics.diagnosed_circuits.insert(circuit)

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reply to reviews on https://reviewable.io/reviews/starkware-libs/cairo/10366

@orizi made 1 comment.
Reviewable status: 2 of 5 files reviewed, 3 unresolved discussions (waiting on zakazaka95).

@zakazaka95 zakazaka95 left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. Addressed the latest review in f3b083d. I removed the cross-use deduplication so repeated malformed uses can each report. Focused regression 1/1 and full cairo-lang-semantic 111/111 pass, along with doctests, root formatting, strict scoped Clippy, and diff checks.

@zakazaka95 made 4 comments.
Reviewable status: 2 of 5 files reviewed, 3 unresolved discussions (waiting on orizi).


crates/cairo-lang-semantic/src/diagnostic.rs line 75 at r1 (raw file):

Previously, orizi wrote…

no reason for this to exist as a function - write it inline at callsite.

Done. Inlined at the callsite in 239afb2; the standalone helper no longer exists.


crates/cairo-lang-semantic/src/diagnostic.rs line 46 at r2 (raw file):

Previously, orizi wrote…

this shouldn't appear here.
if we have the same problematic type twice, we can report it twice.

Done. Removed in f3b083d. SemanticDiagnostics no longer keeps diagnosed_circuits state, so the same problematic type can be reported at each use.


crates/cairo-lang-semantic/src/types.rs line 962 at r2 (raw file):

Previously, orizi wrote…

this shouldn't be required.

Done. Removed in f3b083d. The diagnosed_circuits insertion guard and the now-unused carried circuit value are gone.

@zakazaka95
zakazaka95 requested a review from orizi September 3, 2026 20:03

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi reviewed 2 files and all commit messages, made 1 comment, and resolved 3 discussions.
Reviewable status: 4 of 5 files reviewed, 1 unresolved discussion (waiting on zakazaka95).


crates/cairo-lang-semantic/src/types.rs line 1132 at r3 (raw file):

                        GenericArgumentId::Type(ty) => Some(*ty),
                        _ => None,
                    }));

Suggestion:

                    stack.extend(
                        generic_args.iter().filter_map(|arg| try_extract_matches!(arg, GenericArgumentId::Type))
                    );

@zakazaka95 zakazaka95 left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zakazaka95 made 1 comment.
Reviewable status: 4 of 5 files reviewed, 1 unresolved discussion (waiting on orizi).


crates/cairo-lang-semantic/src/types.rs line 1132 at r3 (raw file):

                        GenericArgumentId::Type(ty) => Some(*ty),
                        _ => None,
                    }));

Done in 636760d. The circuit regression passes, along with strict scoped Clippy and workspace formatting.

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@orizi reviewed 1 file and all commit messages, made 2 comments, and resolved 1 discussion.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on zakazaka95).


a discussion (no related file):
@TomerStarkware @eytan-starkware for 2nd eye.

@eytan-starkware eytan-starkware left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eytan-starkware+AGNT made 2 comments.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on zakazaka95).


crates/cairo-lang-semantic/src/types.rs line 1188 at r4 (raw file):

                    continue;
                }
                stack.extend(generic_args.iter().filter_map(|arg| match arg {

Same extraction as in circuit_input_index_violation above (already switched to the macro in r4) - use it here too for consistency.

Suggestion:

                stack.extend(
                    generic_args.iter().filter_map(|arg| try_extract_matches!(arg, GenericArgumentId::Type)),
                );

crates/cairo-lang-semantic/src/types.rs line 1194 at r4 (raw file):

            }
            TypeLongId::Tuple(types) => stack.extend(types.iter().copied()),
            _ => {}

A Missing type here means a diagnostic was already reported and the input set is unknown, so the contiguity result below is a cascade of that error. E.g. with a typo in one input type:

use core::circuit::{CircuitElement, CircuitInput, CircuitInputs, circuit_add};
fn main() {
    let a = CircuitElement::<CircuitInput<0>> {};
    let b = CircuitElement::<CircuitInpt<1>> {}; // typo
    let c = CircuitElement::<CircuitInput<2>> {};
    let sum = circuit_add(a, b);
    let _acc = (sum, c).new_inputs();
}

this PR reports, on top of the E0006: Type not found at the typo:

error[E2204]: Circuit input indices must be contiguous and start at 0. Expected index 1, found 2.
 --> lib.cairo:7:16
    let _acc = (sum, c).new_inputs();
               ^^^^^^^^^^^^^^^^^^^^^

Fixing the typo makes it disappear, and it points at the wrong place. Bail out on Missing, as type_size_info does:

Suggestion:

            TypeLongId::Tuple(types) => stack.extend(types.iter().copied()),
            TypeLongId::Missing(_) => return None,
            _ => {}

Please also add the snippet above as a case in test_data/circuit, expecting only the E0006.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Non-contiguous circuit input indices crash compiler

4 participants