Skip to content

Comments

Improve the documentation of ElementFactory.#2027

Merged
copybara-service[bot] merged 1 commit intomainfrom
test_872557496
Feb 20, 2026
Merged

Improve the documentation of ElementFactory.#2027
copybara-service[bot] merged 1 commit intomainfrom
test_872557496

Conversation

@copybara-service
Copy link
Contributor

Improve the documentation of ElementFactory.

Currently, the documentation references some Javadoc about how a given lowercase-e element may be represented by different Element instances over time. However, I think that's a red herring for the problem that we're solving:

  • The doc doesn't necessarily imply that older instance become "invalid," only that they should be compared with equals instead of ==. (And I suspect that we've done as much all along. As a bonus, using equals probably isn't even technically necessary today, at least if we depend on implementation details of javac—though of course we shouldn't.)
  • The doc doesn't necessarily imply that there's anything special about the boundary between rounds. Thus, as far as the Javadoc for Element is concerned, the compiler could switch which instance it returns during a round just as easily as it could use different instances across rounds. And thus, the doc isn't explicit on whether we should treat a given Element as having a lifetime of exactly the round in which we looked it up.

But this code does appear to be a response to a real behavior in javac—and perhaps especially in Eclipse. (I haven't looked into Turbine.) There does in fact appear to be an undocumented(?) restriction on the the lifetime of an Element instance:

(The behavior of Element instances across rounds might have changed for Java 7, but the behavior seen in Java 7 appears to have persisted despite some changes to reuse state across rounds in Java 9.)

Notably, while Element documents that callers should use Element.equals instead of identity comparison, it appears that even Element.equals does not consider two distinct Element instances for "the same declaration" to be equal. (As of this writing, javac does not appear to override equals in its Element implementations at all.)

Concretely, we've seen that an ExecutableElement from one round may have an enclosing element whose enclosed-elements list does not contain anything equal to that ExecutableElement. That specific quirk may or may not be a problem, but it's further evidence that such usage is unsupported.

(For what it's worth, I edited our ElementFactory implementations to always return the original Element, and I got no failures in a TAP Global Presubmit run except for those in BasicAnnotationProcessorTest, which depends on the quirk discussed just above. That quirk comes up only because ElementFactory looks up the enclosing element as part of its equality logic, so I'd expect all tests to pass if we were to replace ElementFactory with Element. It would be nice to have test coverage that demonstrated a clearer need for ElementFactory. We could add some by directly performing an equality comparison on an ExecutableElement from a different round, but it would be nice to have a better sense of how that might matter in practice and how widespread the problem is—e.g., whether it also affects TypeMirror instances. But given that we know Eclipse behaves differently from javac, we might well see the problems when testing under ejc, which I think we might have some setup for?)

(One use case that probably works for modern versions of javac (but quite likely still not Eclipse) is that of storing only ClassSymbol/TypeElement instances, which are currently reused across rounds.)

(followup to e20db25 / #1348)

RELNOTES=n/a

Currently, the documentation references some Javadoc about how a given lowercase-e element may be represented by different `Element` instances over time. However, I think that's a red herring for the problem that we're solving:

- The doc doesn't necessarily imply that older instance become "invalid," only that they should be compared with `equals` instead of `==`. (And I suspect that we've done as much all along. As a bonus, using `equals` probably isn't even technically necessary today, at least if we depend on implementation details of javac—though of course we shouldn't.)
- The doc doesn't necessarily imply that there's anything special about the boundary between rounds. Thus, as far as the Javadoc for `Element` is concerned, the compiler could switch which instance it returns *during* a round just as easily as it could use different instances across rounds. And thus, the doc isn't explicit on whether we should treat a given `Element` as having a lifetime of exactly the round in which we looked it up.

But this code does appear to be a response to a real behavior in javac—and perhaps especially [in Eclipse](https://bugs.eclipse.org/bugs/show_bug.cgi?id=480936). (I haven't looked into Turbine.) There does in fact appear to be an undocumented(?) restriction on the the lifetime of an `Element` instance:

- [JDK-6191665](https://bugs.openjdk.org/browse/JDK-6191665)
- [JDK-7026845](https://bugs.openjdk.org/browse/JDK-7026845?focusedId=12082299&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-12082299)

(The behavior of `Element` instances across rounds [might have changed for Java 7](https://bugs.openjdk.org/browse/JDK-8144105), but the behavior seen in Java 7 appears to have persisted despite [some changes to reuse state across rounds in Java 9](https://bugs.openjdk.org/browse/JDK-8038455).)

Notably, while `Element` documents that callers should use `Element.equals` instead of identity comparison, it appears that even `Element.equals` does not consider two distinct `Element` instances for "the same declaration" to be equal. (As of this writing, javac does not appear to override `equals` in [its `Element` implementations](https://github.com/openjdk/jdk/blob/2a71f89bc8d72be8095113695e541f4f38acdeca/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java) at all.)

Concretely, we've seen that an `ExecutableElement` from one round may have an enclosing element whose enclosed-elements list does not contain anything equal to that `ExecutableElement`. That specific quirk may or may not be a problem, but it's further evidence that such usage is unsupported.

(For what it's worth, I edited our `ElementFactory` implementations to always return the original `Element`, and I got no failures in a TAP Global Presubmit run except for those in `BasicAnnotationProcessorTest`, which depends on the quirk discussed just above. That quirk comes up only because `ElementFactory` looks up the enclosing element as part of its equality logic, so I'd expect all tests to pass if we were to replace `ElementFactory` with `Element`. It would be nice to have test coverage that demonstrated a clearer need for `ElementFactory`. We could add some by directly performing an equality comparison on an `ExecutableElement` from a different round, but it would be nice to have a better sense of how that might matter in practice and how widespread the problem is—e.g., whether it also affects `TypeMirror` instances. But given that we know Eclipse behaves differently from javac, we might well see the problems when testing under ejc, which I think we might have some setup for?)

(One use case that probably works for modern versions of javac (but quite likely still not Eclipse) is that of storing only `ClassSymbol`/`TypeElement` instances, which [are currently reused across rounds](https://bugs.openjdk.org/browse/JDK-6191665?focusedId=13519728&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13519728).)

(followup to e20db25 / #1348)

RELNOTES=n/a
PiperOrigin-RevId: 872912877
@copybara-service copybara-service bot merged commit fecb834 into main Feb 20, 2026
@copybara-service copybara-service bot deleted the test_872557496 branch February 20, 2026 16:33
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.

1 participant