Skip to content

Handle Rawtype more gracefully#1438

Open
aosen-xiong wants to merge 6 commits intoeisop:masterfrom
aosen-xiong:eisop-792
Open

Handle Rawtype more gracefully#1438
aosen-xiong wants to merge 6 commits intoeisop:masterfrom
aosen-xiong:eisop-792

Conversation

@aosen-xiong
Copy link
Copy Markdown
Collaborator

@aosen-xiong aosen-xiong commented Nov 9, 2025

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.

@aosen-xiong aosen-xiong marked this pull request as ready for review November 9, 2025 06:47
aosen-xiong added a commit to aosen-xiong/checker-framework that referenced this pull request Mar 4, 2026
Copilot AI review requested due to automatic review settings March 4, 2026 03:47
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

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#getTypeParameterElement to return null (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.

@aosen-xiong aosen-xiong requested a review from wmdietl March 4, 2026 06:53
Copy link
Copy Markdown
Member

@wmdietl wmdietl left a comment

Choose a reason for hiding this comment

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

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?

@wmdietl wmdietl assigned aosen-xiong and unassigned wmdietl Mar 23, 2026
@aosen-xiong
Copy link
Copy Markdown
Collaborator Author

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)
upper/extends bound: another wildcard object (? extends @Top Object)
lower/super bound: @Bottom NullType (implicit lower bound for ? extends ...)
This is the wildcard stored in the parent type-arg list.

Iteration 2 (recursive, raw/synthesized wildcard)

reached from iteration 1 via scan(extendsBound)
wildcard: ? extends Object (typeArgOfRawType=true)
upper/extends bound: @Top Object
lower/super bound: @Bottom NullType

This is a different wildcard instance/symbol from iteration 1 (different wrapper and underlying wildcard).
Because iteration 2 uses a synthesized raw wildcard, but the parents field only contain the type for the first iteration.

@aosen-xiong
Copy link
Copy Markdown
Collaborator Author

aosen-xiong commented Mar 23, 2026

Btw, the fix up for raw types are completely skipped in this case because of this check.

if (!AnnotatedTypes.isTypeArgOfRawType(typeArgs.get(i))) {
    // Sometimes the framework infers a more precise type argument, so just use it.
    continue;
}

@aosen-xiong aosen-xiong requested a review from wmdietl March 23, 2026 20:19
@aosen-xiong aosen-xiong assigned wmdietl and unassigned aosen-xiong Mar 23, 2026
}
visitedNodes.put(wildcard, null);

// Raw wildcard args are already fixed up in visitDeclared.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This change can be undone again?

@wmdietl wmdietl assigned aosen-xiong and unassigned wmdietl Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Not a type argument crashes

3 participants