Skip to content

GROOVY-12509: STC: allow members of a private nested type from another class of the same nest - #3044

Merged
paulk-asert merged 1 commit into
apache:masterfrom
codeconsole:stc-nestmate-private-access
Oct 29, 2026
Merged

paulk-asert merged 1 commit into
apache:masterfrom
codeconsole:stc-nestmate-private-access

Conversation

@codeconsole

@codeconsole codeconsole commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Since GROOVY-12310 (6.0.0), the type checker rejects a field whose qualifying type is out of reach. That check does not know about nests, so a public field or an enum constant of a private nested type fails with "Cannot access field" when it is used from another class of the same top-level class, even from the outer class itself. 5.0.8 compiles the same code, and javac accepts it (JLS 6.6.1).

@TypeChecked
class Outer {
    static class A {
        private static class Hidden { public static int x = 1 }
        private enum E { ONE }
    }
    static class B {
        static read() { [A.Hidden.x, A.E.ONE] }  // Cannot access field: x of class: Outer$A$Hidden from class: Outer$B
    }                                            // Cannot access field: ONE of class: Outer$A$E from class: Outer$B
}

Every type of a nest is now accessible from every other type of the same nest. A private nested type of another top-level class is still rejected, and so is the package-private nested type of another package that GROOVY-12310 is about. Methods and constructors of such a type were never affected.

This is the first part of GROOVY-12509. Private methods, constructors and accessors of another class of the same nest follow in separate pull requests.

Tests

FieldsAndPropertiesSTCTest, also run statically compiled by FieldsAndPropertiesStaticCompileTest: reads, a write, an attribute, a closure, a doubly nested private type and an enum constant, from a sibling and from the outer class, and a private nested type of another top-level class, which stays rejected. The first test fails without the change. 14,940 tests in the groovy, gls, bugs, org.codehaus.groovy.classgen, transform, runtime, reflection, vmplugin, control and ast packages and the stub generator tests pass, apart from bugs.Groovy12363, which fails on master too when it runs after bugs.Groovy12142 in the same JVM (#3025 fixes it). groovy-contracts, groovy-typecheckers, groovy-ginq, groovy-macro-library, groovy-json, groovy-xml and groovy-templates pass their tests, and every subproject compiles its main and test sources. The Checkstyle and CodeNarc gates pass.

Backport

This is a regression in 6.0.0, so the change is a candidate for GROOVY_6_0_X.

@codecov-commenter

codecov-commenter commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.6116%. Comparing base (8cc8646) to head (8fbea12).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##               master      #3044        +/-   ##
==================================================
+ Coverage     73.6107%   73.6116%   +0.0009%     
  Complexity      42321      42321                
==================================================
  Files            1657       1657                
  Lines          145631     145632         +1     
  Branches        27470      27470                
==================================================
+ Hits           107200     107202         +2     
  Misses          28466      28466                
+ Partials         9965       9964         -1     
Files with missing lines Coverage Δ
...roovy/transform/stc/StaticTypeCheckingVisitor.java 87.4971% <100.0000%> (+0.0261%) ⬆️

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codeconsole codeconsole changed the title GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519: private members and qualified this and super of nested classes under static type checking and compilation GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519, GROOVY-12520, GROOVY-12521: private members and qualified this and super of nested classes under static type checking and compilation, and super property access Oct 25, 2026
@codeconsole codeconsole changed the title GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519, GROOVY-12520, GROOVY-12521: private members and qualified this and super of nested classes under static type checking and compilation, and super property access GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519, GROOVY-12520, GROOVY-12521: private members and qualified this and super of nested classes under static type checking and compilation, and super property and method access Oct 26, 2026
@codeconsole codeconsole changed the title GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519, GROOVY-12520, GROOVY-12521: private members and qualified this and super of nested classes under static type checking and compilation, and super property and method access GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519, GROOVY-12520, GROOVY-12521, GROOVY-12522, GROOVY-12523: private members and qualified this and super of nested classes under static type checking and compilation, and super property and method access Oct 26, 2026
@codeconsole
codeconsole force-pushed the stc-nestmate-private-access branch from 5252718 to ca201ca Compare October 26, 2026 03:52
@codeconsole codeconsole changed the title GROOVY-12509, GROOVY-12510, GROOVY-12511, GROOVY-12512, GROOVY-12513, GROOVY-12514, GROOVY-12516, GROOVY-12517, GROOVY-12518, GROOVY-12519, GROOVY-12520, GROOVY-12521, GROOVY-12522, GROOVY-12523: private members and qualified this and super of nested classes under static type checking and compilation, and super property and method access GROOVY-12509: private members of nested classes under static type checking and compilation Oct 26, 2026
@codeconsole
codeconsole marked this pull request as ready for review October 26, 2026 03:52
@testlens-app

This comment has been minimized.

@codeconsole

Copy link
Copy Markdown
Contributor Author

The failed additional (23) job is unrelated to this change: LanguageQualityTest.dotCompletionListsReceiverMembers in groovy-lsp hit GROOVY-12532, a race in the shared ErrorCollector under parallel parsing, which happens on master too.

Could someone with the rights rerun the failed job?

@eric-milles

Copy link
Copy Markdown
Member

Is it possible to step this in with smaller increments? It is hard to consider all the implications of such changes when they come all at once. Nest mates and private access went through a lot of careful consideration leading up to Groovy 5 release.

@codeconsole

Copy link
Copy Markdown
Contributor Author

hi @eric-milles, yeah, I can break this up

…r class of the same nest

Since GROOVY-12310 (6.0.0), the type checker rejects a field whose
qualifying type is out of reach, through hasAccessToClass. That check
did not know about nests, so a public field or enum constant of a
private nested type, such as A.Hidden.x or A.E.ONE, failed with
"Cannot access field" when used from a sibling of A or even from the
outer class. Groovy 5.0.8 compiles and runs the same code, and javac
accepts it (JLS 6.6.1). Methods and constructors of the same private
type were never affected.

hasAccessToClass now treats every type of a nest as accessible from
every other type of the same nest. A private nested type of another
top-level class is still rejected, and so is the package-private
nested type of another package that GROOVY-12310 is about.
@codeconsole
codeconsole force-pushed the stc-nestmate-private-access branch from ca201ca to 8fbea12 Compare October 26, 2026 16:14
@codeconsole codeconsole changed the title GROOVY-12509: private members of nested classes under static type checking and compilation GROOVY-12509: STC: allow members of a private nested type from another class of the same nest Oct 26, 2026
@codeconsole

Copy link
Copy Markdown
Contributor Author

I've cut this down to the first step: the 6.0.0 regression for fields of a private nested type (GROOVY-12310). The rest follows one pull request at a time, each opened after the previous one is merged:

  1. GROOVY-12512: calling an outer instance method where there is no enclosing instance is a compile error. This only rejects code that already fails at run time in every mode.
  2. GROOVY-12509: private getters and setters of another class of the same nest, through property syntax.
  3. GROOVY-12510: a private method of a superclass is not found through a subclass. Code that runs under @CompileStatic today stops compiling, as javac rejects it.
  4. GROOVY-12509: private methods and constructors of another class of the same nest.
  5. GROOVY-12511: @CompileStatic uses the enclosing instance for a private member of an outer class that a nested class extends.
  6. GROOVY-12513: private fields and accessors through a variable that flow typing narrows to a subclass.

Each description lists the code that stops compiling and anything that behaves differently under @CompileStatic. #3049 and #3050 are drafts until the steps they need are merged.

@daniellansun daniellansun left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me.

hasAccessToClass was applying the member-access rule with the enclosing type as receiver, so a private nested type was treated as unreachable from another type of the same nest even when the field itself was public (or an enum constant). The early inSameNest check matches JLS 6.6.1 / nestmates, and the existing isFieldAccessible nestmate path still covers the private-field case in the test.

Thanks for keeping this to the GROOVY-12310 regression and for the negative test on another top-level nest. The matrix (sibling, outer, write, attribute, closure, double nesting, enum) is exactly what I want to see here.

One process note: #3052 rewrites the same method more broadly (protected + precompiled outer). Whichever lands second will need a careful rebase; the tests in this PR should stay.

+1 for master, and I agree it is a candidate for GROOVY_6_0_X.

@paulk-asert
paulk-asert merged commit 5b077f9 into apache:master Oct 29, 2026
33 checks passed
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.

5 participants