Stop relying on mutable WildcardType.bound outside validated contexts
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 4.1k
- Forks
- 370
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 68
Description
Background
PR #1849 fixes one false positive caused by javac mutating Type.WildcardType.bound while computing a supertype. A wildcard object can be shared between parameterized views, so this field can describe whichever generic context javac processed most recently rather than the type NullAway is currently analyzing.
WildcardType has no back-pointer to its containing parameterized type or type-argument index. Once code reduces a type argument to a bare wildcard, the correct contextual formal variable generally cannot be reconstructed. Dependent bounds such as Pair<T, U extends T> also require the enclosing type arguments to compute the effective upper bound.
Remaining suspicious reads
Production reads remain in these areas:
GenericsUtils: general effective-upper-bound computation and nested wildcard/capture resolution. Callers include generic-method constraint solving, functional-interface target grounding, enhanced-for element types, and wildcard read nullness.TypeSubstitutionUtils: annotation restoration for unbounded and lower-bounded wildcards, including captured wildcards. These paths process results of operations such asasSuper,memberType, andsubst, which can recontextualize shared wildcards.AddAnnotationToNestedTypeVisitor: applying library-model annotations to implicit wildcard upper bounds and captured wildcards.GenericsChecks: capture provenance used in wildcard-bound diagnostics. This is primarily diagnostic-only, but it can report a stale upper bound or the wrong originating formal variable.
The assignment to updatedWildcard.bound in TypeSubstitutionUtils appears intentional and safe because it targets a freshly allocated wildcard and a detached type variable.
Possible direction
- Preserve the enclosing
ClassType, argument index, and corresponding declaration type variable while traversing direct wildcard arguments. - Use capture conversion of the complete containing type when an implicit upper bound is needed, especially for dependent formal bounds.
- For an existing
CapturedType, prefergetUpperBound()only after accounting for explicit or default nullness that NullAway may have restored into its backing wildcard. - Narrow or remove helper APIs that accept only a bare implicit wildcard.
- Keep diagnostic-only cleanup separable from correctness-sensitive changes.
A naive change to always use CapturedType.getUpperBound() is not sufficient: existing tests show that NullAway can restore nullness annotations into a detached capture's backing wildcard while javac's structural capture upper bound lacks those annotations.
Acceptance criteria
- No correctness-sensitive code trusts
WildcardType.boundwithout validated containing context. - Dependent bounds, F-bounds, explicit bound annotations, unannotated-code defaults, classfile wildcards, and library-model annotation paths remain covered.
- Diagnostic provenance does not rely on a potentially stale field, or degrades gracefully when context is unavailable.
This is follow-up work to #1840 and PR #1849. It is related to, but distinct from, #1576, which tracks computing the JLS effective upper bound when an explicit wildcard bound and a formal type-variable bound both apply.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the suspicious WildcardType.bound reads in GenericsUtils, TypeSubstitutionUtils, AddAnnotationToNestedTypeVisitor, and GenericsChecks, along with the existing tests covering dependent bounds, F-bounds, annotations, and captures. Validate each path with its containing type context and preserve restored nullness; done means correctness-sensitive code no longer trusts stale bounds and diagnostic provenance degrades safely without context.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- devtools
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100