Skip to content
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

Report error if 'record struct' constructor calls default parameterless constructor #58339

Merged
merged 2 commits into from
Dec 16, 2021

Conversation

cston
Copy link
Member

@cston cston commented Dec 15, 2021

Confirmed with C# LDT:
In a record struct with a primary constructor, explicit constructors require a this() initializer that invokes the primary constructor or another explicitly declared constructor.

Fixes #58328


[Fact]
[WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")]
public void ExplicitConstructors_05()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExplicitConstructors_05

The this() constructor initializer calls in this test and the following resulted in invalid code previously.

@@ -1993,7 +1993,7 @@ internal enum ErrorCode
WRN_CallerArgumentExpressionParamForUnconsumedLocation = 8966,
ERR_NewlinesAreNotAllowedInsideANonVerbatimInterpolatedString = 8967,
ERR_AttrTypeArgCannotBeTypeVar = 8968,
// WRN_AttrDependentTypeNotAllowed = 8969, // Backed out of of warning wave 6, may be reintroduced later
ERR_RecordStructConstructorCallsDefaultConstructor = 8969, // Added in 17.1 for an invalid C#10 scenario.
Copy link
Member

@Youssef1313 Youssef1313 Dec 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is reusing the warning number okay here? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, but reusing seems unnecessary. Updated.

@cston cston marked this pull request as ready for review December 15, 2021 17:48
@cston cston requested a review from a team as a code owner December 15, 2021 17:48
Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (commit 1)

@cston cston merged commit 9fa4adf into dotnet:main Dec 16, 2021
@ghost ghost added this to the Next milestone Dec 16, 2021
@cston cston deleted the 58328 branch December 16, 2021 04:54
@cston cston modified the milestones: Next, 17.1 Dec 16, 2021
@@ -3524,6 +3530,9 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor,
null :
bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody,
constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded));

bool hasAnyRecordConstructors() =>
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasAnyRecordConstructors

I think the name is confusing. The body is checking for a primary constructor, not just any constructor. #Closed

@@ -6911,6 +6911,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_LambdaWithAttributesToExpressionTree" xml:space="preserve">
<value>A lambda expression with attributes cannot be converted to an expression tree</value>
</data>
<data name="ERR_RecordStructConstructorCallsDefaultConstructor" xml:space="preserve">
<value>A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor.</value>
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor.

The wording looks too general. The requirement is only for records with primary constructors, I think the message should reflect that. See, for example, how we do that for UnexpectedOrMissingConstructorInitializerInRecord:

  <data name="ERR_UnexpectedOrMissingConstructorInitializerInRecord" xml:space="preserve">
    <value>A constructor declared in a record with parameter list must have 'this' constructor initializer.</value>
  </data>
``` #Closed

@@ -3514,6 +3514,12 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor,
&& thisInitializer
&& ContainingType.IsDefaultValueTypeConstructor(initializer);

if (skipInitializer &&
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skipInitializer

Should we also check if !constructorSymbol.IsStatic? #Closed

}
record struct S4(char A, char B)
{
public S4(object o) : this() { }
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public S4(object o) : this() { }

Consider also testing with static constructors. For example:

record struct S(int x)
{
    static S() : this()
    {}

    static int F = 3;
}
``` #Closed

@@ -3514,6 +3514,12 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor,
&& thisInitializer
&& ContainingType.IsDefaultValueTypeConstructor(initializer);

if (skipInitializer &&
Copy link
Contributor

@AlekseyTs AlekseyTs Dec 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skipInitializer

The fact that skipInitializer depends on includesFieldInitializers looks suspicious. I think it is technically possible to have a primary constructor and no initializers. It would be good trying to construct a scenario like that. Consider instead using condition thisInitializer && ContainingType.IsDefaultValueTypeConstructor(initializer), I think that would exactly reflect the condition that design requires us to check.
#Closed

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 2)

@cston
Copy link
Member Author

cston commented Dec 20, 2021

Responded to additional feedback in #58430.

cston added a commit to cston/roslyn that referenced this pull request Jan 25, 2022
RikkiGibson added a commit that referenced this pull request Feb 16, 2022
…implicit parameterless constructor (#59055)

* Report error if 'record struct' constructor calls default parameterless constructor (#58339)

* Improve error reporting for 'this()' initializer from 'record struct' constructor

* Require definite assignment of all fields if struct includes any field initializers (#57925)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants