Florian, Thanks for drawing attention to this part of the spec:
On 11/2/2019 3:21 AM, Florian Weimer wrote:
Is it allowed to declare a canonical constructor explicitly and make it non-public? I think the naswer is no. But it's not quite obvious from the spec, I think.
JLS 8.10.4 defines a "canonical" ctor to be a public ctor whose formal parameter list is identical to the record header. If you explicitly declare a ctor whose formal parameter list is identical to the record header, but you do not make it public, then you have not declared a "canonical" ctor. You have declared a ctor that, by virtue of its signature, prevents an implicitly declared canonical ctor from being declared. This is so unfortunate that it deserves an error message, and you will get one.
However, you are right that the spec is slightly misworded. It says "It is a compile-time error if a record declaration contains a canonical constructor declaration that is not public." but a "canonical" ctor is public by definition. It should say "It is a compile-time error if a record declaration contains a constructor declaration whose formal parameter list is identical to the record header of R, but which is not public."
An alternative approach: JLS 8.10.4 could define a "canonical" ctor as follows: "Every record type R has a _canonical_ constructor, which is a constructor [note the silence on accessibility] whose formal parameter list is identical to the record header of R." ... and deem an implicitly declared canonical ctor to be public. Then, the compile-time error can be left alone, though I would reword it for clarity and for tonal agreement with '[exp|imp]licitly declared': "If a canonical constructor is explicitly declared, then it must be public, or a compile-time error occurs."
The alternative approach is best because the sense of "canonical" is dominated by the signature -- just look at javac's error message "canonical record constructor must be public" which assumes that canonical-ness is obvious (signature-driven) and that the access modifier is a separate thing, so to speak. That seems a pretty natural view of things for the JLS to embody.
(Sidebar: Why allow a compact ctor declaration to be non-public, when we control its grammar that could be hard-coded to use `public`?)
(Sidebar: The compact ctor declaration should be introduced at the same time as "A canonical constructor can be explicitly declared ...". I recall privately discussing the flow of this section but can't find it easily. I suggest that 8.10.4 should be "Canonical Constructor of a Record" and that the sole non-canonical clause "A record declaration may contain constructor declarations." should live in 8.10.2 "Record Body" (not plural; yes, the title of 8.9.2 should drop the D-word too, which will happen auto-magically) ... 8.10.2 already says "may contain constructor and member declarations" but that opening paragraph should advertise the compact ctor's role in helping to initialize the member [because otherwise CompactConstructorDeclaration goes unexplained] ... the line will be hard to write and this mail is already long enough.)
Alex