**Title:** MustBeClosed/StreamResourceLeak should combine multiple findings per method into one fix with unique variable names

**Background:**
The `MustBeClosedChecker` and `StreamResourceLeak` checks each flag individual expressions where a resource isn’t closed. When a method contains several such expressions, the check reports multiple diagnostics, each with its own suggested fix. Those independent fixes can conflict—particularly by introducing `try`-with-resources variables with identical names (e.g., `closeable`), and by inserting closing braces at the same position multiple times.

**Desired behavior:**
For each method, emit **at most one diagnostic** covering all offending expressions. The single suggested fix must:

- Construct nested `try`-with-resources blocks (or other appropriate structures) so every resource is properly closed.
- Assign each resource to a **locally unique variable name**. If the plain name derived from the type (e.g., `closeable`, `stream`) is already used by a fix within that method, append a suffix (`closeable2`, `closeable3`, …) to avoid clashes. The checker only needs to avoid clashes among variables *introduced by the fix itself*; it does not need to avoid pre-existing variable names in the source code.
- **Not duplicate closing brace insertions** when multiple `try` blocks end at the same program point. The aggregated fix must add exactly the required number of closing braces at each location.

**Scope:**
- `MustBeClosedChecker` for methods/constructors annotated `@MustBeClosed`.
- `StreamResourceLeak` for `Files.lines`, `newDirectoryStream`, `list`, `walk`, `find`, etc.

Both checkers must switch from per-node matching (e.g., `MethodInvocationTreeMatcher`, `NewClassTreeMatcher`) to method-level scanning, and reuse the common aggregation logic in `AbstractMustBeClosedChecker`.

**Edge cases / Safety:**
- Continue to **skip** invocations inside exception-testing patterns, Mockito `when(…)` calls, and resources already handled in a `try`-with-resources or closed in `finally`.
- Expression lambdas, statement lambdas, returns from `@MustBeClosed` methods, and ternary expressions must still produce fixless findings or be handled correctly.
- The new `scanEntireMethodFor` must accept any matcher (`MethodInvocationTree` and `NewClassTree`) and produce a single combined fix; the current per-invocation logic (`matchNewClassOrMethodInvocation`) must be refactored to return an optional change description rather than emitting a description directly.
- For `StreamResourceLeak`, the custom fix logic that decides between wrapping a statement, variable, loop, or method invocation must be integrated into the aggregated scheme without losing its special behavior.

**Context in codebase:**
- Abstract base: `core/src/main/java/com/google/errorprone/bugpatterns/AbstractMustBeClosedChecker.java`
- Two concrete checkers: `MustBeClosedChecker` and `StreamResourceLeak` in the same package.

Avoid name clashes when MustBeClosedChecker introduces multiple variables


**Title:** Separate string‑based `Api` representation from javac‑symbol conversion logic

**Context:** The `Api` class (in `com.google.errorprone.bugpatterns.checkreturnvalue`) currently mixes two concerns: parsing a human‑readable method‑signature string (e.g. `java.util.List#add(java.lang.Object)`) and creating `Api` instances from javac `MethodSymbol`s. It also imports compiler‑internal types and Error Prone matcher classes. This coupling prevents reuse of the pure‑string model in contexts that don’t have access to javac, and makes the parsing logic harder to maintain.

**Ask:**
Refactor `Api` so that it is solely a string‑based value type with a public `parse(String)` method. Move all javac‑specific logic—converting a `MethodSymbol` to an `Api`, type erasure, annotation stripping, and `Matcher` creation—into a separate utility (e.g., a new `ApiFactory` class).

**Required behavior and invariants:**

- `Api.parse(String)` must remain the primary public entry point. It should accept signatures with arbitrary leading, trailing, and inner whitespace (e.g., `"  com.google.Foo  #  bar  ( int ,  java.lang.String ) "`) and return a normalized `Api` whose `toString()` contains no extraneous spaces.
- The internal parsing routines must still work correctly when the caller can guarantee that the input contains no whitespace (the flag‑based variant may be kept private/package‑private for performance‑sensitive internal paths, but it must not be part of the public API).
- The new factory must produce `Api` instances that are **equal** (by `AutoValue` equality) to those produced by parsing the equivalent string, and its output must use fully‑erased, unannotated type strings exactly as before.
- `ExternalCanIgnoreReturnValue` and any other existing callers that use `Api` objects for method matching must continue to behave identically. In particular, `ExternalCanIgnoreReturnValue` should switch from the whitespace‑free parse to the standard `Api.parse(String)` (which trims whitespace) since the stored signatures already contain no whitespace; this change must not alter which methods are matched.
- The `Api` class must **not** import or depend on `com.sun.tools.javac` types, `Matcher`, `ExpressionTree`, or `VisitorState` after the refactor. Dependencies on these types must stay confined to the new factory.
- There should be a package‑restricted way for the factory to construct `Api` instances directly (e.g., a static `internalCreate` method), so that the factory does not need to re‑serialize/parse a symbol into a string and back.

**Acceptance criteria:**

- No change in which APIs are recognized or flagged by any existing Error Prone check.
- `Api` contains only the string representation, parsing logic, and a controlled creation hook for the factory; all javac‑symbol handling lives in a separate utility class.

Refactor implementation of Api to separate javac tree methods from the


**Title:** `TryFailRefactoring`: drop unused catch exception variable instead of renaming it

**Context**
The `TryFailRefactoring` check suggests converting `try { ...; fail(); } catch (FooException e) { ... }` into `assertThrows(FooException.class, () -> ...)`.
Currently, when the catch block contains statements, the refactoring always renames the exception parameter (to avoid shadowing) and includes the renamed variable in the transformed code. This leads to an unnecessary, unused exception variable when the original catch block never references it.

**Request**
When the caught exception variable is **not referenced anywhere** inside the catch block, the refactoring should:

- Omit the exception variable entirely.
- Move the catch body’s statements **directly after** the generated `assertThrows` call, without any renaming or variable declaration for the exception.

The detection of “unused” must be based on symbol identity (not string matching), so that references inside nested classes, lambdas, or other inner scopes are correctly recognized.
If the variable **is** referenced, the existing rename-to-avoid-shadowing behavior should be preserved as-is.

**Example**
From:
```java
try {
  Files.readAllBytes(p);
  fail();
} catch (IOException e) {
  cleanup();
}
```
The refactoring should produce:
```java
assertThrows(IOException.class, () -> Files.readAllBytes(p));
cleanup();
```
instead of introducing an unused `e` (e.g., `IOException e` inside a lambda or leaving the catch body with an unused parameter).

`TryFailRefactoring`: remove unused exception variable when the exception is not referenced in the `catch` block.


As part of improving the “return this” detection across several Error Prone checks, we should extend the following checkers to recognize two additional common patterns:

1. **`getThis()` methods alongside existing `self()` methods**
   Many projects use a zero-argument method named `getThis()` (instead of or in addition to `self()`) that simply returns `this`. The checks should treat these exactly like `self()`.

2. **Parenthesized and/or cast `this` expressions**
   Return expressions like `return (this);`, `return ((Foo) this);`, `return ((Foo) (this));` should be understood as returning `this`.

### Affected checkers

- **`SelfAlwaysReturnsThis`**
  - Extend the check to require that non‑abstract, zero‑arg, instance methods named `getThis()` (and `self()`) that return the enclosing class **must** return `this`.
  - When inspecting the return expression, skip through parentheses and type casts to check for `this`.
  - Provide an Error Prone flag `SelfAlwaysReturnsThis:GetThis` (default `true`) so that users can optionally disable the `getThis` recognition if needed.

- **`CanIgnoreReturnValueSuggester`**
  - When deciding whether a method “always returns `this`” (to suggest `@CanIgnoreReturnValue`), treat `return getThis();` as returning `this`.
  - Treat `return ((Type) (this));` and similar parenthesized/cast forms as a simple return of `this`.
  - Continue to exclude `getThis()` methods themselves from the suggestion (just as `self()` is excluded); extend `isDefinitionOfZeroArgSelf` to also cover `getThis`.

- **`NoCanIgnoreReturnValueOnClasses`**
  - When analyzing methods inside a class annotated with `@CanIgnoreReturnValue` (to push the annotation down to individual methods), recognize `return getThis();` and return expressions with casts/parentheses as returning `this`.
  - This allows the check to correctly propose moving the annotation to the method level rather than leaving it on the class.

### Non‑goals / safety constraints

- Only match `getThis()` when it is a zero‑argument call on `this` (implicit or explicit) and the return type matches the enclosing class.
- Do **not** match `getThis()` on other objects (e.g., `someObj.getThis()`). The existing checks already restrict matches to the current instance.
- The parenthesized/cast unwrapping should be shallow: skip parentheses and casts, but do not evaluate arbitrary expressions.

These enhancements will make the “return this” analysis more robust across a wider range of real‑world builder and fluent API patterns.

Enhance various `return this` checks in various ways:


Extend `UnsafeWildcard` to detect and flag inferred wildcard type arguments that are logically impossible. In particular, when the compiler infers a wildcard `? super L` for a type parameter with an upper bound `U`, and `L` is a strict supertype of `U` (but not `Object`), the wildcard cannot be satisfied by any type and should be reported as unsafe just like other problematic inferred wildcards. The existing logic only checks the case where the wildcard's bound ends with `Object`; this new case catches scenarios like `? super Object` used for `T extends Number`, where the inferred bound conflicts with the declared bound.


**Title:** Extend `MemberName` naming checks to exception parameters and try-with-resources variables

**Background**
The `MemberName` bug check enforces lowerCamelCase naming on methods and non-static variables. For local variables (`LOCAL_VARIABLE`), it both flags violations and offers an automatic rename because they are private to the enclosing block and renaming is always safe. Exception parameters (`EXCEPTION_PARAMETER`) and resource variables declared in try-with-resources statements (`RESOURCE_VARIABLE`) have the same local scope and rename-safety, but they are currently ignored.

**Request**
Extend `MemberName` to treat `EXCEPTION_PARAMETER` and `RESOURCE_VARIABLE` the same as `LOCAL_VARIABLE` for purposes of naming enforcement and refactoring.

**Expected behavior**
- Catch-block variables (including multi-catch) with names that do not follow lowerCamelCase should be flagged with the existing warning and should be renamed automatically when the suggested fix is applied.
- Variables declared as resources in try-with-resources (`try (var foo_bar = ...)`) should similarly be flagged and renamed.
- The existing exemptions (e.g., `static final` fields, test-annotated methods, enums, etc.) should not be affected. Exception parameters and resource variables are never static, and the current exemption logic does not need to expand.
- The `isConformant` check and the rename suggestion logic (`suggestedRename`) do not need to change; they already handle any symbol for which `canBeRenamed` returns true.
- The implementation should ensure that the set of “local” kinds is used consistently; the old direct comparison against `LOCAL_VARIABLE` alone should be replaced by a check that includes these additional kinds.

**Non-goals**
- No changes to how static fields, methods, or enum constants are handled.
- No new configuration options or annotations.

Recognize other kinds of "local variables."


The `@ThreadSafeTypeParameter` annotation is defined and documented but is not actually enforced by the `ThreadSafe` checker. This means generic `@ThreadSafe` classes with thread-safe-bound type parameters can be instantiated with non-thread-safe type arguments without any warning, silently undermining the thread-safety contract.

**Observed behavior:**
- Declaring a `@ThreadSafe` class with a type parameter annotated `@ThreadSafeTypeParameter` compiles without error, but the checker does not validate that instantiations use a thread-safe type argument.
- For example, `new Foo<Object>()` is accepted even when `Foo` is declared as `@ThreadSafe class Foo<@ThreadSafeTypeParameter T>` and `Object` is not thread-safe.
- Misplaced `@ThreadSafeTypeParameter` annotations (e.g., on a type parameter of a non-`@ThreadSafe` class) go undetected, even though the annotation’s semantics only apply to thread-safe containers.

**Expected behavior:**
1. **Instantiation checking**: When a `@ThreadSafe` class or interface has one or more type parameters annotated with `@ThreadSafeTypeParameter`, the `ThreadSafe` checker should report an error if any such type parameter is instantiated with a type that is not thread-safe (according to the checker’s existing rules).
2. **Scope restriction**: Applying `@ThreadSafeTypeParameter` to a type parameter of a class/interface that is *not* annotated `@ThreadSafe` should be flagged as an error (since the annotation’s meaning is tied to the thread-safety of the enclosing type).
3. **Documentation consistency**: The `@ThreadSafe` Javadoc already mentions that type parameters “that denote containment” must be thread-safe for the type to be deeply thread-safe. The checker should enforce this via `@ThreadSafeTypeParameter`.

**Impact:**
Without these checks, developers relying on the `ThreadSafe` checker can introduce non-thread-safe state into supposedly thread-safe generic containers, defeating the purpose of the static analysis. The annotation is effectively ignored, which is both confusing and unsafe.

Make ThreadSafeTypeParameter useful in the open-source version of ErrorProne.


## Feature Request: Smarter Hoisting in `AssertThrowsMinimizer` for Binary and Parenthesized Expressions

### Context
The `AssertThrowsMinimizer` checker currently suggests extracting any non-trivial expression from a lambda passed to `assertThrows` if that expression *might* throw the expected exception. This includes simple binary expressions like arithmetic operations (`i - 1`) and string concatenation (`"fail" + str`), even when the operands themselves are constants or local variables that cannot possibly throw the exception under test. The resulting suggestion adds a local variable for an inherently safe expression, creating unnecessary refactoring noise.

### Desired Behavior
The checker should avoid hoisting a binary expression (e.g., `+`, `-`, `*`, string `+`) when **both** its left and right operands do not, individually, require hoisting. In other words, a binary expression is only hoisted if at least one of its operands would be hoisted on its own.

Similarly, a parenthesized expression `(expr)` should be treated as equivalent to `expr` for hoisting decisions. The checker should look through the parentheses and decide based on the inner expression.

### Acceptance Criteria
1. **Binary expression with safe operands**
   Given `assertThrows(SomeException.class, () -> doWork(a + b))` where `a` and `b` are local variables, constants, or other expressions that do not need hoisting, the checker **must not** suggest hoisting `a + b`.

   *Example*:
   ```java
   void f(String str) {
       assertThrows(IllegalArgumentException.class,
           () -> TimeUnit.valueOf("fail" + str));
   }
   ```
   → `"fail" + str` remains in the lambda (unchanged).

2. **Binary expression with at least one hoist-worthy operand**
   If either operand needs hoisting (e.g., a method call that may throw the expected exception), the entire binary expression **must still be hoisted**, preserving existing safety behavior.

3. **Parenthesized expression**
   `assertThrows(SomeException.class, () -> doWork((x)))` where `x` does not need hoisting → unchanged. If `x` needs hoisting, the entire parenthesized expression `(x)` is hoisted as one unit (the parentheses are retained in the extracted variable).

4. **No regressions**
   All previously hoisted expressions that contain a potentially-throwing subexpression must continue to be hoisted. The change only relaxes hoisting for binary and parenthesized expressions whose **entire subtree** is known-safe with respect to the asserted exception.

### Additional Safety Constraints
- The decision for a binary expression is purely a recursive check: `needsHoisting(binary) = needsHoisting(left) || needsHoisting(right)`. This ensures we never drop a hoist-worthy subexpression.
- Parenthesized expressions similarly delegate to their contained expression, so nothing is accidentally suppressed.

Allow binary expressions (like string concatenation and safe arithmetic) inside `assertThrows` lambda if their operands don't need hoisting.


## Feature Request: Restrict `TreePath` access inside `VisitorState.memoize` suppliers

### Background
`VisitorState.memoize` returns a `Supplier<T>` whose value is computed once and cached for the entire compilation. The cache is shared across all `CompilationUnitTree` instances (i.e., across all files being compiled). However, `TreePath` (and anything derived from it) is specific to a single compilation unit. If a memoized supplier reads the current `TreePath`, the cached value will be silently wrong for every other file processed after the first evaluation.

### Desired behavior
When a `Supplier` provided to `VisitorState.memoize` attempts to access the current `TreePath`, the framework must throw a clear runtime exception to prevent invalid cross-file caching bugs.

**Acceptance criteria:**

1. **`TreePath` access from a memoized supplier throws an exception**
   If a `Supplier` passed to `VisitorState.memoize` calls `VisitorState.getPath()` (directly or indirectly via methods that use the path, e.g., `findEnclosing`, `getSourceCode`, `getSourceForNode`), an `UnsupportedOperationException` is thrown. The message must explain that `TreePath` is not available because the memoized result is shared across compilation units.

2. **Exception is thrown at access time, not at registration time**
   The error occurs when the supplier *evaluates* the path, not when the supplier is registered with `memoize`. This preserves existing call patterns where the supplier only conditionally reads the path.

3. **Non-memoized `VisitorState` usage is unaffected**
   Regular usage during `match*` calls, utility creation, or any code outside of a memoized supplier must continue to allow `getPath()` as before. The only restriction is on the `VisitorState` passed to the `Supplier` obtained from `memoize`.

4. **Null path propagation is prevented in normal flow**
   To avoid accidental creation of `VisitorState` instances with a null path from regular code, `VisitorState.withPath(TreePath)` must reject null arguments (e.g., via `checkNotNull`).

### Motivation
This prevents a class of subtle correctness bugs where checkers unknowingly cache a value that depends on the first file they process, producing incorrect (or different) results for subsequent files.

### External constraints
- The `Supplier<T>` returned by `VisitorState.memoize` must keep the same type signature; no changes to the public static method signature.
- Existing memoized suppliers that do *not* access `TreePath` must continue to work without any changes.

Prevent access to `TreePath` when computing data to be cached through `VisitorState`.


Title: Error Prone should flag contradictory nullness annotations on the same type or declaration

**Observed behavior:**
Currently, Error Prone does not complain when a type use or declaration has both a nullable and a non-null nullness annotation (for example `@Nullable @NonNull Object field;`, a method parameter `@Nullable @NonNull String x`, or a type argument `List<@Nullable @NonNull String>`). This can easily happen when annotations from different libraries are mixed or when annotations are migrated and old annotations remain. The contradictory intent goes unnoticed.

**Expected behavior:**
A new default-enabled warning should be emitted whenever a type use, method return type, parameter, field, or local variable carries **both**:
- at least one annotation that implies the type is nullable (e.g. `@Nullable`, `@NullableDecl`, `@CheckForNull`, …)
- **and** at least one annotation that implies the type is non-null (e.g. `@NonNull`, `@NonNullDecl`, `@Nonnull`, …)

The check must consider all nullness-relevant annotations present on the element (both declaration‑level annotations and type‑use annotations) as a single combined set. Annotations that do not belong to the known set of nullness‑relevant annotations should be ignored.

Cases where multiple annotations all indicate the same nullness (e.g. two different non-null annotations, or two different nullable annotations) should **not** be flagged.

**Impact:**
This would prevent ambiguous nullness contracts from silently persisting in the codebase. It catches accidental contradictions that can lead to missed null‑check warnings or incorrect assumptions by downstream analysis tools and human readers. The warning makes the intended nullness explicit and forces programmers to resolve the conflict.

Discourage multiple nullness annotations


The `JUnitIncompatibleType` bugchecker crashes when processing `assertArrayEquals` calls where both arguments are explicit type casts (e.g., `assertArrayEquals((byte[]) obj1, (byte[]) obj2)`). Instead of reporting a diagnostic or passing, it throws an internal error. In addition, the existing cast-handling logic is too narrow: it does not correctly resolve the element types of arrays through casts in all cases, which can lead to missed or incorrect incompatibility reports.

## Acceptance criteria
- **No crash:** The checker must handle any `assertArrayEquals` invocation, regardless of the presence or nesting of type casts on its arguments, without throwing an exception.
- **Correct type resolution through casts:** When an argument is a cast expression, the checker should determine the array element type by considering both the cast type and the expression’s type according to standard Java subtyping rules:
  - If the cast type is a subtype of the expression type, the cast type is used.
  - Otherwise (the expression type is the more specific type), the expression’s type is used.
  - This logic should apply consistently for both `assertEquals` (scalar) and `assertArrayEquals` comparisons.
- **Consistency across checkers:** The same cast-handling robustness must be extended to `TruthIncompatibleType`, which shares the same pattern of analyzing assertion subject/argument types. Any `Truth` assertion methods that compare array or iterable element types (e.g., `contains`, `containsExactly`, `containsAnyOf`, etc.) should be covered.
- **Backward compatibility:** The existing detection of incompatible types in assertions without casts must remain unchanged. Only the handling of casts is improved.

## Non-goals
- Modifying the behavior of checkers outside the `collectionincompatibletype` package.
- Introducing new diagnostics or changing the textual output of existing diagnostics.


The `ReturnValueIgnored` check should be extended to flag ignored return values of additional `java.util.Map` methods whose sole purpose is to produce a result. Ignoring these results almost certainly indicates a logic error.

### Desired Behavior

When the return value of any of the following methods on `java.util.Map` is not used (for example, the call appears as an expression statement or its result is discarded), Error Prone should report a `ReturnValueIgnored` error:

* Instance methods: `isEmpty()`, `size()`, `entrySet()`, `keySet()`, `values()`
* Static methods: `Map.of(...)`, `Map.copyOf(...)`, `Map.entry(...)`, `Map.ofEntries(...)`

The existing handling of `containsKey` and `containsValue` should remain unchanged.

These methods are pure queries or factory methods; ignoring their return values is semantically meaningless. The static methods are available in JDK 9+ but should be checked whenever they appear in the analyzed source, regardless of the target version.

### Configuration

To allow users to keep the older, narrower set of `Map` methods if needed, the new cases should be controlled by an Error Prone flag:

* Flag name: `ReturnValueIgnored:MoreMap`
* Default value: `true` (i.e., the additional methods are flagged by default)
* Setting the flag to `false` reverts to the previous behavior (only `containsKey` and `containsValue` are checked)

### Constraints & Compatibility

* Suppression annotations (e.g., `@SuppressWarnings("ReturnValueIgnored")`) must work for the new methods with no changes.
* Error Prone’s severity mechanism and other rule options should continue to apply.
* No new dependencies or JDK version restrictions beyond the checked methods being present in the source code.

Add additional `Map` APIs to `ReturnValueIgnored`.


**Title:** Refaster before-templates using unqualified method names do not match target code

**Description**
When a `@BeforeTemplate` method body contains an unqualified method invocation (e.g. `hashCode()` without an explicit receiver), Refaster does not match the target code that uses the same unqualified call, even when the name resolution would be identical in the target context. This prevents the rule from being applied.

**Acceptance criteria**
- A Refaster rule with a before-template like:
  ```java
  @BeforeTemplate
  public int before() {
    return hashCode();
  }
  ```
  must match a target class like:
  ```java
  public class Example {
    public int example() {
      return hashCode();
    }
  }
  ```
  and apply the corresponding `@AfterTemplate` transformation.
- This must work for instance methods inherited from a superclass (e.g. `hashCode()` inherited from `Object`), not just methods declared in the target class itself.
- It must also work in anonymous-class contexts where the simple name refers to an inherited method of the enclosing instantiation.
- The transformation produced must be identical to what Refaster would produce if the method name were qualified (e.g. `this.hashCode()`).

**Non‑obvious constraints**
- Unqualified method names must be **distinct from local‑variable names** during template matching. Today they are incorrectly treated as local variables, causing a mismatch.
- The fix must not break existing templates that use unqualified identifiers for local variables, fields, or type parameters. Only the matching of method‑symbol identifiers should change.

Handle unqualified instance method names in refaster


**Title:** `CompileTimeConstantExpressionMatcher` should recognise string concatenation with `@CompileTimeConstant` parameters

**Description**

The `@CompileTimeConstant` annotation marks parameters that must receive compile-time constant values. The `CompileTimeConstantExpressionMatcher` is used by several Error Prone checks to determine whether an expression is a compile-time constant. Currently, the matcher does **not** accept expressions formed by concatenating a compile-time-constant `String` parameter with a string literal, even though the result is itself a compile-time constant.

**Steps to reproduce**

```java
void example(@CompileTimeConstant String ctc, String nonCtc) {
  "prefix_" + ctc;       // not recognised as compile-time constant
  ctc + "_suffix";       // same
  nonCtc + ctc;          // should remain not recognised (one operand not constant)
}
```

Any check that calls `CompileTimeConstantExpressionMatcher.matches(...)` will report these expressions as non-constant, despite the `@CompileTimeConstant` annotation.

**Expected behaviour**

`CompileTimeConstantExpressionMatcher` should accept a binary `+` expression whose left and right operands are both of type `String` and each independently satisfies the matcher (i.e., is a compile-time constant expression). This mirrors the JLS rule that constant string concatenation yields a compile-time constant.

Concretely, when:
- the operator is `+` (binary, not compound assignment),
- both operand trees have type `java.lang.String`, and
- both operands individually match `CompileTimeConstantExpressionMatcher`,

the matcher should return `true` for the whole concatenation.

**Edge cases / safety constraints**
- Only `+` should be relaxed for now; other arithmetic or bitwise operators remain as-strict to avoid unintended approved expressions.
- Both operands must be strings – if either operand is not a `String`, the matcher should fall through to existing behaviour.
- If either operand is not a compile-time-constant expression (e.g., an ordinary parameter, method call, etc.), the concatenation must still be rejected.
- The change should only add new matching behaviour, not break existing matches for literals, `null`, static finals, `@CompileTimeConstant` final parameters, or conditional expressions.

**Impact**

This limitation causes false positives in checks that rely on compile-time constant detection, making `@CompileTimeConstant` impractical for APIs that use string concatenation (a very common pattern). Relaxing the matcher for this safe subset would remove unnecessary friction and align with developer expectations without sacrificing correctness.

CompileTimeConstantExpressionMatcher: be more permissive about allowing stuff like "foo" + compileTimeConstantParam.


**TL;DR:** The `UnnecessaryAsync` checker detects local atomic variables that never escape but does not offer an automatic fix. Users must manually rewrite all usages, which is tedious and error-prone.

---

**Observed Behavior**

When `Xlint:UnnecessaryAsync` flags a variable (e.g. `AtomicInteger ai = new AtomicInteger();`) that is confined to a single scope, the diagnostic only reports the problem:

```
warning: [UnnecessaryAsync] This atomic variable only lives inside a single scope and is never shared; use the non-concurrent alternative instead.
```

No suggested fix is provided. Developers must manually:

1. Replace the atomic type with the primitive/java.lang equivalent (e.g., `int`).
2. Replace every method call on the variable (`ai.set(1)`, `ai.get()`, `ai.incrementAndGet()`, …) with the corresponding plain operation.
3. Ensure initializers are correctly translated, including default values for no-arg constructors.

The absence of a fix makes it easy to introduce mistakes during the rewrite.

---

**Expected / Desired Behavior**

When the checker confirms a variable is an unnecessary atomic and does not escape, it should offer an automatic refactoring that:

- Converts the variable declaration to the equivalent non-concurrent type:
  - `AtomicInteger` / `AtomicLong` → `int` / `long`
  - `AtomicBoolean` → `boolean`
  - `AtomicReference<T>` → `T` (or `Object` when used as a raw type)
- Translates every invocation on that variable within the scope, as long as the invoked method is one of the commonly-supported atomic operations. The mapping should be:

  | Method call | Replacement |
  |---|---|
  | `var.set(value)` | `var = value;` |
  | `var.get()` | `var` |
  | `var.getAndIncrement()` | `var++` |
  | `var.getAndDecrement()` | `var--` |
  | `var.incrementAndGet()` | `++var` |
  | `var.decrementAndGet()` | `--var` |
  | `var.addAndGet(x)` | `var += x;` |
  | `var.compareAndSet(expected, update)` | `var = update;` (since concurrency is irrelevant, the expected value and return value are meaningless) |

- Suppresses the fix entirely if:
  - The variable type is not one of the above four `java.util.concurrent.atomic` classes (e.g., a custom atomic wrapper).
  - Any method call on the variable is an unsupported operation – in that case the patch should not be offered, because partially rewriting would produce broken code.

- Correctly handles initializers:
  - No-arg constructor → default primitive/zero value (`0` for `AtomicInteger`/`AtomicLong`, `false` for `AtomicBoolean`, `null` for `AtomicReference`).
  - Single-argument constructor → propagate the argument as the initializer expression.
  - (Raw `AtomicReference` without type arguments uses `Object` as the replacement type.)

- Respects the existing “does not escape” analysis: the fix must only be applied to identifiers that refer to the flagged variable and are not used in a way that would leak the (now removed) atomic object.

**Why this matters**

Local atomic variables used in single-threaded code are purely overhead, both in readability and runtime cost. Providing a one-click fix for the entire scope of the variable dramatically lowers the friction of removing this unnecessary indirection and helps keep codebases clean.

Attempt some fixes for UnnecessaryAsync.


**Title:** `FormatStringShouldUsePlaceholders` produces false positives when the format arguments are passed as an `Object[]` varargs

**Problem:**
The check `FormatStringShouldUsePlaceholders` currently flags calls where the format string is built by concatenation, even when the trailing argument is an `Object[]` that serves as a pass-through varargs parameter. For example:

```java
Preconditions.checkArgument(
    false,
    String.format("%s: %s" + moreFormat, args.toArray()));
```

Here `args.toArray()` returns `Object[]`, and the concatenated segment `moreFormat` provides additional format specifiers that are satisfied by the elements of that array. The checker suggests replacing the concatenation with a single format string that embeds `%s` placeholders, but the suggested rewrite does not (and generally cannot safely) unpack the `Object[]` argument – the resulting code would either fail to compile or lose the varargs semantics entirely.

**Expected behavior:**
The check should silently ignore any invocation where the single varargs argument (immediately after the format string) is of type `Object[]`. In these situations the concatenation is a necessary pattern to combine a static format prefix with a caller-supplied format string, and the checker must not propose a rewrite.

**Impact:**
The false positive causes developers to apply a broken suggestion or waste time investigating an invalid warning.

FormatStringShouldUsePlaceholders shouldn't rewrite calls with a pass-through varargs `Object[]` argument


**Title:** `ImmutableSetForContains` – incorrect exclusions and missing coverage

**Check name:** `ImmutableSetForContains`
**Severity:** SUGGESTION
**Summary:** The checker proposes replacing a `private static final ImmutableList` with an `ImmutableSet` when the constant is only used for membership checks (`contains`, `containsAll`, `isEmpty`). Several false negatives and overly-conservative exclusions remain.

---

### 1. Multiple immutable-list variables in the same call are not properly analyzed

When a method invocation’s receiver is one candidate `ImmutableList` variable, the usage scanner skips scanning the call’s arguments. This means a second candidate variable passed as an argument is never seen, and its usage is incorrectly treated as allowed even if it involves a disallowed list-specific operation.

**Expected:** Every argument of a method invocation must be visited to detect disallowed uses on *other* candidate variables, regardless of the receiver.

---

### 2. `ImmutableList.Builder` created via constructor is not recognized

The rewrite for `ImmutableList.Builder` only matches static factory invocations (`ImmutableList.builder()`). Code that writes `new ImmutableList.Builder<String>().add(...).build()` is left unchanged, keeping the variable as an `ImmutableList`.

**Expected:** When a candidate’s initializer uses `new ImmutableList.Builder<>()`, the fix should replace that constructor type with `ImmutableSet.Builder` exactly as it does for the static factory.

---

### 3. Private constants of nested classes used in enclosing classes are missed

The usage scanner only searches the class where the constant is declared. Private `static final ImmutableList` fields of a nested class that are referenced from a parent class go undetected, so the checker never fires.

**Expected:** Scanning must cover the whole compilation unit (the enclosing `CompilationUnitTree`), not just the class tree, so that usages in enclosing classes are seen.

---

### 4. Enhanced `for` loops do not scan the loop body or variable initializer

`visitEnhancedForLoop` currently returns immediately when the expression being iterated is a candidate list. This prevents detection of list-specific operations that appear inside the loop body (e.g. `MY_LIST.indexOf(...)`), and also skips the loop variable declaration.

**Expected:** When the expression is a candidate, the scanner should still descend into the loop statement and the loop variable, but skip the expression itself (since a simple read for iteration is allowed).

---

### 5. Uniqueness detection should cover class literals and distinct constructor invocations

The “unique elements” heuristic (used to allow conversion even when the list is iterated) today only recognizes distinct constant values and distinct enum constants. Immutable lists built from distinct **class literals** (`Long.class, Double.class`) or **distinct constructor invocations** (`new Long(1), new Double(2.0)`) are not recognized as containing unique elements, so the checker refuses to convert them.

**Expected:** If every element of the `of(...)` call is either a class literal (`Foo.class`) with a distinct class name, or a `new SomeType(...)` with a distinct type name, the list should be considered to have unique elements.

---

### 6. Passing a list with unique elements to a function that accepts `Set` should be allowed

Even when elements are unique, the checker currently marks the variable as disallowed if it is passed as an argument to any method, because the callee’s signature might not explicitly accept `ImmutableSet`. However, many common APIs accept `Collection`, `Iterable`, or `Set` contracts where substituting a set is safe (e.g. `assertThat(...).containsAllElementsIn(LIST)`).

**Expected:** If a candidate variable with unique elements is passed to a method where the corresponding parameter’s erased type is a supertype of `ImmutableSet` (e.g. `Set`, `Collection`, `Iterable`), the usage should be treated as allowed. The conversion must be suppressed only when the parameter’s erased type is `Object` (indicating potential reflection or `.equals()` that relies on the concrete `List` type).

Bug fixes and more coverage for ImmutableSetForContains.


## Bug Report: MustBeClosedChecker crashes with `ClassCastException` on flexible constructor bodies (JEP 447)

**Summary**

When Java 22+ preview features (specifically [JEP 447: Statements before `super()`](https://openjdk.org/jeps/447)) are enabled, constructors may contain arbitrary statements before the explicit `super()` or `this()` call. The `MustBeClosedChecker` currently crashes with a `ClassCastException` when analyzing such a constructor, preventing the checker from running at all on codebases that use this preview feature.

**Steps to reproduce**

1. Enable preview features in Java 22 or later (`--enable-preview --release 22` or similar).
2. Define a class hierarchy where a parent constructor is annotated `@MustBeClosed`, e.g.:
   ```java
   class Parent implements AutoCloseable {
       @MustBeClosed Parent() {}
       public void close() {}
   }
   ```
3. Define a child constructor that places a statement before the `super()` call:
   ```java
   class Child extends Parent {
       Child(int i) {
           i++;          // statement before super()
           super();
       }
   }
   ```
4. Run the Error Prone `MustBeClosed` check (or compile with the checker enabled).

**Observed behavior**

The compilation fails with a `ClassCastException` similar to:

```
java.lang.ClassCastException: class com.sun.tools.javac.tree.JCTree$JCVariableDecl cannot be cast to class com.sun.tools.javac.tree.JCTree$JCExpressionStatement
	at com.google.errorprone.bugpatterns.MustBeClosedChecker.invokedConstructorMustBeClosed(MustBeClosedChecker.java:...)
```

The checker internally assumes that the first statement in a constructor is always the explicit `super()`/`this()` invocation, which is no longer true under JEP 447.

**Expected behavior**

- The checker should **not crash** on constructors containing statements before `super()`/`this()`.
- It should still correctly identify whether the explicitly invoked constructor carries a `@MustBeClosed` annotation, and report the corresponding error if the child constructor itself is not annotated.
- The fix should handle cases where the explicit constructor invocation is **any** statement (not necessarily the first) and is represented as an `IdentifierTree` for `super` or `this`. If the constructor body contains no explicit `super`/`this` call (implicit default), the method should safely return `false` (as it already does for empty bodies).

**Impact**

This crash blocks adoption of the Error Prone `MustBeClosed` check in projects that use JEP 447, a preview feature now available in JDK 22+. The checker must be resilient to future Java language evolution without losing coverage of `@MustBeClosed` enforcement.

**Suggested direction**

The logic in `invokedConstructorMustBeClosed` should iterate over the constructor’s statements and locate the statement that is an `ExpressionStatementTree` whose expression is a `MethodInvocationTree` with a `super` or `this` identifier. Once found, it should check the symbol for `@MustBeClosed`. This approach avoids assumptions about the position of the explicit constructor call and prevents the crash.

Fix MustBeClosedChecker crash on flexible constructors.


**Title:** Extend `EqualsIncompatibleType` to catch mismatched types in `Predicate.isEqual`

**Context**
The `EqualsIncompatibleType` checker currently identifies calls to `Objects.equals` and instance `equals` where the argument and receiver types are demonstrably incompatible (the comparison can never be true). This check should be extended to cover `java.util.function.Predicate.isEqual(Object)`, which creates a predicate that tests equality with the given object.

**Request**
When `Predicate.isEqual(x)` is used (including static imports), and the expression appears in a context that expects a `Predicate<T>`, the checker should report an error if the inferred target type `T` is incompatible with the type of `x`. Similarly, method references of the form `Predicate::isEqual` should be checked: if the functional interface’s sole type argument is incompatible with the argument type passed to the predicate, the checker should flag it.

**Observable behaviour / invariants**
- Direct invocation: `collection.allMatch(isEqual(1))` where the stream element type is `Long` should trigger a warning because `int` is not compatible with `Long` in an equality check via `Predicate<Long>`.
- Method reference: passing `Predicate::isEqual` to a method that expects `Function<Long, Predicate<Integer>>` should trigger a warning, because the predicate will compare a `Long` argument against an `Integer`-typed target.
- The check should only fire when the target type’s type argument can be determined from the surrounding calling context. If the target type has an unexpected number of type arguments (e.g., raw type), no match should be reported.
- The existing handling of `Objects.equals` and instance `equals` remains unchanged.

**Configuration**
Add a flag `EqualsIncompatibleType:MatchPredicate` (default `true`) that allows disabling this new matching if needed for build compatibility or performance.


When an unused variable’s initializer or assignment is a method invocation annotated `@CanIgnoreReturnValue`, order the suggested fixes so that the “preserve side effects” fix (drop the variable, keep the call) is the primary suggestion, rather than the fix that removes the entire statement. This prevents the default suggestion from deleting side-effect‑ful calls whose return value is annotated as ignorable.

For unused variables, suggest the "preserve side effects" fix as the primary fix on methods that are `@CanIgnoreReturnValue`.


**Feature Request: AnnotationPosition should enforce TYPE_USE annotations after modifiers**

Current behavior: the `AnnotationPosition` check flags annotations that are not placed consistently with Google Style, but it does not force strictly TYPE_USE annotations (i.e., those annotated with `@Target(ElementType.TYPE_USE)` and *not* also `DECLARATION`) to appear after modifiers and directly before the type. It also treats annotations that target both `TYPE_USE` and `DECLARATION` inconsistently.

Desired behavior:

1. **Strictly TYPE_USE annotations** (target is only `TYPE_USE`) **must** appear after all modifiers and directly before the type (e.g., `public @TypeUse String foo()`). Any other position triggers a warning with a fix.

2. **Non-TYPE_USE annotations** (no `TYPE_USE` in `@Target`, i.e., `DECLARATION`‑only) **must** appear before all modifiers and after any Javadoc. Deviation triggers a warning with a fix.

3. **Flexible annotations** (annotations that target both `TYPE_USE` and non‑`TYPE_USE`, or have no `@Target`) may be placed either before modifiers or after modifiers. The checker should **not** flag them when they are already in a valid location. However, when they are mixed with strictly positioned annotations and the combination violates the ordering rule (non‑`TYPE_USE` before `TYPE_USE`), the fix should reposition them to restore the correct ordering while retaining their original side relative to modifiers if possible.

4. **Ordering when both strict types are present**: If a declaration has both strictly non‑`TYPE_USE` and strictly `TYPE_USE` annotations, all non‑`TYPE_USE` annotations must appear before all `TYPE_USE` annotations. Flexible annotations are allowed in either position but must not break this relative ordering. A violation triggers a fix that rearranges them accordingly.

5. **Declarations without any modifiers** (e.g., interface methods with no explicit `public`) should be handled as if there were an empty modifier list. Non‑`TYPE_USE` annotations should be placed at the very start of the declaration (before the type), and `TYPE_USE` annotations directly before the type. The fix must produce correct whitespace.

6. **Warning messages** should be updated to use mandatory language:
   - Non‑`TYPE_USE`: “… should appear before any modifiers and after Javadocs.”
   - `TYPE_USE`: “… should appear after modifiers and directly before the type.”

7. **Javadoc and comments** must be preserved and placed before any annotations in the fixed output.

The check should handle all tree kinds where annotations can appear (classes, methods, variables) and produce a single consolidated fix, not multiple conflicting suggestions.

Make AnnotationPosition force TYPE_USE annotations after modifiers.


The `ASTHelpersSuggestions` bug pattern should not suggest that callers use `com.google.errorprone.util.ASTHelpers` when the `ASTHelpers` class itself is not present on the compilation classpath. Previously, the check only suppressed suggestions within the `ASTHelpers` class itself, but it would still fire in codebases that don't depend on the utility, suggesting a static import that would cause a compilation failure. The desired behavior is to issue a warning and offer a fix only if `ASTHelpers` is actually resolvable in the current compilation; if it is not available, the check must stay silent.

Only suggest ASTHelpers if it's already on the classpath


**Title:** `EqualsIncompatibleType` and related type‑compatibility checks miss self‑comparisons on types that lack value equality (e.g., proto Builders)

**Summary**
When two references of the same type are compared using `equals` (or passed to collection methods like `Set.contains`), Error Prone currently treats them as unconditionally type‑compatible. Many types define their own `equals` and this is correct, but some types — notably protocol‑buffer Builders — intentionally **do not** override `equals`. Comparing two distinct instances of such a type always returns `false` (they use `Object.equals`, which is reference equality). The checker should flag these as incompatible‑type uses.

**Steps to reproduce**
1. Define a proto and generate Java code.
2. Write:
   ```java
   MyProto.newBuilder().equals(MyProto.newBuilder());
   ```
3. Run Error Prone with the `EqualsIncompatibleType` check.
4. Observe: **no warning is emitted**, despite the fact that this call is essentially always `false` for distinct Builder instances.

The same issue occurs for other checks that use `TypeCompatibilityUtils`, such as `CollectionIncompatibleType`, `IncompatibleArgumentType`, and `TruthIncompatibleType`. For example:
```java
Set<MyProto.Builder> set = new HashSet<>();
set.contains(MyProto.newBuilder()); // should warn
```

**Expected behavior**
When a final type whose simple name ends with `Builder` does **not** override `equals` (i.e., the type inherits `Object.equals`), the type should be considered **incompatible with itself** for equality and collection-containment purposes.
- `EqualsIncompatibleType` should emit a warning (e.g., “though these types are the same, the type doesn't implement equals”).
- Analogously, `CollectionIncompatibleType` and the other checks should report an incompatibility.

This heuristic must **not** apply to types that naturally have value semantics without an explicit `equals`, such as **enums**, which already provide a correct `equals` via `Enum`.

The behaviour should be gated behind an Error Prone flag (e.g., `TypeCompatibility:TreatBuildersAsIncomparable`) that defaults to `true`, so that projects can temporarily disable it if needed.

**Impact**
Without this check, developers inadvertently write buggy equality comparisons on proto Builders (and similar no-equals classes), assuming value semantics that do not exist. The tool currently provides a false sense of correctness.

Rollforward of https://github.com/google/error-prone/commit/9941ae5eff1d6e98de933bfe3210771e621a9a34: TypeCompatibilityUtils: regard a type as incompatible with _itself_ if it ends with "Builder" and doesn't override equals.


## Feature Request: Warn on unnecessary long-to-int conversion when passing to a `long` parameter

**Summary**
Add a warning that flags method calls where a `long`/`Long` value is explicitly narrowed to an `int` just before being passed to a parameter of type `long`. The conversion is redundant (a `long` can be passed directly) and can accidentally discard bits. The warning should suggest `Longs.constrainToRange()` if a range check is intended.

**Acceptance Criteria**

1. **When the warning must appear**
   For any method invocation, if **all** of the following hold for an argument:
   - The **formal parameter** type is `long` (primitive), **not** `Long`.
   - The **compile-time type** of the argument expression is `int`.
   - The argument expression is one of:
     - A cast of a `long` or `Long` expression to `int` (e.g., `(int) someLong`).
     - An invocation of `Long.intValue()` on a `Long` expression.
     - An invocation of `Ints.checkedCast(...)` or `Math.toIntExact(...)` where the argument is a `long` or `Long` expression.

2. **When the warning must NOT appear**
   - The target parameter is `Long` (the boxed type) — passing `int` there would already cause a type error, so no warning is needed.
   - The argument is an `int` value that was **not** derived from a `long`/`Long` (e.g., passing an existing `int` variable, an `int` literal, or an `Integer` → `int` unboxing).
   - The conversion is performed in a separate statement before the method call (e.g., `int i = (int) x; acceptLong(i);`).
   - The result is used for an `Integer` parameter or assigned to an `Integer` boxed type (out of scope).

3. **Suggested fix message**
   The warning message should clearly state that the conversion is usually unnecessary and, if it is intentional, recommend `Longs.constrainToRange()` as an explicit way to constrain the value without silently losing bits.

4. **Severity**
   `WARNING` — the program compiles but contains a likely mistake or unnecessary operation.

Add new error prone check for converting a long or Long argument to an int when the method parameter accepts a long.


**Title:** Compile-time explosion during bounds inference and stage skipping for deep conditional pipelines

**Background:** Pipelines that construct deep truncated pyramids (e.g., many levels of `select(max(width, height) > 5, ..., 0)`) trigger exponential growth of the internal IR during bounds inference (`Bounds.cpp`) and the skip-stages pass (`SkipStages.cpp`). With N pipeline stages the IR size can grow combinatorially, causing compilation to hang or take an impractical amount of time.

**Desired behavior:** Such pipelines should compile in a reasonable time without altering the generated code or the runtime predicates.

**Scope of changes:**

- **Bounds inference (`BoxesTouched::visit_let` and `visit(const Let *)`):**
  Avoid unconditionally substituting a let-bound value into every box bound. Only substitute when the bound actually contains the let-variable; otherwise retain the `let` expression to preserve sharing and prevent duplication.

- **Value bounds computation (`compute_function_value_bounds`):**
  Run `common_subexpression_elimination` on the min and max expressions before simplifying them, so that shared subexpressions are not expanded naïvely.

- **Skip-stages predicate finder (`PredicateFinder`):**
  - In `visit_let` and `visit(const LetStmt *)`, do not substitute a value into the predicate when the predicate doesn’t contain the variable; use `Let::make` to wrap the predicate instead.
  - In `visit_conditional`, replace the straightforward construction of combined predicates (which duplicates shared subexpressions) with eager constant-folding on logical operators (`make_and`, `make_or`, `make_select`, `make_not`) that immediately simplify trivial cases (e.g., `a && true`, `b || false`) and recognize identical arguments.
  - When simplifying the final compute and allocate predicates in `visit(const Realize *)`, apply `common_subexpression_elimination` before simplification to keep the result compact.

**Constraints:**

- The generated Halide IR after these transformations must be semantically equivalent to the previous output.
- No changes to the public scheduling API or the language are allowed.

**Verification (not part of this request):** As a test, compilations of an artificial truncated pyramid with 20 levels (using `select`, `specialize`, `vectorize`, `parallel`, and edge‑condition boundaries) that previously hung forever should now complete in seconds.

Avoid combinatorial Expr expansion in skip stages and bounds inference


**Title:** `FindSteadyState` incorrectly tightens bounds when devectorizing conditions on a different loop dimension

**Affected component:** `src/PartitionLoops.cpp` – `FindSteadyState::simplify_to_true`

**Description**
When loop partitioning encounters a condition whose type is a vector, it attempts to convert it to a scalar condition using `max_lane`/`min_lane`. That scalar condition is stricter than the original vector condition. In the current code, this stricter expression **replaces** the original `cond` and is then:

- passed to `solve_expression` to compute bounds for the loop being partitioned,
- used in `expr_uses_vars` to check dependencies on inner loop variables,
- and **returned** to the caller as the simplified condition.

If the vector lanes correspond to a dimension that is *not* the loop being partitioned (e.g. partitioning over `x` while vectorization is over `y`), the devectorized condition imposes spurious constraints on the partitioned loop. This causes the prologue/epilogue bounds to be too tight, and the computed result becomes incorrect.

**Expected behavior**
The devectorized expression should only be used locally for solving the loop bounds and for checking inner-loop-variable usage. The original vector condition must be preserved and returned without being overwritten. This ensures that partitioning one loop dimension does not inadvertently restrict another dimension that happens to be vectorized.

**Steps to reproduce (conceptual)**
1. Define a pipeline with a reduction or boundary condition that can be expressed as a vector condition over `y`.
2. Partition the outer loop over `x`.
3. Vectorize the inner loop over `y`.
4. Observe that the output differs from the same pipeline without vectorization / partitioning.

**Acceptance criteria**
- A loop partitioned on `x` and vectorized on `y` with a cross-lane condition produces the same result as its scalar equivalent.
- The steady-state region derived for the partitioned loop is not artificially narrowed by devectorization of conditions on a different loop variable.
- No regression in existing cases where devectorization *should* tighten bounds on the partitioned dimension itself.


Title: Reduce mutex contention in POSIX thread pool when more threads than tasks

**Problem**
When a parallel job offers fewer tasks than the number of worker threads, the existing POSIX thread pool exhibits severe overhead: all threads wake up on a single condition-variable broadcast, contend on the `pthread_mutex_t`, and most find no work, repeat. Pathological cases such as:

```
for i from 0 to 1000000:
    parallel for j from 0 to 2:
        trivial work
```

cause a large fraction of CPU time to be spent inside the kernel on mutex contention, making fine-grained parallelism inside loops unusably slow.

**Desired behavior**
The thread pool should dynamically limit the number of threads actively contending for work to roughly the number of available tasks in the most-recently-submitted job. The remaining threads should sleep on a separate condition until a new job arrives that actually needs them.

**Specific requirements**
- Replace the single `pthread_cond_t state_change` with:
  - a condition for job-owner threads to wait on job completion,
  - a condition for the active set of worker threads to wait for new work,
  - a condition for the reserve set of threads that are intentionally idle when the job’s task count is smaller than the total thread count.
- When `default_do_par_for` enqueues a job of size `S`:
  - If `S` is less than the total number of threads, set a target active-team size to `S`.
  - If `S` is greater than or equal to the total threads, the target should be the full thread count.
- Worker threads must check on wakeup whether they belong to the active set or the reserve set, and transition between sets accordingly. A thread that finds itself over the target active size should decrement the active count and block on the reserve condition until it is explicitly woken by a large job.
- Enqueueing a new job must broadcast the work-available condition for the active set, *and* also broadcast the reserve condition if the job’s size exceeds the current active-team size.
- Shutdown must broadcast all conditions so all threads exit cleanly.
- The implementation must correctly handle nested parallelism (e.g., internal parallel jobs submitted from within a task) without deadlock or starvation.

**Non-goals**
- No changes to the Windows thread pool or the macOS GCD path.
- The public API (`default_do_par_for`, `halide_shutdown_thread_pool`) and the `work` structure’s semantics remain unchanged.
- No changes required to the task scheduling fairness logic.

**Acceptance criteria**
For a loop with 2 tasks and ≥ 4 threads running many iterations, wall-clock time should not degrade significantly compared to running with 2 threads. The `posix_thread_pool.cpp` implementation should show a clear separation of worker-thread wakeup signals as described.

Improve performance when #threads > #tasks


Fix the lowering pipeline ordering so that vectorization happens before GPU thread-loop fusion, preventing incorrect results and an internal compiler assertion when a GPU kernel uses an inner vectorized loop.

**Observed problem**
When a Halide pipeline is scheduled for GPU execution—with blocks, threads, and an inner dimension vectorized (e.g. `.gpu_blocks(...)`, `.gpu_threads(...)`, `.vectorize(...)`)—the generated kernel may produce wrong output or crash with an internal error. The issue appears when the vectorized dimension is inside the thread loop nest.

**Desired behavior**
- The lowering pipeline must apply the vectorization pass **before** the pass that fuses GPU thread loops.
- After this reordering, vectorized loop allocations are properly hoisted before the fusion pass runs.
- The compiler must never encounter a `For` node of type `Vectorized` while extracting register allocations inside the GPU thread-loop fusion pass. An `internal_assert` should guard this expectation.
- Any valid schedule that combines GPU parallelism (blocks/threads) with vectorization on an inner dimension should produce correct results.

**Constraints**
- The fix must not change the behavior for non‑GPU pipelines.
- The reordering must preserve all existing invariants of the vectorization and fusion passes (e.g., proper handling of shared-memory allocations, register lifetimes, and thread barriers).
- The guard assertion serves only as a sanity check and must not trigger for correctly lowered schedules.

Reorder vectorization and fuse gpu thread loops


Disable sliding window for functions that contain reduction update stages. The existing analysis cannot safely prove correctness for reductions, and the old TODO block that attempted it was incomplete and commented out. The optimization must not be applied when the function has any reduction definition (`has_reduction_definition()`). Additionally, when sliding is allowed (pure definitions), the LetStmt nodes that define the shifted min/max bounds must be updated with the newly computed values; the pass previously inserted them with the original expressions, preventing the region from actually shrinking. Add an assertion in the per‑pipeline visitor that no update stage is present, so the unreachable code is clearly marked.


**Title:** Support pipelines that use multiple GPU backends (e.g., CUDA + OpenCL) simultaneously

**Background**
Halide currently restricts each compiled pipeline to a single GPU API (CUDA, OpenCL, Metal, OpenGL, or OpenGLCompute). However, real-world pipelines may need to run different stages on different device backends within one Target. The host-device buffer copy injection pass and the runtime linker prevent this from working correctly.

**Desired behaviour**
A Target that has multiple GPU features enabled (e.g., `CUDA` and `OpenCL`) should allow different scheduling directives (`.gpu_tile(..., DeviceAPI::Cuda)` and `.gpu_tile(..., DeviceAPI::OpenCL)`) within the same Func chain. The generated code must insert necessary host–device and device–device copies so that data moves correctly between devices, and the runtime must load modules for all requested GPU backends.

**Core changes needed**

1. **Buffer copy injection (`InjectHostDevBufferCopies`)**
   - The pass currently tracks which devices read/write a buffer within a limited scope and uses that to decide whether a buffer is “on a single device”. That decision must instead consider **all devices ever touching the buffer** across the entire pipeline, not just within a single copy round.
   - Buffers that are touched by more than one non-host device (or by a non-host device other than the one they were first touched on) must never be allocated with a combined host+device allocation. They should fall back to separate host and device allocations, and explicit copies must be injected appropriately.
   - The logic that decides whether to elide the host-side allocation or make a combined zero-copy allocation should switch from a per-scope `on_single_device` to a lifetime `devices_touched` set.
   - Edge case: When a buffer is used by *only* the host and one non-host device, existing single-device optimizations remain valid.

2. **Runtime linker (`LLVM_Runtime_Linker` / `get_initial_module_for_target`)**
   - The function currently uses `else if` chains when selecting GPU runtime modules; only the first matching backend is linked. It must be changed so that *every* enabled GPU backend can be linked into the same module, allowing multiple device interfaces to coexist at runtime.
   - This applies to CUDA, OpenCL, Metal, OpenGL, OpenGLCompute, and HVX (the latter is not a GPU but follows a similar pattern).

3. **Metal runtime (`halide_metal_device_free`)**
   - The debug assertion that checks whether `buf->host == buffer_contents(metal_buf)` is too strong when multiple backends are present: the host pointer may be a zero-copy mapping from a different device and not match the Metal buffer contents. This assertion should be removed (or relaxed) so that a Metal buffer can be freed even when the host pointer is owned by another device interface.

**Invariants / Safety**
- Single-API pipelines must continue to work unchanged and benefit from existing optimizations (combined allocations, host elision).
- When a buffer is touched by two or more different GPU APIs, the system must not attempt to avoid copies between them; it should inject the necessary device-to-device copies via the host.
- The runtime must remain linkable on platforms that only support a subset of the requested backends; the change to multiple `if` blocks should only add modules for features present in the target.

**Acceptance criteria**
- A pipeline that uses, for example, `DeviceAPI::CUDA` on one stage and `DeviceAPI::OpenCL` on another stage compiles and executes correctly, with data transferred between devices as needed.
- No regression in single-GPU-API pipelines.
- The Metal debug assertion no longer fires when freeing a buffer while a second device interface is also active.


**Bug:** `rfactor` fails to prove associativity for unsigned saturating addition expressed with `select`, blocking parallel reduction scheduling.

**Observed Behavior**
When a reduction uses unsigned saturating addition implemented via a `select` expression (e.g., `select(a > max - b, max, a + b)` or `select(a < ~b, a + b, max)`), the associativity check fails. Consequently, `rfactor` does not split the reduction, and the pipeline cannot exploit parallelism over the reduction domain—even though the operation is safe to reassociate.

**Expected Behavior**
`rfactor` should recognize the common saturating-add idioms for the unsigned integer types `uint8`, `uint16`, and `uint32` used in `select` expressions. Specifically, the associative-operations table should contain patterns that match `select(a < max - b, a + b, max)` and `select(a < ~b, a + b, max)` (including `<=` variants), so that reductions involving those patterns are proven associative and become eligible for `rfactor`.

**Impact**
Image pipelines that accumulate into fixed-width unsigned buffers with saturation (e.g., pixel-blending, histogram aggregation, or any reduction that must clamp to the type’s maximum) cannot be parallelized via `rfactor`. Users are forced to either accept sequential reduction or manually rewrite the reduction into an `rfactor`-friendly form, losing the natural saturating-add expression.

**Proposed Fix**
Update the per-type populate functions for unsigned integer `select` patterns (the `populate_ops_table_single_uint*_select` helpers) to include the correct saturating-add patterns, replacing the currently invalid entries that do not express the addition of the two operands. The patterns should cover the equivalence of `select(x < tmax - y, x + y, tmax)` and `select(x < ~y, x + y, tmax)`, with both strict and non‑strict comparisons where ambiguous signedness is not an issue.

Fix saturating add patterns in rfactor (#8839)


The OpenGL shader intrinsics for texture load and store currently use `Float(32)` as their type and perform range conversion on the host side using a scale factor plus an epsilon. This epsilon causes rounding errors, particularly for `uint8` and `uint16` buffers, leading to off-by-one mismatches between CPU and GPU outputs.

**Desired behavior:**

1. The `glsl_texture_load` intrinsic should return the actual integer type (`UInt(8)` or `UInt(16)`) and generate GLSL that multiplies the float texture sample by the integer maximum (e.g., `255.0` for `uint8`) – without any epsilon – to reconstruct the original integer value.

2. The `glsl_texture_store` intrinsic should accept the same integer type and generate GLSL that divides the integer value by its maximum before writing to `gl_FragColor`, again without epsilon.

3. The intrinsic’s first argument (the buffer name) must be a string literal, not a `Variable` node carrying a type, so that buffer identification is type-agnostic and consistent.

This ensures integer values round-trip exactly through the shader when using `UInt(8)` and `UInt(16)` buffers, eliminating the off-by-one errors observed previously.

Fix rounding behavior of uint8s in shaders


**Title:** Let sliding window pass fold single-iteration loops whose bounds depend on the slid variable

**Background**
The `SlidingWindowOnFunctionAndLoop` pass walks loops looking for producer-consumer patterns where it can reuse previously computed values instead of recomputing them. When it encounters an inner `For` loop it checks whether the loop’s min and/or extent vary with the loop variable it is currently sliding over (`loop_var`). The current implementation relies on `is_monotonic(…, loop_var)` to decide whether it is safe to enter the loop. If either the min or the extent is not provably *Constant* with respect to `loop_var`, the pass conservatively leaves the loop untouched and does not attempt any sliding inside it.

**Problem**
In practice, scheduling strategies like `.compute_at(h, x).vectorize(c).reorder(c, x)` can produce inner loops with an extent of exactly 1, whose min (or the loop variable binding) still depends on the outer loop variable being slid over. The pass currently bails out on such loops, missing an opportunity to fold sliding windows that would otherwise be valid.

The pass *can* already handle `LetStmt` bindings where the value depends on the sliding variable; a single-iteration loop is semantically equivalent to such a let. If we could convert these loops into lets, the sliding window logic would apply correctly.

**Proposed change**
In `SlidingWindowOnFunctionAndLoop::visit(const For *)`, before the existing monotonicity check, add a special case:

- If the loop extent evaluates to `1` (`is_one(extent)`), treat the loop as a let binding:
  - Build a `LetStmt` with the loop variable name, the min as the value, and the loop body.
  - Run the sliding window mutation on that let statement.
  - Unpack the result back into a `For` node with the original loop properties (name, min, extent, for_type, device_api), replacing the body with the mutated let body.

If the extent is not 1, keep the existing behaviour: skip the loop if bounds depend on the sliding variable, otherwise proceed as usual.

**Acceptance criteria**
- When a `For` loop with extent 1 is encountered and its min depends on the sliding variable, the pass enters the loop and performs sliding window folding as if the loop were a `LetStmt`.
- This does not regress correctness for `For` loops with extent > 1 or for those where the min does not depend on the sliding variable.
- The optimization correctly handles vectorized inner dimensions that collapse to a single value after scheduling.

**Internal references**
- `SlidingWindowOnFunctionAndLoop` in `src/SlidingWindow.cpp`
- `visit(const For *op)` and `visit(const LetStmt *op)` within that class
- `is_one`, `LetStmt::make`, `For::make` helpers available in the IR


Title: saturating_add and saturating_sub intrinsics need widening-free lowering and support in `lower_intrinsic`

**Observed behavior**
When using the `saturating_add` and `saturating_sub` intrinsics (e.g. on `int64`/`uint64`), the compiler eventually fails. The existing lowering path attempts to widen the operands (`widening_add` / `widening_sub`) and then cast back, but for 64‑bit types the required 128‑bit intermediate type does not exist in Halide. Moreover, the `lower_intrinsic` function currently ignores these two intrinsics entirely, so they remain as call nodes that many backends cannot handle.

**Expected behavior**
Saturating addition and subtraction must be lowerable for **all** integer types supported by Halide (8, 16, 32, and 64‑bit, both signed and unsigned) without requiring a wider intermediate type. The `lower_intrinsic` function should lower `saturating_add` and `saturating_sub` into plain scalar operations that perform correct saturating arithmetic.

**Proposed solution**
Implement lowering directly via min/max clamping, eliminating any dependency on widening. For example:
- `saturating_add(a, b)` → `simplify(clamp(a, a.type().min() - min(b, 0), a.type().max() - max(b, 0)) + b)`
- `saturating_sub(a, b)` → `simplify(clamp(a, a.type().min() + max(b, 0), a.type().max() + min(b, 0)) - b)`

These formulas correctly compute the saturating result for any bit width and for both signed and unsigned types, and the internal `simplify` pass keeps the expression from becoming needlessly large.

**Impact**
Without this fix, saturating arithmetic is broken or unavailable on 64‑bit data, limiting the usability of Halide for algorithms that require saturating integer math (e.g., signal processing, pixel‑value clamping, fixed‑point arithmetic).

Lower saturating arithmetic without widening (#5662)


Title: Nested parallel for loops hang or produce incorrect results on x86

**Observed behavior**
When using multiple `.parallel()` scheduling directives on the same pipeline (e.g., `.parallel(x).parallel(y).parallel(z)`), executing the pipeline on x86 either hangs indefinitely, crashes with a segmentation fault, or yields corrupted output. The problem appears reliably even for small workloads such as a 3‑dimensional function realized at 8×8×8.

**Root cause analysis**
The current `do_par_for` implementation in the x86 runtime (`architecture.x86.stdlib.cpp`) creates one thread per work item and immediately joins them. This approach fails under nested parallelism for several reasons:
- **Unbounded thread creation**: each level spawns its own threads, quickly exhausting system resources or hitting OS limits.
- **No work‑stealing participation**: the calling (master) thread blocks and does not contribute to the actual work, which can deadlock when inner parallel loops require a free thread to make progress.
- **Missing hierarchy control**: outer parallel loops do not wait for all inner parallel sub‑tasks, leading to races on shared state and unsynchronized returns.

**Expected behavior**
The runtime should support arbitrary nesting of `do_par_for` calls on x86. Specifically:
- A fixed‑size thread pool (e.g., 8 workers) should be used so that thread creation stays bounded regardless of the nesting depth.
- The calling thread must participate in the work queue as a worker; it should only return when every sub‑task, including any inner parallel loops it spawned, has completed.
- The implementation must be thread‑safe so that multiple concurrent `do_par_for` invocations (outer and inner loops) can share the same queue and synchronization primitives without deadlocks or data races.

With these properties, a pipeline such as
```
f(x, y, z) = x*y + z*k;
f.parallel(x).parallel(y).parallel(z);
Image<int> result = f.realize(8, 8, 8);
```
should produce the correct element values and terminate promptly, the same as the non‑parallelized version modulo performance.

**Impact**
Users cannot apply `.parallel()` to more than one dimension on x86 without risking crashes or incorrect results. This renders nested parallelism unusable and forces manual workarounds (e.g., fusing loops or avoiding parallelism), which defeats the purpose of the high‑level scheduling API.


Fix deinterleave and simplify handling of single-lane shuffles to improve scalarization of predicated loads.

- In Deinterleave, when a Shuffle extracts exactly one lane, recursively deinterleave the appropriate input vector rather than constructing a new Shuffle. This avoids leftover shuffle nodes that cannot be further simplified, enabling the downstream scalarization of predicated loads to produce better code.
- In Simplify_Shuffle, for single-lane extractions from a non‑Variable vector, use `extract_lane` to delegate to the regular simplification logic instead of manually rewriting only `Ramp` and `Broadcast`. This generalizes the simplification and allows more cases to be optimized.
- Add a bounds check in the single‑lane deinterleave path; if the computed index falls outside the input vectors, raise an internal error.

Improve the code quality of scalarized code (#6218)


Title: GLSL backend: mod operator fails with integer arguments

**Description**
The `CodeGen_GLSL` class currently emits a direct call to the GLSL built‑in `mod` for the Halide `Mod` IR node, without any argument casting. In GLSL (at least up to version 120), the `mod` function is only defined for floating‑point types. This means that any Halide expression using `%` with integer operands inside an OpenGL kernel will produce invalid shader code or a runtime error.

The visitors for `Max` and `Min` in `CodeGen_GLSL` already handle this correctly: they cast integer operands to `float`, call the built‑in (`max`/`min`), and then cast the result back if the original Halide type was integral. The `Mod` visitor must be updated to follow the same pattern so that integer remainder expressions (`y % 3`, etc.) produce valid GLSL.

**Expected behavior**
- Expressions like `y % 3` inside a GLSL‑targeted `Func` should compile and yield the correct integer remainder.
- The generated GLSL should use `mod(float(a), float(b))` and cast back to the integer result type when needed, matching how `min`/`max` are already handled.
- There should be no regression for existing float‑typed `%`; the behavior when both operands are already floats must stay unchanged.

**Constraints / notes**
- The casting logic should be consistent with `visit(const Max *)` / `visit(const Min *)`, which use `Float(32)` for all integer widths.
- The fix should avoid unnecessary duplication of the cast‑call‑cast pattern; a shared helper would be ideal but is not mandatory.

GLSL: fix codegen for mod function, add small test for special functions


Ensure that x86 CPU feature detection respects operating-system state support for AVX, AVX-512, and APX, otherwise enable missing feature detections, and refactor `cpuid` to be safer.

**Problem**
- x86 features that require XSAVE-managed state (AVX, AVX-512, APX) are currently enabled based solely on CPUID bits, without checking whether the OS has enabled the corresponding XCR0 state components. This can cause illegal-instruction faults when the OS does not save/restore the extended registers.
- Several advertised Halide features have missing or broken host/runtime detection: FMA4 (AMD extended leaf), AVX10, and X86APX.
- On ARM Windows, SVE2 detection is absent; only SVE is probed.
- The `cpuid` wrapper in both the host code (`Target.cpp`) and the runtime (`x86_cpu_features.cpp`) returns results via out-parameters and is not marked `[[nodiscard]]`, silently discarding results on misuse.

**What to do**
1. **OS state checks (x86)**
   - Before enabling AVX, F16C, or FMA, verify that the OS has set XCR0 bits for XMM (bit 1) and YMM (bit 2).
   - Before enabling any AVX-512 variant, additionally verify XCR0 bits for opmask (5), ZMM_Hi256 (6), and Hi16_ZMM (7).
   - Before enabling X86APX, verify XCR0 bit 19 (APX extended GPRs).
   - Apply these checks consistently in both `Target::calculate_host_target()` and the runtime CPU feature detection (`halide_get_cpu_features`).

2. **New feature detections**
   - **FMA4**: Read CPUID extended leaf `0x80000001`, test ECX bit 16, and only enable if OS AVX support is present (uses VEX-encoded YMM instructions). Expose in both host and runtime.
   - **AVX10**: CPUID leaf 7, EDX bit 19; requires OS AVX-512 state. Expose `AVX10_1` and choose vector width from leaf `0x24`.
   - **APX**: CPUID leaf 7 subleaf 1, EDX bit 21; requires XCR0 bit 19.
   - **ARM Windows SVE2**: Use `IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)` and set the `SVE2` feature when present, analogous to the existing SVE probe.

3. **cpuid refactor**
   - Change the `cpuid` function (both compiler-time and runtime versions) to return a `cpuid_result` struct containing `eax, ebx, ecx, edx`, and mark it `[[nodiscard]]`.
   - Provide an analogous `xgetbv` helper that returns the low 32 bits of the requested XCR for the runtime, and the full 64-bit value in host code.
   - Update all call sites to use the returned struct, especially the host detection flow and the runtime `halide_get_cpu_features`.

4. **Runtime parity**
   - Register all newly detected features as “known” in the runtime’s `halide_get_cpu_features` and only mark them available if the CPU and OS support checks pass, exactly replicating the logic used in `Target::calculate_host_target`.

Fill in missing CPU detection logic (#9017)


**Title:** Pipeline constructor overrides explicit `compute_at`/`store_at` on output Funcs when applied before custom trace handlers
**Area:** Scheduling / Lowering boundary
**Severity:** moderate (silently discards user schedule)

### Context
When a user schedules an output `Func` with `compute_at` or `store_at` and later calls `set_custom_trace` on that `Func`, the custom trace path internally constructs a `Pipeline`. The current `Pipeline` constructor unconditionally calls `compute_root().store_root()` on each output function, then freezes it. This nukes a previously applied `compute_at`/`store_at` schedule, leaving the output incorrectly scheduled at root level.

### Desired behavior
1. `Func::set_custom_trace` (and anything else that triggers `Pipeline` construction) must not alter an already-scheduled output’s compute/store level.
2. Outputs that have no explicit compute/store level should still end up scheduled at root, but that default should be applied during the final lowering pass, not during `Pipeline` construction.
3. After lowering, every output function must behave as if `compute_root().store_root()` was called – without requiring the user to call those methods explicitly.

### Specific ask
Move the enforcement of `compute_root().store_root()` on pipeline outputs out of the `Pipeline` constructors (`Pipeline::Pipeline(Func)` and `Pipeline::Pipeline(vector<Func>)`) and into the `lower()` pipeline (e.g., inside `Lower.cpp`’s lowering flow). The lowering should apply these defaults *after* all scheduling, wrapper substitutions, and freeze operations have completed, so that a user’s explicit `compute_at`/`store_at` on an output is respected and only missing defaults are filled in.

### Acceptance criteria
- If a user writes:
  ```
  f.compute_at(g, x);
  f.set_custom_trace(my_trace);
  ```
  the resulting pipeline still produces `f` inside the loop over `x` of `g`, not at root level.
- No change in the final compute/store level of outputs that have no user-specified scheduling; they remain at root.
- The fix should not break other `Pipeline` consumers (JIT compilation, AOT compilation, realization, etc.).

Move compute/store root reset on outputs to lowering


**Bug Report: Incorrect results from non-rectangular reductions with self-referencing predicates or random conditions**

## Observed Behavior

When using a reduction domain (`RDom`) with a `where` clause (non-rectangular reduction), the final output is incorrect in at least two scenarios:

1. **Self-reference in the predicate** – the predicate of the `RDom` calls the same function that is being updated. For example:

   ```c++
   Func f("f");
   f(x, y) = x + y;
   RDom r(0, 100, 0, 100);
   r.where(f(r.x, r.y) >= 40);
   f(r.x, r.y) += 1;
   ```

   The result stored in `f` does not match the expected values (some elements that should have been incremented are left unchanged, or vice versa).

2. **Random expression in the predicate** – the `where` clause uses `random_float()` or similar random functions:

   ```c++
   Expr e = random_float() < 0.5f;
   f(x, y) = Tuple(e, x + y);
   RDom r(0, 100, 0, 100);
   r.where(f(r.x, r.y)[0]);
   f(r.x, r.y) = Tuple(f(r.x, r.y)[0], f(r.x, r.y)[1] + 10);
   ```

   The number of updated elements is wildly off (often far outside the expected statistical range), and the stored values are wrong.

On further inspection, the miscompilation appears to stem from two underlying problems:

- **Missing predicate handling during update definition finalization.** In `Function::define_update`, the reduction domain’s predicate is not traversed by the existing checks that freeze called functions and lower random expressions. This means self-references in the predicate are not properly frozen, and random calls are not properly rewritten with deterministic seeds, leading to incorrect codegen.

- **Bogus bounds tightening for unsupported boolean nodes.** The analysis in `AndConditionOverDomain` (which tightens loop bounds for non-rectangular reductions) encounters boolean subexpressions built from `Cast`, `Load`, or `Call` nodes. These are not handled by the current solver, but instead of failing/backing off it continues as if the condition is absent, potentially producing invalid interval bounds and thus incorrect computation. The compiler should either conservatively refuse to tighten bounds when these nodes appear (failing the optimization step safely) or handle them correctly; the current silent fallthrough produces wrong code.

## Expected Behavior

1. **Self-referencing predicates should work correctly.** The system must handle predicates that reference the function being updated just as it handles them in the update values. All called functions should be frozen and any random expressions properly lowered.

2. **Random predicates should produce correct statistical results.** Random number generation inside an `RDom`’s `where` clause must receive stable seeds tied to the reduction domain’s iteration variables, so that repeated evaluations are deterministic and the computation yields the expected number of updates.

3. **Bounds analysis must not silently ignore unsupported expressions.** When `AndConditionOverDomain` encounters `Cast`, `Load`, or `Call` nodes in a boolean expression that cannot be solved into intervals, it must either conservatively bail out of that particular condition tightening (e.g., by calling `fail()`) or otherwise avoid using the unanalyzable condition to derive bounds. It must not proceed as if the condition were true or false, as that leads to silently incorrect loop bounds.

## Impact

Non-rectangular reductions are a powerful feature, but these bugs make any usage that involves self-references or random predicates silently produce wrong results. This severely limits the types of algorithms that can be expressed (e.g., iterative stencils that reference their own previous state, or stochastic sampling patterns). Users encounter mysterious wrong outputs without any compiler error or warning, making debugging very difficult.

Added more test cases for non rect RDom; some bug fixed


Fix a bounds-inference bug where nested splits with factors that do not divide the extent cause an undefined variable reference.
In the loop nest builder, compute the inner loop’s starting index using `old_extent` and `min_extent_produced` rather than referencing a non-existent `max_min`.

Also, correct the minimum-realization-size helpers for pure definitions and reduction updates (`min_extent_produced` / `min_extent_updated`): they must ignore fused dimensions when walking the split tree. Fuse should not contribute to the product of split factors, and the walk must stop at the first non-split (or fuse) without following the `outer` of a fuse. The helpers should accept a dimension name instead of an index.

Fix a minor bug in bounds inference, added a test for fuse


We need to extend `skip_stages` to handle `define_extern` stages that produce external array functions. Currently the skip logic only recognizes internal Halide calls that reference a buffer. For `define_extern` stages, the produced buffer is passed as an argument to an external call (e.g. a variable named `func.buffer`), but the existing visitors (`PredicateFinder`, `MightBeSkippable`) do not treat that as a use of the buffer. As a result, stages that are only consumed via external array functions are never skipped even when the output is conditionally discarded.

Additionally, `address_of` intrinsic calls (e.g. `address_of(func(...))`) do not represent an actual call to `func`; the visitors should ignore the `address_of` itself and only examine the arguments of the inner call to correctly detect buffer usage.

Make `skip_stages` correctly skip `define_extern` stages when guarded by a false predicate, with the same reliability as for internal Halide functions across selects, if-then-else, tuple-valued outputs, and non-trivial schedules.

Modify skip stages support to handle external array functions. Code is


**Title:** Ensure deterministic checkpoint blocks via canonical signature ordering and validation

**Description:**
Checkpoint blocks are produced by multiple federation nodes. Currently, the signature set embedded in a checkpoint’s `HefPostEcip1097` extra fields can arrive in any order, leading to non‑deterministic block hashes even when the same set of valid signatures is available. This breaks the determinism goal established by ETCM‑173.

We need two changes:

1. **Block generation:** When building a checkpoint block (the code path that creates a new block on top of a parent with a given checkpoint), the signatures in the checkpoint must be sorted into a canonical order before they are written into the block header. The canonical order is lexicographic over the triples `(r, s, v)` of each `ECDSASignature`. This guarantees that every correctly operating node produces exactly the same block for the same parent and valid signature set.

2. **Block validation:** The validator responsible for checkpoint blocks must reject any incoming block whose checkpoint signatures are not in the canonical order defined above. A new, distinct error (e.g., `HeaderInvalidOrderOfCheckpointSignatures`) should be returned when the signatures are unsorted. This validation should run *before* the existing signature validity and count checks.

**Additional constraints:**
- The lexicographic ordering must be implemented as an implicit `Ordering[ECDSASignature]` so that `.sorted` works naturally.
- The error message used when a checkpoint is missing from a block that should have one is duplicated; extract it into a shared constant to avoid drift between the order validation and the signature validation paths.
- Existing tests that inject invalid signatures or duplicate signers must not be broken by the new ordering; helper code that constructs checkpoint signatures should always produce them sorted to match the new generation rule.

**Acceptance criteria:**
- A checkpoint block generated with the same parent and same set of signatures (regardless of input order) always has an identical hash.
- A checkpoint block whose signatures are not in lexicographic `(r, s, v)` order is rejected with `HeaderInvalidOrderOfCheckpointSignatures`.
- Blocks with missing checkpoints continue to produce the appropriate error, now derived from a shared definition.

etcm-174 validate checkpoint signatures for lexicographical order


**Title:** Prune incoming handshaked peers based on average response rate instead of random selection

**Background**
When we reach the limit of incoming handshaked peers, the `PeerManagerActor` currently disconnects a random subset of them after a minimum age. This can evict peers that are actively serving us useful data while keeping unresponsive connections.

We already collect per-peer statistics in `PeerStatisticsActor`, which tracks `responsesReceived` and `firstSeenTimeMillis` for handshaked peers.

**Desired behavior**
Instead of pruning randomly, we should prefer to keep peers with a higher average number of responses received per second over their lifetime. Peers that provide less value (low responses/sec) should be pruned first.

The pruning logic should:

1. Still honour the existing minimum prune age (`minPruneAge`), so peers that haven’t been connected long enough are not considered.
2. Obtain the relevant statistics for all connected peers before deciding whom to prune. This likely means requesting stats from `PeerStatisticsActor` and waiting for the reply, rather than pruning synchronously when an incoming connection is rejected.
3. Compute a priority score for each candidate peer:

   **priority = responsesReceived / (age in seconds since firstSeenTimeMillis)**

   - If `firstSeenTimeMillis` is missing, treat the priority as `0.0` (lowest, so the peer is a strong pruning candidate).
   - If the age would be zero or negative (due to clock skew), treat the priority as `0.0` as a safety measure.
4. Sort the eligible peers by this priority (ascending) and prune the N peers with the lowest scores, where N is determined by the existing `numberOfIncomingConnectionsToPrune` calculation.

The `ConnectedPeers.prunePeers` method currently selects candidates randomly. It should be refactored to accept an optional priority function (`PeerId => Double`) that defaults to returning `0.0` to preserve existing behaviour for callers that don’t care about priority. The priority-based selection should replace the random shuffle.

**Integration notes**
- The request for stats should be made with a reasonable timeout so the pruning doesn’t block indefinitely.
- The overall count of peers to prune is unchanged; we’re only changing *which* peers are selected.
- Peers for which we have no stats (e.g., not yet recorded) should be treated as priority 0.0 and are therefore more likely to be pruned, which seems acceptable.

**Affected components (informative)**
- `ConnectedPeers.prunePeers`
- `PeerManagerActor` (the part that reacts to a full incoming handshaked peer set by pruning)

ETCM-446: Use the average received response per second as priority for pruning.


We need the application state storage to track the latest known checkpoint block number.
This value should be persisted across restarts, default to `0` when no checkpoint is stored, and be usable atomically within the storage’s transactional batch-update model.

Acceptance criteria / observable behavior:
- There must be a way to **get** the latest checkpoint block number; returns `0` when none has been stored.
- There must be a way to **set** the latest checkpoint block number, producing a batch update that can be committed.
- There must be a way to **remove** the stored checkpoint block number, so subsequent reads return the default `0`.
- The implementation must follow the patterns already established for other `BigInt`-valued numeric settings in the same storage (e.g., best block number, sync starting block, estimated highest block).
- The storage key space must not collide with existing keys, and the key type/representation should be consistent with the rest of the storage.

Edge cases and constraints:
- When the storage is empty (e.g., first run), getting the checkpoint block number must return `BigInt(0)`, not throw.
- Removing a non-existent checkpoint should be harmless (idempotent).
- Storing, retrieving, and removing should integrate with the existing transactional commit semantics (`commit()` after batch updates) so that changes are not visible until committed.

etcm-74 added checkpoint block number methods to appStateStorage


**Title:** RegularSync branch resolution should not transition to `running` before processing headers; replace `Thread.sleep` with `eventually` in related tests

**Context**
In `RegularSync`, when a peer sends block headers that form a valid branch, `handleBlockBranchResolution` merges them with the current header queue and passes them to `processBlockHeaders` for validation and import.

**Problem**
Currently the branch-resolution code calls `context become running(...)` *before* calling `processBlockHeaders`. This premature state change can leave the actor in an inconsistent state (e.g., logged as `running` while still processing an invalid or incomplete branch), which causes several tests that wait for blacklisting or other state updates to be flaky. The tests currently use `Thread.sleep` with arbitrary durations to allow the actor to settle; this is fragile and makes the test suite slower and less reliable.

**Desired behavior**
- Remove the early `context become running(...)` call inside the branch-resolution path; rely on `processBlockHeaders` (or the subsequent message handling) to transition the actor state correctly.
- Update the test suite (likely in `RegularSyncSpec`) so that all assertions about eventual actor state (e.g., `isBlacklisted(peer)`) use `eventually` with an appropriate timeout instead of `Thread.sleep`.

**Acceptance criteria**
- Branch resolution no longer transitions to `running` before the headers are actually processed.
- All `Thread.sleep` calls in the regular sync test spec are replaced with `eventually` blocks (or removed).
- Existing behavior for normal syncing and branch resolution remains unchanged; the test suite passes consistently without relying on fixed sleeps.

Remove Thread.sleep and use eventually block instead


As part of ETCM-393 we want to eliminate reliance on `DiscoveryService.lookupRandom` inside the peer discovery manager. Currently the random-node stream calls `service.lookupRandom` to pull a fresh set of random nodes on demand. We'd like to replace that with the standard `service.lookup` method, supplying a freshly generated random node ID as the lookup target.

The observable behavior should remain unchanged: each time the client needs random nodes, it performs a discovery lookup toward a new, unpredictable target.

The target ID should be constructed from random bytes that are the same length as the local node’s ID. Because the discovery service hashes the key internally for XOR distance calculations, it doesn’t matter what the raw bytes are — randomness is sufficient. Use an ordinary PRNG to fill a byte array of that length, wrap the bytes in a `BitVector`, and then construct a `PublicKey` (which acts as the `Node.Id`). Make sure the necessary imports (`PublicKey`, `BitVector`) are added.

There is no need to introduce any new configuration or external randomness source; the standard library random is adequate here.