Skip to content

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

Merged
cston merged 2 commits intodotnet:mainfrom
cston:58328
Dec 16, 2021
Merged

Report error if 'record struct' constructor calls default parameterless constructor#58339
cston merged 2 commits intodotnet:mainfrom
cston:58328

Conversation

@cston
Copy link
Copy Markdown
Contributor

@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

@ghost ghost added the Area-Compilers label Dec 15, 2021

[Fact]
[WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")]
public void ExplicitConstructors_05()
Copy link
Copy Markdown
Contributor 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.

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
Copy Markdown
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
Copy Markdown
Contributor 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
Copy Markdown
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
bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody,
constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded));

bool hasAnyRecordConstructors() =>
Copy link
Copy Markdown
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

<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
Copy Markdown
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

&& thisInitializer
&& ContainingType.IsDefaultValueTypeConstructor(initializer);

if (skipInitializer &&
Copy link
Copy Markdown
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
Copy Markdown
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

&& thisInitializer
&& ContainingType.IsDefaultValueTypeConstructor(initializer);

if (skipInitializer &&
Copy link
Copy Markdown
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
Copy Markdown
Contributor

Done with review pass (commit 2)

@cston
Copy link
Copy Markdown
Contributor 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

Development

Successfully merging this pull request may close these issues.

System.InvalidProgramException when constructing a record struct with parameter list and calling the default constructor

5 participants