Skip to content

Bug fix with type substitutions after inference#1277

Merged
msridhar merged 7 commits intomasterfrom
issue-1267
Sep 6, 2025
Merged

Bug fix with type substitutions after inference#1277
msridhar merged 7 commits intomasterfrom
issue-1267

Conversation

@msridhar
Copy link
Copy Markdown
Collaborator

@msridhar msridhar commented Sep 4, 2025

Fixes #1267

Due to possible type mutations, e.g., when substituting for bounds, sometimes we see TypeVars in types that are not the same object as in the original method type for which we performed inference. Previously, we wouldn't substitute properly in such cases. Here, we collect all TypeVars with the same Symbol observed during inference and substitute for all of them, fixing the problem. This makes the Caffeine integration test green again.

Summary by CodeRabbit

  • Bug Fixes
    • Improved nullability handling for generics: all matching occurrences of a type variable are now annotated with a synthetic nullable marker, ensuring consistent analysis and avoiding missed substitutions across repeated type-variable occurrences.
  • Tests
    • Updated tests to reflect improved generic inference behavior; removed prior suppression/workarounds.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 4, 2025

Walkthrough

Collect all occurrences of a type variable (by symbol) during nullability substitution, substitute each occurrence with a synthetic @nullable type cached on the GenericsChecks instance, add a TypeVarWithSymbolCollector visitor to find matches, and remove a test suppression/comment in a JSpecify test.

Changes

Cohort / File(s) Summary
Generics substitution logic
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
substituteInferredNullabilityForTypeVariables now collects all matching TypeVar occurrences (via TypeVarWithSymbolCollector), builds parallel substitution lists, returns original type if none found, and uses a per-instance cached synthetic @Nullable Type. getSyntheticNullAnnotType changed from static → private instance method; caching field added.
TypeVar collector utility
nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java
New visitor class that traverses Type structures to collect TypeVar instances whose tsym equals a given Element, preserving insertion order and avoiding revisits; exposes matches via getMatches().
JSpecify test adjustment
nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java
Removed a TODO/link and @SuppressWarnings("NullAway") for a Caffeine inference edge case; replaced with a short comment.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller as Caller
  participant GC as GenericsChecks
  participant TVC as TypeVarWithSymbolCollector

  Caller->>GC: substituteInferredNullabilityForTypeVariables(targetType, symbol, inferredMap)
  GC->>TVC: new TypeVarWithSymbolCollector(symbol)
  GC->>TVC: TVC.scan(targetType)
  TVC-->>GC: matches (Set<TypeVar>)
  alt no matches
    GC-->>Caller: return targetType
  else matches found
    GC->>GC: build typeVarsToReplace & inferredTypes (use getSyntheticNullAnnotType(state))
    GC->>GC: apply TypeSubstitutionUtils.subst to produce substitutedType
    GC-->>Caller: return substitutedType
  end

  note over GC: synthetic @Nullable Type is lazily cached on GC instance
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
JSpecify inference: fix bug in type substitutions exposed by Caffeine (#1267)

Possibly related PRs

Suggested reviewers

  • yuxincs
  • lazaroclapp
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-1267

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java (2)

47-53: Reference equality is intended; suppress the warning locally.

t.tsym == symbol is correct in javac land; add suppression to avoid static-analysis churn.

@@
-  @Override
-  public Void visitTypeVar(TypeVar t, Void p) {
+  @Override
+  @SuppressWarnings("ReferenceEquality")
+  public Void visitTypeVar(TypeVar t, Void p) {

58-65: Optional: consider explicit handling of IntersectionClassType.

Bounds like Object & Comparable are IntersectionClassType; current visitClassType only scans type args/enclosing type. Not strictly required for substitution, but handling components would make the collector more robust.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1f49bef and e30ddbd.

📒 Files selected for processing (3)
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1 hunks)
  • nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java (1 hunks)
  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java
🧬 Code graph analysis (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)
nullaway/src/main/java/com/uber/nullaway/generics/TypeSubstitutionUtils.java (1)
  • TypeSubstitutionUtils (22-287)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build and test on windows-latest
🔇 Additional comments (2)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)

660-671: Good: substitute by symbol across all matching TypeVars.

Collecting all TypeVar occurrences by tsym and substituting them fixes the identity-mismatch bug described in #1267.

nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java (1)

1113-1113: LGTM: assertion comment updated to reflect inference now working.

The Caffeine-inspired case documents the behavior without suppressions.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)

660-671: Solid fix: symbol-based TypeVar substitution; consider tiny perf tweak.

Current approach correctly substitutes all occurrences matching the same symbol, fixing the identity mismatch bug. To avoid repeated creation of the synthetic annotation type, cache it once per method call.

   for (Map.Entry<Element, ConstraintSolver.InferredNullability> entry :
       typeVarNullability.entrySet()) {
     if (entry.getValue() == NULLABLE) {
+      // Create once per method invocation
+      Type syntheticNullAnnot = GenericsChecks.getSyntheticNullAnnotType(state);
       // find all TypeVars occurring in targetType with the same symbol and substitute for those.
       // we can have multiple such TypeVars due to previous substitutions that modified the type
       // in some way, e.g., by changing its bounds
       Element symbol = entry.getKey();
       TypeVarWithSymbolCollector tvc = new TypeVarWithSymbolCollector(symbol);
       targetType.accept(tvc, null);
       for (Type.TypeVar tv : tvc.getMatches()) {
         typeVars.append(tv);
         inferredTypes.append(
-            TypeSubstitutionUtils.typeWithAnnot(
-                tv, GenericsChecks.getSyntheticNullAnnotType(state)));
+            TypeSubstitutionUtils.typeWithAnnot(tv, syntheticNullAnnot));
       }
     }
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e30ddbd and 84f9fe9.

📒 Files selected for processing (1)
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
🧬 Code graph analysis (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)
nullaway/src/main/java/com/uber/nullaway/generics/TypeSubstitutionUtils.java (1)
  • TypeSubstitutionUtils (22-287)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build and test on macos-latest
  • GitHub Check: Build and test on windows-latest
  • GitHub Check: Build and test on ubuntu-latest
  • GitHub Check: Build caffeine with snapshot
🔇 Additional comments (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)

674-680: Return original type when no substitutions — good fix.

This resolves the earlier compile-time missing-return issue and preserves identity when no changes are needed.

@codecov
Copy link
Copy Markdown

codecov bot commented Sep 4, 2025

Codecov Report

❌ Patch coverage is 77.27273% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.44%. Comparing base (66e9f57) to head (52fc27a).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
.../nullaway/generics/TypeVarWithSymbolCollector.java 66.66% 13 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1277      +/-   ##
============================================
- Coverage     88.55%   88.44%   -0.12%     
- Complexity     2431     2448      +17     
============================================
  Files            91       92       +1     
  Lines          8022     8075      +53     
  Branches       1598     1603       +5     
============================================
+ Hits           7104     7142      +38     
- Misses          459      472      +13     
- Partials        459      461       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@msridhar
Copy link
Copy Markdown
Collaborator Author

msridhar commented Sep 4, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 4, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)

673-679: Good: safe fallback when no substitutions are needed.

Returning targetType if nothing to replace addresses the prior compile error.

🧹 Nitpick comments (6)
nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java (3)

36-41: Add a public entrypoint that resets the seen set and uses the guard from the root.

Currently callers do root.accept(this, null), which bypasses the guard for the root node and keeps seen across multiple traversals. Provide an entrypoint that clears seen and starts traversal via scan.

Apply:

@@
-  /** Walk a (possibly null) type. Safe to call multiple times on different roots. */
+  /** Walk a (possibly null) type. Internal recursive step. */
   private void scan(@Nullable Type t) {
     if (t != null && seen.add(t)) {
       t.accept(this, null);
     }
   }
+
+  /** Public entrypoint: traverse from root, resetting cycle guard each time. */
+  public void collectFrom(@Nullable Type root) {
+    seen.clear();
+    scan(root);
+  }

12-16: Minor import cleanup.

Prefer importing IdentityHashMap and using the already-imported Collections for readability.

Apply:

@@
-import java.util.Collections;
+import java.util.Collections;
+import java.util.IdentityHashMap;
@@
-  private final Set<Type> seen =
-      java.util.Collections.newSetFromMap(new java.util.IdentityHashMap<>());
+  private final Set<Type> seen = Collections.newSetFromMap(new IdentityHashMap<>());

114-121: Consider explicit handling for union/intersection types.

If UnionType or intersection types appear in bounds, relying on getTypeArguments() may miss components depending on javac version. Add explicit overrides to visit union/intersection components if needed.

If helpful, I can draft the exact overrides based on the javac version you target.

nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (3)

664-666: Use the collector’s entrypoint to leverage the cycle guard and reset semantics.

After adding collectFrom(...), prefer it over accept(...).

Apply:

-        targetType.accept(tvc, null);
+        tvc.collectFrom(targetType);

651-679: Avoid N traversals over the same target type (micro-optimization).

You currently traverse targetType once per NULLABLE entry. For large signatures, consider a single pass that collects matches for all NULLABLE symbols (e.g., a collector that takes a Set<Element>), then build the parallel lists. Low priority.

I can provide a single-pass collector if you’d like.


1451-1476: Narrow getSyntheticNullAnnotType visibility to private

The method is only invoked internally (at line 669) and there are no external or static call sites—making it private is safe. Apply:

-  public Type getSyntheticNullAnnotType(VisitorState state) {
+  private Type getSyntheticNullAnnotType(VisitorState state) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 84f9fe9 and 75c613f.

📒 Files selected for processing (2)
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (3 hunks)
  • nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
🔇 Additional comments (3)
nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java (2)

33-41: Cycle guard looks good; prevents recursion on self-referential bounds.

Using an IdentityHashSet via seen.add(t) avoids infinite recursion and duplicate visits. LGTM.


48-57: Matching by symbol is correct.

Comparing t.tsym by identity against the provided Element aligns with javac symbol semantics and fixes the identity-mismatch issue. LGTM.

nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)

660-671: Robust substitution by symbol; resolves post-inference TypeVar identity drift.

Collecting all TypeVars by symbol and substituting each occurrence fixes the mismatch. LGTM.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)

673-679: Return original type when no replacements — fix looks good

The early return prevents the prior compile-time “missing return” issue.

🧹 Nitpick comments (2)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (2)

657-666: Minor perf: traverse targetType once for all NULLABLE symbols

You currently walk targetType once per NULLABLE entry. Consider a single-pass collector that accepts a Set and returns a Set for all matches to reduce repeated traversals. Low-impact, but cleaner under many type vars.


1451-1452: Make cached field volatile to avoid rare visibility races

Not strictly needed here, but volatile is a cheap guard if analyses ever parallelize.

-  private @Nullable Type syntheticNullAnnotType;
+  private volatile @Nullable Type syntheticNullAnnotType;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 75c613f and 52fc27a.

📒 Files selected for processing (2)
  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (3 hunks)
  • nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • nullaway/src/main/java/com/uber/nullaway/generics/TypeVarWithSymbolCollector.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.
📚 Learning: 2025-08-28T04:54:20.953Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.953Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java
🧬 Code graph analysis (1)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (1)
nullaway/src/main/java/com/uber/nullaway/generics/TypeSubstitutionUtils.java (1)
  • TypeSubstitutionUtils (22-287)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build spring-framework with snapshot
  • GitHub Check: Build and test on macos-latest
  • GitHub Check: Build and test on windows-latest
  • GitHub Check: Build caffeine with snapshot
  • GitHub Check: Build and test on ubuntu-latest
🔇 Additional comments (3)
nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java (3)

660-671: Symbol-based multi-occurrence substitution fixes inference mismatch — LGTM

Collecting all TypeVars by symbol and substituting each occurrence addresses the object-identity pitfall and aligns with the PR objective.


1466-1476: Instance-cached synthetic @nullable type — LGTM

Lazily creating and caching an ErrorType in a synthetic package is reasonable and avoids repeated allocations.


1466-1476: No stale static calls found. Confirmed getSyntheticNullAnnotType has no remaining static invocations or external callers after privatization.

Copy link
Copy Markdown
Collaborator

@lazaroclapp lazaroclapp left a comment

Choose a reason for hiding this comment

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

LGTM!

Name simpleName = names.fromString("Nullable");
return new Type.ErrorType(simpleName, packageSymbol, Type.noType);
private Type getSyntheticNullAnnotType(VisitorState state) {
if (syntheticNullAnnotType == null) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Are we comparing this for identity in any way in which we would need to care about synchronization here? Or is either the case that (a) we don't care if we end up with multiple copies of the synthetic type / this is only a performance optimization, and/or (b) this class is never shared across threads (I actually don't fully recall how much parallelism there is in NullAway / EP overall beyond different javac instances in a parallel build having a copy of the plugin :) ).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is a combo of everything 🙂 Types do get compared by object equality in certain places (as I learned when fixing this issue), so seems good to only have one of these synthetic type objects floating around. Should be a small improvement in performance. And, this class should not be shared across threads; in a parallel build each thread should have its own NullAway instance, which creates its own GenericsChecks instance. javac itself doesn't support multithreading in a single instance AFAIK

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

That matches what I thought re: NullAway / javac plugins, but it's good to have confirmation. Cool! :)

@msridhar msridhar merged commit 9272d8b into master Sep 6, 2025
8 of 11 checks passed
@msridhar msridhar deleted the issue-1267 branch September 6, 2025 18:39
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.

JSpecify inference: fix bug in type substitutions exposed by Caffeine

2 participants