Conversation
There was a problem hiding this comment.
Pull request overview
Fixes a Checker Framework crash when PropagationTypeAnnotator encounters a wildcard related to a raw declared type (as reported in #792), and adds a regression test to prevent recurrence.
Changes:
- Add a regression test (
RawtypeCrash.java) that triggers the rawtype/wildcard scenario from the crash report. - Update
PropagationTypeAnnotator#getTypeParameterElementto returnnull(instead of throwing) when the enclosing declared type is raw. - Record the fix in the changelog as closing
eisop#792.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| framework/tests/viewpointtest/RawtypeCrash.java | Adds a minimal test case that previously crashed the framework during type annotation propagation. |
| framework/src/main/java/org/checkerframework/framework/type/typeannotator/PropagationTypeAnnotator.java | Avoids throwing BugInCF when attempting to map a wildcard to a type parameter in the presence of raw types. |
| docs/CHANGELOG.md | Notes the issue as closed in the upcoming release notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
wmdietl
left a comment
There was a problem hiding this comment.
Thanks for looking into fixing this!
Isn't the code at https://github.com/aosen-xiong/checker-framework/blob/08171af48258a6d3a543f0ba85932dabd712dbb6/framework/src/main/java/org/checkerframework/framework/type/typeannotator/PropagationTypeAnnotator.java#L91 supposed to fix up raw types?
Is this crash an indication that the fix-up over there isn't doing the right things?
Maybe that code should do something different for wildcards?
Debugging the added test shows two visitWildcard iterations linked by scan(wildcard.getExtendsBound(), null). Iteration 1 (non-raw wildcard)wildcard: ? extends V (typeArgOfRawType=false) Iteration 2 (recursive, raw/synthesized wildcard)reached from iteration 1 via scan(extendsBound) This is a different wildcard instance/symbol from iteration 1 (different wrapper and underlying wildcard). |
|
Btw, the fix up for raw types are completely skipped in this case because of this check. |
| } | ||
| visitedNodes.put(wildcard, null); | ||
|
|
||
| // Raw wildcard args are already fixed up in visitDeclared. |
There was a problem hiding this comment.
"Raw wildcard" doesn't make sense, does it? You mean "If the wildcard is a type argument to a raw type" or something like that?
| visitedNodes.put(wildcard, null); | ||
|
|
||
| // Raw wildcard args are already fixed up in visitDeclared. | ||
| // Recursive traversal through scan(wildcard.getExtendsBound(), ...) can |
There was a problem hiding this comment.
I don't understand what this comment is trying to say. The body of the then-block calls scan on both bounds of the wildcard. Is this intended? What is the problem with re-entering?
Is there any point in scanning these two bounds here? Will they ever propagate anything useful?
| * @param typeArg a typeArg of {@code declaredType} | ||
| * @param declaredType the type in which {@code typeArg} is a type argument | ||
| * @return the type parameter in {@code declaredType} that corresponds to {@code typeArg} | ||
| * @return the type parameter in {@code declaredType} that corresponds to {@code typeArg}, or |
There was a problem hiding this comment.
This change can be undone again?
Fixes #792
Summary
Fix a crash in PropagationTypeAnnotator.getTypeParameterElement() when processing raw types. When a generic type is used as a raw type (e.g., (RawtypeCrash) null), the
compiler synthesizes wildcard type arguments that don't correspond to actual type arguments on the declared type. This caused getTypeParameterElement() to fail with a
BugInCF exception: "Wildcard ... is not a type argument of ...".
Fix
Add an early return of null in getTypeParameterElement() when the declared type is a raw type (declaredType.isUnderlyingTypeRaw()). The caller in visitWildcard() already
handles a null type parameter element gracefully by skipping annotation propagation, so no additional changes are needed there.