I managed to observe a change in program behavior based on the order of files passed to the compiler. Never mind how; that's what it is. The file name order matters.
By default, projects do wildcard expansion of files (which is unstable), but there exists the option <Deterministic>true</Deterministic>
; what order are files passed to the compiler when <Deterministic>true</Deterministic>
is set?
Searching for documentation turned up nothing.
If you tell me no such order exists I will most definitely file a bug against Roslyn; if I can observe a functional change by re-ordering files than it's definitely not deterministic.
Simplest possible reproduction:
/* a file */
partial class Class1 {
public static int x = 3;
}
/* another file */
partial class Class1 {
public static int y = x;
}
/* Program.cs */
Console.WriteLine(Class1.y);
Reproduction is easiest on a FAT filesystem. The file names are unimportant; the order you create them is important.
I managed to observe a change in program behavior based on the order of files passed to the compiler. Never mind how; that's what it is. The file name order matters.
By default, projects do wildcard expansion of files (which is unstable), but there exists the option <Deterministic>true</Deterministic>
; what order are files passed to the compiler when <Deterministic>true</Deterministic>
is set?
Searching for documentation turned up nothing.
If you tell me no such order exists I will most definitely file a bug against Roslyn; if I can observe a functional change by re-ordering files than it's definitely not deterministic.
Simplest possible reproduction:
/* a file */
partial class Class1 {
public static int x = 3;
}
/* another file */
partial class Class1 {
public static int y = x;
}
/* Program.cs */
Console.WriteLine(Class1.y);
Reproduction is easiest on a FAT filesystem. The file names are unimportant; the order you create them is important.
Share Improve this question edited Feb 5 at 18:54 Joshua asked Feb 5 at 18:40 JoshuaJoshua 43.3k9 gold badges78 silver badges149 bronze badges 8- 1 Your observation is correct: when static members depend on each other during initialization, the compiler initializes them in textual order. It does not do a partial order sort. See Order of static constructors/initializers in C#. But the textual ordering of members across partial classes is explicitly undefined, see Is the "textual order" across partial classes formally defined?. So you can get inconsistent static initializations for interdependent statics in different files. – dbc Commented Feb 5 at 19:23
- In fact I think your question is a duplicate of those two questions, agree? – dbc Commented Feb 5 at 19:24
- @dbc: I actually think it's not a duplicate. Even undefined behavior must be stable (even if it's stable in a way that's useless to depend on like a hash ordering of the input files) or deterministic is violated. – Joshua Commented Feb 5 at 19:43
- The current specification states that the order is undefined. Now in the C++ community "undefined" means behavior for which this document imposes no requirements -- so it could be completely random -- while "unspecified behavior" means behavior, for a well-formed program construct and correct data, that depends on the implementation, see this answer. So if "undefined" in the C# means the same thing, it might not even be stable. (And in fact I think it's really just the order in which the compiler encounters the files, so OS+filesystem dependent.) – dbc Commented Feb 5 at 19:48
- 1 I updated the links in this answer to Order of static constructors/initializers in C# by the way, the original MSDN links were way out of date for .NET Core. – dbc Commented Feb 5 at 19:50
1 Answer
Reset to default 3The compiler's behavior indeed depends on the ordering of files. There's some other cases too around partial classes/members, which can have XML doc comments on both which get merged, and the order of the merging depends on the file ordering.
The compiler, as in "the csc.exe process", will ensure that the order it gets the source files passed in via the command line are handed around and processed in the correct order internally. It is absolutely deterministic, given a specific ordering of files -- it's just that the file ordering is effectively an input to the determinism. If you had a case where the same compiler invocation with the same ordering of files gave a different non-deterministic output, we'd absolutely want that bug.
However, the end-to-end tooling makes this murky: as you observed the globbing MSBuild does might depend on your file system, or operating system, etc. And frankly, if your file ordering matters, then you might just want to not use globbing because that's just asking for trouble at some point!
We absolutely have bugs in the IDE where when we create the "internal" compiler for IntelliSense, we might not get the file ordering correct to match the ordering the actual build will use. We haven't prioritized these bugs since they're very hard to hit. Frankly, in the 15 years I've worked on this you might be the winner for the first customer to notice!