Allow public parameterless struct constructors declared in source#51679
Allow public parameterless struct constructors declared in source#51679cston merged 25 commits intodotnet:features/struct-ctorsfrom
Conversation
I thought we already handled parameter-less struct constructors defined in metadata and made use of them. Could you please elaborate what behavior difference, if any, with regard to these constructors in this PR? |
You're right, the compiler was using public parameterless constructors from metadata. But private parameterless constructors were ignored, so for a struct with a private parameterless constructor, the compiler would emit There's a question of whether the compiler should recognize cases where a In reply to: 791037969 [](ancestors = 791037969) |
| private static bool HasPublicParameterlessConstructor(NamedTypeSymbol type) | ||
| private static bool? HasPublicParameterlessConstructor(NamedTypeSymbol type) | ||
| { | ||
| Debug.Assert(type.TypeKind == TypeKind.Class); |
There was a problem hiding this comment.
Instead of deleting the assertion, should it be updated to type.TypeKind is TypeKind.Class or TypeKind.Struct? #Resolved
2b1524b to
f8e3948
Compare
…rameterless .ctors from metadata
|
|
||
| Cci.IMethodReference Cci.ICustomAttribute.Constructor(EmitContext context, bool reportDiagnostics) | ||
| { | ||
| if (this.AttributeConstructor.IsDefaultValueTypeConstructor()) |
There was a problem hiding this comment.
Should we allow such attributes when the default struct constructor exists for real (ie. is emitted)?
|
@dotnet/roslyn-compiler, please review. |
| /// <summary> | ||
| /// Returns true if the type is defined in source and contains field initializers. | ||
| /// </summary> | ||
| internal virtual bool HasFieldInitializers() => false; |
There was a problem hiding this comment.
Feels like this should follow our general pattern of being abstract here, rather than virtual. #Closed
There was a problem hiding this comment.
For example, I don't see any changes to WrappedNameTypeSymbol, which I would expect to see.
There was a problem hiding this comment.
The method is only valid when called on a definition. Added an assert.
There was a problem hiding this comment.
Re-opened this for the question about abstract versus virtual.
There was a problem hiding this comment.
Never mind. I'm slow on the uptake.
| { | ||
| public static void M(in int i) | ||
| { | ||
| System.Console.WriteLine(i); |
There was a problem hiding this comment.
I'm not really certain what this is testing: is the constructor called at runtime or not? Consider putting some side-effecting thing in to inform. #Closed
There was a problem hiding this comment.
We're testing the struct type can be used as an "attribute type" if the type has a public constructor. (The attribute type is not instantiated at runtime.) Added a comment to the test.
| "System.ITupleInternal.Size" }, | ||
| m1Tuple.MemberNames.ToArray()); | ||
| "Item1", | ||
| "Item2", |
There was a problem hiding this comment.
Consider reverting the changes in this file. #Closed
| Assert.Same(f1, test9.GetMembers()[0]); | ||
| Assert.Same(f2, test9.GetMembers()[1]); | ||
| Assert.True(((MethodSymbol)test9.GetMembers()[2]).IsDefaultValueTypeConstructor()); | ||
| Assert.True(((MethodSymbol)test9.GetMembers()[2]).IsDefaultValueTypeConstructor(requireZeroInit: false)); |
There was a problem hiding this comment.
Why are these asserting false? Shouldn't existing code be true? #Closed
| CompileAndVerify(sourceB, references: new[] { refA }, expectedOutput: | ||
| $@"True | ||
| True | ||
| {ExecutionConditionUtil.IsCoreClr}"); // Desktop framework ignores constructor in Activator.CreateInstance<T>() where T : struct. |
| {{ | ||
| {accessibility} S() {{ }} | ||
| }}"; | ||
| var comp = CreateCompilation(sourceA, parseOptions: TestOptions.RegularPreview); |
There was a problem hiding this comment.
I thought this was supposed to be a compiler error? #Closed
There was a problem hiding this comment.
This is an error but the error was not being verified since the focus of the test was using the resulting type. I've added VerifyDiagnostics() so the expected behavior is clear.
| } | ||
| struct S1 | ||
| { | ||
| internal bool Initialized = false; |
There was a problem hiding this comment.
This is a general comment for all the ThisInitializer tests, I can't reason about the behavior of the existing code.
| { | ||
| static void Main() | ||
| { | ||
| Console.WriteLine(new A<object>.S1().X); |
There was a problem hiding this comment.
I'd expect that with the current implementation, we'd see
falseon theSubstitutedNestedNamedTypeSymbol...
HasFieldInitializers() should only be called on a definition. Added an assert to that method to make it clear.
|
Done review pass (commit 17) In reply to: 856257577 |
|
Done review pass (commit 21). |
| switch (typeArgument.TypeKind) | ||
| { | ||
| case TypeKind.Struct: | ||
| return HasPublicParameterlessConstructor((NamedTypeSymbol)typeArgument, synthesizedIfMissing: true); |
There was a problem hiding this comment.
Just curious, do you know why the class case has a type argument check, but the struct case doesn't need one? #Resolved
There was a problem hiding this comment.
The typeArgument is the struct type and cannot be abstract.
| if ((object)node.Constructor == null || | ||
| (node.Arguments.Length == 0 && !node.Type.IsStructType()) || | ||
| node.Constructor.IsDefaultValueTypeConstructor()) | ||
| node.Constructor.IsDefaultValueTypeConstructor(requireZeroInit: false)) |
There was a problem hiding this comment.
Looks like expression trees scenarios are handled already.
Could you clarify the follow-up work item tracked in test plan? "Expression tree: synthesized parameterless constructor is not called in Expression e = () => new S();" #Closed
There was a problem hiding this comment.
This call should have used requireZeroInit: true. Fixed and checked off work item in test plan.
Support
publicparameterlessstructconstructors and declared in source, and supportstructfield initializers.Proposal: parameterless-struct-constructors
Test plan: #51698