antlr/antlr4 · error · IllegalStateException

Only precedence DFAs may contain a precedence start state.

Error message

Only precedence DFAs may contain a precedence start state.

What it means

DFA.getPrecedenceStartState(precedence) returns the per-precedence start state used by ALL(*) prediction for left-recursive rules. Only DFAs flagged as precedence DFAs (created with new DFA(atnStart, -1) or otherwise marked) keep the s0.edges array of per-precedence starts; a normal DFA has a single s0, so the request is a logic error and throws IllegalStateException.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/dfa/DFA.java:96

	 */
	public final boolean isPrecedenceDfa() {
		return precedenceDfa;
	}

	/**
	 * Get the start state for a specific precedence value.
	 *
	 * @param precedence The current precedence.
	 * @return The start state corresponding to the specified precedence, or
	 * {@code null} if no start state exists for the specified precedence.
	 *
	 * @throws IllegalStateException if this is not a precedence DFA.
	 * @see #isPrecedenceDfa()
	 */
	@SuppressWarnings("null")
	public final DFAState getPrecedenceStartState(int precedence) {
		if (!isPrecedenceDfa()) {
			throw new IllegalStateException("Only precedence DFAs may contain a precedence start state.");
		}

		// s0.edges is never null for a precedence DFA
		if (precedence < 0 || precedence >= s0.edges.length) {
			return null;
		}

		return s0.edges[precedence];
	}

	/**
	 * Set the start state for a specific precedence value.
	 *
	 * @param precedence The current precedence.
	 * @param startState The start state corresponding to the specified
	 * precedence.
	 *
	 * @throws IllegalStateException if this is not a precedence DFA.

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Guard with isPrecedenceDfa() before calling, and fall back to get s0 via the normal start-state accessor
  2. If the DFA should be a precedence DFA, construct it as new DFA(atnStart, precedence) with precedence >= 0 (e.g. -1 initialization in ParserATNSimulator means precedence DFA for the artificial rule-start decision)
  3. Otherwise use dfa.s0 (or the appropriate public API) for non-precedence DFAs

Example fix

// before
DFAState start = dfa.getPrecedenceStartState(prec); // throws for normal DFA
// after
DFAState start = dfa.isPrecedenceDfa()
        ? dfa.getPrecedenceStartState(prec)
        : dfa.s0;
Defensive patterns

Strategy: type-guard

Validate before calling

DFAState start = dfa.isPrecedenceDfa() ? dfa.getPrecedenceStartState(prec) : dfa.s0;

Type guard

static boolean isPrecedenceDfa(DFA dfa) { return dfa.isPrecedenceDfa(); }

Prevention

When it happens

Trigger: Calling getPrecedenceStartState on a DFA constructed with new DFA(atnStartState) (no precedence argument), or before the ParserATNSimulator has initialized it as a precedence DFA (setPrecedenceDfa path). Typically hit by custom prediction-context or DFA-inspection code, since the runtime itself only calls it after checking isPrecedenceDfa().

Common situations: Writing DFA diagnostics/visualization tools that assume every decision DFA is a precedence DFA. Copying runtime code that handles left-recursive rules into a context where the DFA was created without precedence. Using a DFA meant for a non-left-recursive grammar decision.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/c23f7fce20961043. Report an issue: GitHub.