Skip to content

Commit

Permalink
[17.0] Report errors for structs with missing field initializers and …
Browse files Browse the repository at this point in the history
…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)
  • Loading branch information
RikkiGibson authored Feb 16, 2022
2 parents 25e7a33 + 9d2819a commit ff289c4
Show file tree
Hide file tree
Showing 20 changed files with 1,008 additions and 181 deletions.
32 changes: 27 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3420,22 +3420,30 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor,

bool thisInitializer = initializer?.IsKind(SyntaxKind.ThisConstructorInitializer) == true;
if (!thisInitializer &&
ContainingType.GetMembersUnordered().OfType<SynthesizedRecordConstructor>().Any())
hasRecordPrimaryConstructor())
{
var constructorSymbol = (MethodSymbol)this.ContainingMember();
if (!constructorSymbol.IsStatic &&
if (isInstanceConstructor(out MethodSymbol constructorSymbol) &&
!SynthesizedRecordCopyCtor.IsCopyConstructor(constructorSymbol))
{
// Note: we check the constructor initializer of copy constructors elsewhere
Error(diagnostics, ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, initializer?.ThisOrBaseKeyword ?? constructor.Identifier);
}
}

bool isDefaultValueTypeInitializer = thisInitializer
&& ContainingType.IsDefaultValueTypeConstructor(initializer);

if (isDefaultValueTypeInitializer &&
isInstanceConstructor(out _) &&
hasRecordPrimaryConstructor())
{
Error(diagnostics, ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, initializer.ThisOrBaseKeyword);
}

// The `: this()` initializer is ignored when it is a default value type constructor
// and we need to include field initializers into the constructor.
bool skipInitializer = includesFieldInitializers
&& thisInitializer
&& ContainingType.IsDefaultValueTypeConstructor(initializer);
&& isDefaultValueTypeInitializer;

// Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel
return new BoundConstructorMethodBody(constructor,
Expand All @@ -3447,6 +3455,20 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor,
null :
bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody,
constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded));

bool hasRecordPrimaryConstructor() =>
ContainingType.GetMembersUnordered().OfType<SynthesizedRecordConstructor>().Any();

bool isInstanceConstructor(out MethodSymbol constructorSymbol)
{
if (this.ContainingMember() is MethodSymbol { IsStatic: false } method)
{
constructorSymbol = method;
return true;
}
constructorSymbol = null;
return false;
}
}

internal virtual BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax initializer, BindingDiagnosticBag diagnostics)
Expand Down
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6866,6 +6866,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 constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor.</value>
</data>
<data name="WRN_CompileTimeCheckedOverflow" xml:space="preserve">
<value>The operation may overflow '{0}' at runtime (use 'unchecked' syntax to override)</value>
</data>
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,7 @@ internal enum ErrorCode

WRN_CompileTimeCheckedOverflow = 8973,
WRN_MethGrpToNonDel = 8974,
ERR_RecordStructConstructorCallsDefaultConstructor = 8982,

#endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static BoundBlock Rewrite(
if (method.ReturnsVoid || method.IsIterator || method.IsAsyncEffectivelyReturningTask(compilation))
{
// we don't analyze synthesized void methods.
if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer) ||
if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer && !(method.ContainingType.IsStructType() && method.IsParameterlessConstructor() && !method.IsDefaultValueTypeConstructor(requireZeroInit: true))) ||
Analyze(compilation, method, block, diagnostics))
{
block = AppendImplicitReturn(block, method, originalBodyNested);
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4932,19 +4932,19 @@ public void Struct_ImplementSynthesizedConstructor()
struct S
{
int a = 1;
int b;
int b = 2;
}
";
var source1 =
@"
struct S
{
int a = 1;
int b;
int b = 2;

public S()
{
b = 2;
b = 3;
}
}
";
Expand Down
Loading

0 comments on commit ff289c4

Please sign in to comment.