-
Notifications
You must be signed in to change notification settings - Fork 89
15.12 Static constructors (when extern) #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
On 1/04/2019, at 11:05 pm, Neal Gafter ***@***.***> wrote:
See also dotnet/roslyn#34651 <dotnet/roslyn#34651>
The specification requires an implementation to initialize static fields before calling the static constructor. However, existing implementations compile the field initializations into the body of the generated static constructor.
I’m wondering whether there is confusion here between the CLI’s .cctor and C#’s static constructor – I don’t think there is anything in the C# Standard which requires its static constructor to *be the* CLI’s .cctor. Of course when running on the CLI the .cctor would be the mechanism used to execute the C# static constructor. In other words a C# compiler could produce:
.cctor()
{
// code to initialise static fields
// code to call C# static constructor
}
and that matches the Standard:
execution of the static field initializers occurs immediately prior to executing that static constructor (§16.5.6.2[*])
and what you are describing with the added compiler optimisation of inlining the static constructor.
When the static constructor is itself declared extern, who is supposed to run the field initializers?
With the above view the answer is the .cctor before it calls the extern C# constructor – in this case it does not inline the C# constructor
Existing implementations simply produce no code for those initializations (and produce a warning on the static constructor declaration). Is this a bug in the compiler? Iif so, what could it possibly do instead? Or is this a bug in the (external) body of the static constructor, which should perform the initialization?
With the above view the C# static constructor never does the field initialisations, that is handled by the compiler generated .cctor.
So yes, it seems the current compiler’s have a bug
The current wording would seem to make the current behavior a compiler bug (that cannot be fixed).
which surely can be fixed?
To initialize a new closed class type, first a new set of static fields (§15.5.2) for that particular closed type is created. Each of the static fields is initialized to its default value (§15.5.5). Next, the static field initializers (§15.5.6.2) are executed for those static fields. Finally, the static constructor is executed.
I propose rewording to say that the static initializers are performed on entry to the static initializer (which is analogous to what we say for instance initializers vs instance constructors). Alternatively, or in addition, in the paragraph that describes the behavior for an external static constructor should say that it is responsible for executing the field initializers.
Is it really analogous? There are fundamental differences between class and instance constructors, the former do not invoke the super class' constructor (or else a class constructor could be called multiple times), the latter do invoke the super class’ constructor and it is this mechanism, which does not exist for class constructors, which is defined in C# as triggering the field initialisation (§16.11.3[*]).
TL;DR: Spec-wise it is a bug in the compiler, the spec is implementable on the CLI, and the analogy is a little lacking. So I’m not yet convinced a spec change is required. However I have not argued, at least at present, that the spec’s semantics are the right/best ones; just that there is no CLI/C# incompatibility here…
unless of course I’m missing something?
Cheers,
Nigel
[*] I just clicked on a link in Word with the text §15… and it jumped to §16… which was the right destination, so all references I enter may be wrong!
… —
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub <https://github.com/ECMA-TC49-TG2/spec/issues/957>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AGWK2_rIXiepVWbtF-EE_JXHiWK3HbaDks5vcoKtgaJpZM4cWu4P>.
|
@Nigel-Ecma Do you believe there are some C# implementations that obey the specification in this area, in a way that the Roslyn compiler does not? Unless there is, I would prefer that we enshrine existing industry practice rather than describing behavior that exists nowhere. It would be a breaking change to our customers to change what IL method they need to implement externally when a static constructor is declared extern. |
On 9/04/2019, at 2:33 am, Neal Gafter ***@***.***> wrote:
@Nigel-Ecma <https://github.com/Nigel-Ecma> Do you believe there are some C# implementations that obey the specification in this area, in a way that the Roslyn compiler does not? Unless there is, I would prefer that we enshrine existing industry practice rather than describing behavior that exists nowhere. It would be a breaking change to our customers to change what IL method they need to implement externally when a static constructor is declared extern.
Both ECMA-TC49-TG2/spec#956 & ECMA-TC49-TG2/spec#957 talk about enshrining industry practice and my response to ECMA-TC49-TG2/spec#956 applies to both, might have been better if I’d posted it attached to this one and this note be on the other, I’ll copy to save folk jumping around:
In general while its good that a Standard stems from actual practice surely a Standard offers something more than just “what the last compiler (bugs and all) does” e.g. stability, certainty, reliability etc. What is the point of a Standard if its just the latest compiler’s documentation? When deciding what goes into a Standard you need to distinguish between quirks of an implementation, which you might find you want to change in the next compiler, and intentional semantics you want to nail down. Of course this often isn’t a simple black/white determination, so you get some mugs to sit on a committee and figure out what should be standardised and what should be left as an artifact of implementation.
Moving to the specific we have a Standard which did grow out of the documentation of the industry practice, and now we have a current compiler which varies from the Standard. Was the documentation or Standard originally wrong? It it is in the Standard we hope it was examined and seen to be good, are did it slip through? Was it correct and the behaviour has now intentionally changed? Is it a case of a mistake in the compiler but we have to enshrine it now for backward compatibility? Etc. Inquiring minds should surely want to know. I’m not saying the change shouldn’t be made, I’m saying I wasn’t persuaded it should be, which is the same thing.
We should be mindful of the, probably apocryphal, story that the quirks in Pascal are due to Wirth documenting the behaviour of the compiler he had his students write…
I must say when Apple announced that every version of Swift would probably be breaking, and many certainly have, I wasn’t overly convinced with that approach for a language (then I saw the quirks in the first public version and was glad they were not set in!), but one can sometimes see the advantages of the approach as it avoids issues like this. However it has meant that Swift is more a family of languages, or just one which has grown up from infancy, through an “interesting" adolescence, and might only just be approaching a more stable adulthood ;-)
|
See also dotnet/roslyn#34651
The specification requires an implementation to initialize static fields before calling the static constructor. However, existing implementations compile the field initializations into the body of the generated static constructor.
When the static constructor is itself declared
extern
, who is supposed to run the field initializers? Existing implementations simply produce no code for those initializations (and produce a warning on the static constructor declaration). Is this a bug in the compiler? Iif so, what could it possibly do instead? Or is this a bug in the (external) body of the static constructor, which should perform the initialization?The current wording would seem to make the current behavior a compiler bug (that cannot be fixed).
I propose rewording to say that the static initializers are performed on entry to the static initializer (which is analogous to what we say for instance initializers vs instance constructors). Alternatively, or in addition, in the paragraph that describes the behavior for an external static constructor should say that it is responsible for executing the field initializers.
The text was updated successfully, but these errors were encountered: