-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Some structs are required to chain the paramerless constructor #8410
Comments
Tagging @jskeet because I remember discussing a similar issue with him, but I can't find any notes or the resolution of this issue. (It might have been in the context of the ECMA spec reviews. |
What's in the rest of the partial |
Sure. Full source code for the class in question is at: |
I don't think it should ever have compiled. Here's a similar example: struct Foo
{
int a;
public Foo(int x)
{
this.a = Method();
}
int Method()
{
return 5;
}
} That gives me the same error ("The 'this' object cannot be used before all of its fields are assigned to"), without using DNX. I wonder whether this is actually to do with Code Contracts... I'll see if I can build your csproj-based code myself... |
@jskeet This code has been in GitHub for almost 2 years like that and it's always compiled just fine. |
Hmm... yes, it builds for me, even after removing the code contracts. Will try to reduce it to a simpler example - hopefully in doing so I'll find out what's going on. |
Okay, narrowing it down somewhat, small repro:
using T = System.Guid;
public partial struct Demo
{
private T field;
public Demo(int ignored)
{
this.field = this.Method();
}
private T Method()
{
return default(T);
}
} That builds, but shouldn't.
I dread to think what the underlying cause of this is, but I think it's a reasonable bug to be fixed in the compiler - making your code invalid. Just add the |
@jskeet Thanks for figuring out a repro for this issue. The extra information about some types working and others not for In general, I agree that the code I had probably should never have compiled and that I should have always had the chained constructor call to |
I took a look at this and one curiosity jumps out:
That definition has all members stripped out. Hence this is essentially testing whether or not I believe this behavior is hence by design. But definitive assignment in struct ctors is an area I've been known to get tripped up on. Interested to hear what others think. CC @gafter |
Right. In that case, it feels like if the compiler is meant to understand reference assemblies, then either:
How unreasonable would be to simply assume that any struct outside the assembly you're currently building always contains fields? (For the purpose of definite assignment, at the very least.) Perhaps special-case known structs which don't have any fields? (I'm thinking of |
@jskeet the compiler doesn't understand reference assemblies though, it just understands assemblies. There is no differentiation to us. The negotiation of reference vs. implementation assemblies, profiles, etc ... is all handled by MSBuild. It makes a determination of what should be referenced and sends it to the compiler (who looks no further).
I think this would have been a very reasonable behavior if it had been adopted in C# from the beginning. My fear in adopting it now though is that it would be a breaking change. Given the tendency for reference assembly tools to strip private fields and the subtle nature of struct ctor definitive assignment, it's likely one that would have a noticeable impact. |
Another possible fix is for PCLs to stop stripping out private fields on structs. Rightly or wrongly it's an aspect of structs that C# has taken a dependency on. By stripping away private fields the reference generator tools have altered the semantic meaning of the type (as consumed by C#). From the perspective of C# the original assembly and reference assemblies, when this occur, are semantically different. |
Having owned/caused this issue, I'll add some background: About ~5 years ago, when we made the decision to strip the fields[1], portable libraries could not use unsafe code or security critical members; it was explicitly disabled due to Silverlight's sandbox. I believe we discuss this particular issue at a design meeting (though can't find the notes), decided that due to lack of unsafe code, private fields weren't needed. We were unaware that C# used them in their definite assignment analysis. My initial thought was that even with unsafe code, as long as we marked the struct with the correct size (via the pseudo StructLayoutAttribute), then we could still skip the fields - but @jaredpar pointed out that C# still uses the fields to determine if a given struct is "blittable" and hence, can be a valid pointer[2]. Given this definite assignment issue and interop reasons, I agree that the fields need to be added to the reference assemblies.// cc @weshaggard @ericstj. [1] We didn't strip per se, just didn't generate them - the reference assemblies were built from generated C# code. |
Another example of semantical significance of all struct fields: struct S2<T>
{
// if ref assembly does not disclose this field,
// S1 will compile
private T field;
}
struct S1
{
private S2<S1> field;
} |
@VSadov Nice one! |
IMO - since structs are basically chunks of data without any point of indirection, the shape/layout of that data constitutes a kind of a contract. |
@VSadov And yet we allow accessibility on struct members... |
Going to move this bug over to corefx. The root problem here is in the definition of the reference assemblies. Roslyn is behaving to spec based on the data it is being given. Need to correct the reference assemblies here so this, and other bugs, can be fixed. For the curious here is a full write up of the various ways private struct fields produce observable behavior. |
Thanks @jaredpar. I'll keep an eye on the new issue. |
@jskeet @scottdorman @BillWagner @scottdorman @jaredpar I will appreciate if you big guns of C# have a swap at this stackoverflow code and give us your view. @ericlippert has given a view but this is one subtle behaviour either in C# or CLR . It is also available here on Roslyn Issues I know @jskeet will love this one @peter-perot. This is somewhat related to this thread. |
I have just learned that Jared already commented on the issue that coderealm refers to: http://blog.paranoidcoding.com/2016/02/15/are-private-members-api-surface.html Jared seems to have this one well in hand; no need for further input from me on this one. |
In the process of upgrading some projects to support .NET Core (by simply moving to the new project.json format) and, once done, the following code failed with compiler error CS0188: The ‘this’ object cannot be used before all of its fields are assigned to.
Prior to the change to .NET Core, this code compiled just fine in a PCL targeting .NET 4.0.
The code in question (edited for brevity) is:
The line (this.dateTime = this.GetDateTimeOffset()) compiles just fine in every version of the Framework except .NET Core.
This is actually easy for me to fix by making sure the constructor chains a call to the parameterless constructor:
I’d argue that the new code is actually more correct and that it’s how the code should have been written in the first place. The issue is that by simply upgrading the project and doing nothing else, I suddenly get a compiler error when there weren’t any before. In fact, there weren’t even any warnings about this before.
Apparently this was similar to an issue with auto properties in structs as well, all the way back to 2009, with the same fix/workaround. According to some of the answers to questions about this on StackOverflow, it was fixed in C#6.
Either way, it’s a regression because what used to compile doesn’t anymore just by upgrading the project format. I'm not certain if it's related to .NET Core or to Roslyn.
The text was updated successfully, but these errors were encountered: