Skip to content

Commit

Permalink
Implicit field initialization in struct constructors (#59788)
Browse files Browse the repository at this point in the history
  • Loading branch information
RikkiGibson authored Mar 28, 2022
1 parent bd00297 commit 65d5234
Show file tree
Hide file tree
Showing 48 changed files with 3,591 additions and 925 deletions.
96 changes: 78 additions & 18 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -864,14 +864,14 @@
<data name="WRN_UseDefViolationProperty_Title" xml:space="preserve">
<value>Use of possibly unassigned auto-implemented property</value>
</data>
<data name="ERR_UnassignedThis" xml:space="preserve">
<value>Field '{0}' must be fully assigned before control is returned to the caller</value>
<data name="ERR_UnassignedThisUnsupportedVersion" xml:space="preserve">
<value>Field '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the field.</value>
</data>
<data name="WRN_UnassignedThis" xml:space="preserve">
<value>Field '{0}' must be fully assigned before control is returned to the caller</value>
<data name="WRN_UnassignedThisUnsupportedVersion" xml:space="preserve">
<value>Field '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the field.</value>
</data>
<data name="WRN_UnassignedThis_Title" xml:space="preserve">
<value>Fields of a struct must be fully assigned in a constructor before control is returned to the caller</value>
<data name="WRN_UnassignedThisUnsupportedVersion_Title" xml:space="preserve">
<value>Fields of a struct must be fully assigned in a constructor before control is returned to the caller. Consider updating the language version to auto-default the field.</value>
</data>
<data name="ERR_AmbigQM" xml:space="preserve">
<value>Type of conditional expression cannot be determined because '{0}' and '{1}' implicitly convert to one another</value>
Expand Down Expand Up @@ -939,14 +939,14 @@
<data name="ERR_DefaultLiteralNotValid" xml:space="preserve">
<value>Use of default literal is not valid in this context</value>
</data>
<data name="ERR_UseDefViolationThis" xml:space="preserve">
<value>The 'this' object cannot be used before all of its fields have been assigned</value>
<data name="ERR_UseDefViolationThisUnsupportedVersion" xml:space="preserve">
<value>The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '{0}' to auto-default the unassigned fields.</value>
</data>
<data name="WRN_UseDefViolationThis" xml:space="preserve">
<value>The 'this' object cannot be used before all of its fields have been assigned</value>
<data name="WRN_UseDefViolationThisUnsupportedVersion" xml:space="preserve">
<value>The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '{0}' to auto-default the unassigned fields.</value>
</data>
<data name="WRN_UseDefViolationThis_Title" xml:space="preserve">
<value>The 'this' object cannot be used in a constructor before all of its fields have been assigned</value>
<data name="WRN_UseDefViolationThisUnsupportedVersion_Title" xml:space="preserve">
<value>The 'this' object cannot be used in a constructor before all of its fields have been assigned. Consider updating the language version to auto-default the unassigned fields.</value>
</data>
<data name="ERR_ArgsInvalid" xml:space="preserve">
<value>The __arglist construct is valid only within a variable argument method</value>
Expand Down Expand Up @@ -2318,14 +2318,14 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_RecursivelyTypedVariable" xml:space="preserve">
<value>Type of '{0}' cannot be inferred since its initializer directly or indirectly refers to the definition.</value>
</data>
<data name="ERR_UnassignedThisAutoProperty" xml:space="preserve">
<value>Auto-implemented property '{0}' must be fully assigned before control is returned to the caller.</value>
<data name="ERR_UnassignedThisAutoPropertyUnsupportedVersion" xml:space="preserve">
<value>Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property.</value>
</data>
<data name="WRN_UnassignedThisAutoProperty" xml:space="preserve">
<value>Auto-implemented property '{0}' must be fully assigned before control is returned to the caller.</value>
<data name="WRN_UnassignedThisAutoPropertyUnsupportedVersion" xml:space="preserve">
<value>Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property.</value>
</data>
<data name="WRN_UnassignedThisAutoProperty_Title" xml:space="preserve">
<value>An auto-implemented property must be fully assigned before control is returned to the caller.</value>
<data name="WRN_UnassignedThisAutoPropertyUnsupportedVersion_Title" xml:space="preserve">
<value>An auto-implemented property must be fully assigned before control is returned to the caller. Consider updating the language version to auto-default the property.</value>
</data>
<data name="ERR_VariableUsedBeforeDeclarationAndHidesField" xml:space="preserve">
<value>Cannot use local variable '{0}' before it is declared. The declaration of the local variable hides the field '{1}'.</value>
Expand Down Expand Up @@ -6697,6 +6697,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="IDS_FeatureLineSpanDirective" xml:space="preserve">
<value>line span directive</value>
</data>
<data name="IDS_FeatureAutoDefaultStructs" xml:space="preserve">
<value>auto default struct fields</value>
</data>
<data name="ERR_LineSpanDirectiveInvalidValue" xml:space="preserve">
<value>The #line directive value is missing or out of range</value>
</data>
Expand Down Expand Up @@ -6769,6 +6772,63 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ListPatternRequiresLength" xml:space="preserve">
<value>List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found.</value>
</data>

<data name="WRN_UseDefViolationPropertySupportedVersion" xml:space="preserve">
<value>Auto-implemented property '{0}' is read before being explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>
<data name="WRN_UseDefViolationPropertySupportedVersion_Title" xml:space="preserve">
<value>Auto-implemented property is read before being explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>

<data name="WRN_UseDefViolationFieldSupportedVersion" xml:space="preserve">
<value>Field '{0}' is read before being explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>
<data name="WRN_UseDefViolationFieldSupportedVersion_Title" xml:space="preserve">
<value>Field is read before being explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>

<data name="WRN_UseDefViolationThisSupportedVersion" xml:space="preserve">
<value>The 'this' object is read before all of its fields have been assigned, causing preceding implicit assignments of 'default' to non-explicitly assigned fields.</value>
</data>
<data name="WRN_UseDefViolationThisSupportedVersion_Title" xml:space="preserve">
<value>The 'this' object is read before all of its fields have been assigned, causing preceding implicit assignments of 'default' to non-explicitly assigned fields.</value>
</data>

<data name="WRN_UnassignedThisAutoPropertySupportedVersion" xml:space="preserve">
<value>Control is returned to caller before auto-implemented property '{0}' is explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>
<data name="WRN_UnassignedThisAutoPropertySupportedVersion_Title" xml:space="preserve">
<value>Control is returned to caller before auto-implemented property is explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>

<data name="WRN_UnassignedThisSupportedVersion" xml:space="preserve">
<value>Control is returned to caller before field '{0}' is explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>
<data name="WRN_UnassignedThisSupportedVersion_Title" xml:space="preserve">
<value>Control is returned to caller before field is explicitly assigned, causing a preceding implicit assignment of 'default'.</value>
</data>

<data name="ERR_UseDefViolationFieldUnsupportedVersion" xml:space="preserve">
<value>Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field.</value>
</data>
<data name="ERR_UseDefViolationPropertyUnsupportedVersion" xml:space="preserve">
<value>Use of possibly unassigned auto-implemented property '{0}'. Consider updating to language version '{1}' to auto-default the property.</value>
</data>

<data name="WRN_UseDefViolationFieldUnsupportedVersion" xml:space="preserve">
<value>Use of possibly unassigned field '{0}'. Consider updating to language version '{1}' to auto-default the field.</value>
</data>
<data name="WRN_UseDefViolationFieldUnsupportedVersion_Title" xml:space="preserve">
<value>Use of possibly unassigned field. Consider updating the language version to auto-default the field.</value>
</data>

<data name="WRN_UseDefViolationPropertyUnsupportedVersion" xml:space="preserve">
<value>Use of possibly unassigned auto-implemented property '{0}'. Consider updating to language version '{1}' to auto-default the property.</value>
</data>
<data name="WRN_UseDefViolationPropertyUnsupportedVersion_Title" xml:space="preserve">
<value>Use of possibly unassigned auto-implemented property. Consider updating the language version to auto-default the property.</value>
</data>

<data name="ERR_UnsupportedTypeForSlicePattern" xml:space="preserve">
<value>Slice patterns may not be used for a value of type '{0}'.</value>
</data>
Expand Down
8 changes: 4 additions & 4 deletions src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1015,7 +1015,7 @@ private void CompileMethod(
}

var unusedDiagnostics = DiagnosticBag.GetInstance();
DefiniteAssignmentPass.Analyze(_compilation, methodSymbol, initializerStatements, unusedDiagnostics, requireOutParamsAssigned: false);
DefiniteAssignmentPass.Analyze(_compilation, methodSymbol, initializerStatements, unusedDiagnostics, out _, requireOutParamsAssigned: false);
DiagnosticsPass.IssueDiagnostics(_compilation, initializerStatements, BindingDiagnosticBag.Discarded, methodSymbol);
unusedDiagnostics.Free();
}
Expand Down Expand Up @@ -1077,7 +1077,7 @@ processedInitializers.AfterInitializersState is null &&
// Flow analysis over the initializers is necessary in order to find assignments to fields.
// Bodies of implicit constructors do not get flow analysis later, so the initializers
// are analyzed here.
DefiniteAssignmentPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod.DiagnosticBag, requireOutParamsAssigned: false);
DefiniteAssignmentPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod.DiagnosticBag, out _, requireOutParamsAssigned: false);
}

// In order to get correct diagnostics, we need to analyze initializers and the body together.
Expand All @@ -1089,7 +1089,7 @@ processedInitializers.AfterInitializersState is null &&
{
// These analyses check for diagnostics in lambdas.
// Control flow analysis and implicit return insertion are unnecessary.
DefiniteAssignmentPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod.DiagnosticBag, requireOutParamsAssigned: false);
DefiniteAssignmentPass.Analyze(_compilation, methodSymbol, analyzedInitializers, diagsForCurrentMethod.DiagnosticBag, out _, requireOutParamsAssigned: false);
DiagnosticsPass.IssueDiagnostics(_compilation, analyzedInitializers, diagsForCurrentMethod, methodSymbol);
}
}
Expand Down Expand Up @@ -1122,7 +1122,7 @@ processedInitializers.AfterInitializersState is null &&
BoundBlock flowAnalyzedBody = null;
if (body != null)
{
flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, diagsForCurrentMethod.DiagnosticBag, hasTrailingExpression: hasTrailingExpression, originalBodyNested: originalBodyNested);
flowAnalyzedBody = FlowAnalysisPass.Rewrite(methodSymbol, body, compilationState, diagsForCurrentMethod, hasTrailingExpression: hasTrailingExpression, originalBodyNested: originalBodyNested);
}

bool hasErrors = _hasDeclarationErrors || diagsForCurrentMethod.HasAnyErrors() || processedInitializers.HasErrors;
Expand Down
23 changes: 17 additions & 6 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ internal enum ErrorCode
WRN_UnreferencedVar = 168,
WRN_UnreferencedField = 169,
ERR_UseDefViolationField = 170,
ERR_UnassignedThis = 171,
ERR_UnassignedThisUnsupportedVersion = 171,
ERR_AmbigQM = 172,
ERR_InvalidQM = 173, // Requires SymbolDistinguisher.
ERR_NoBaseClass = 174,
Expand All @@ -161,7 +161,7 @@ internal enum ErrorCode
WRN_IsAlwaysFalse = 184,
ERR_LockNeedsReference = 185,
ERR_NullNotValid = 186,
ERR_UseDefViolationThis = 188,
ERR_UseDefViolationThisUnsupportedVersion = 188,
ERR_ArgsInvalid = 190,
ERR_AssgReadonly = 191,
ERR_RefReadonly = 192,
Expand Down Expand Up @@ -582,7 +582,7 @@ internal enum ErrorCode
//ERR_AutoPropertiesMustHaveBothAccessors = 840,
ERR_VariableUsedBeforeDeclaration = 841,
//ERR_ExplicitLayoutAndAutoImplementedProperty = 842,
ERR_UnassignedThisAutoProperty = 843,
ERR_UnassignedThisAutoPropertyUnsupportedVersion = 843,
ERR_VariableUsedBeforeDeclarationAndHidesField = 844,
ERR_ExpressionTreeContainsBadCoalesce = 845,
ERR_ArrayInitializerExpected = 846,
Expand Down Expand Up @@ -1885,12 +1885,12 @@ internal enum ErrorCode

// The following warnings correspond to errors of the same name, but are reported
// when a definite assignment issue is reported due to private fields imported from metadata.
WRN_UnassignedThisAutoProperty = 8880,
WRN_UnassignedThis = 8881,
WRN_UnassignedThisAutoPropertyUnsupportedVersion = 8880,
WRN_UnassignedThisUnsupportedVersion = 8881,
WRN_ParamUnassigned = 8882,
WRN_UseDefViolationProperty = 8883,
WRN_UseDefViolationField = 8884,
WRN_UseDefViolationThis = 8885,
WRN_UseDefViolationThisUnsupportedVersion = 8885,
WRN_UseDefViolationOut = 8886,
WRN_UseDefViolation = 8887,

Expand Down Expand Up @@ -2051,6 +2051,17 @@ internal enum ErrorCode
ERR_MisplacedRecord = 9012,
ERR_PatternSpanCharCannotBeStringNull = 9013,

ERR_UseDefViolationPropertyUnsupportedVersion = 9014,
ERR_UseDefViolationFieldUnsupportedVersion = 9015,
WRN_UseDefViolationPropertyUnsupportedVersion = 9016,
WRN_UseDefViolationFieldUnsupportedVersion = 9017,

WRN_UseDefViolationPropertySupportedVersion = 9018,
WRN_UseDefViolationFieldSupportedVersion = 9019,
WRN_UseDefViolationThisSupportedVersion = 9020,
WRN_UnassignedThisAutoPropertySupportedVersion = 9021,
WRN_UnassignedThisSupportedVersion = 9022,

#endregion

// Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd)
Expand Down
13 changes: 10 additions & 3 deletions src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,14 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_NubExprIsConstBool2:
case ErrorCode.WRN_StaticInAsOrIs:
case ErrorCode.WRN_PrecedenceInversion:
case ErrorCode.WRN_UnassignedThisAutoProperty:
case ErrorCode.WRN_UnassignedThis:
case ErrorCode.WRN_UseDefViolationPropertyUnsupportedVersion:
case ErrorCode.WRN_UseDefViolationFieldUnsupportedVersion:
case ErrorCode.WRN_UnassignedThisAutoPropertyUnsupportedVersion:
case ErrorCode.WRN_UnassignedThisUnsupportedVersion:
case ErrorCode.WRN_ParamUnassigned:
case ErrorCode.WRN_UseDefViolationProperty:
case ErrorCode.WRN_UseDefViolationField:
case ErrorCode.WRN_UseDefViolationThis:
case ErrorCode.WRN_UseDefViolationThisUnsupportedVersion:
case ErrorCode.WRN_UseDefViolationOut:
case ErrorCode.WRN_UseDefViolation:
case ErrorCode.WRN_SyncAndAsyncEntryPoints:
Expand Down Expand Up @@ -494,6 +496,11 @@ internal static int GetWarningLevel(ErrorCode code)
case ErrorCode.WRN_InterpolatedStringHandlerArgumentAttributeIgnoredOnLambdaParameters:
case ErrorCode.WRN_CompileTimeCheckedOverflow:
case ErrorCode.WRN_MethGrpToNonDel:
case ErrorCode.WRN_UseDefViolationPropertySupportedVersion:
case ErrorCode.WRN_UseDefViolationFieldSupportedVersion:
case ErrorCode.WRN_UseDefViolationThisSupportedVersion:
case ErrorCode.WRN_UnassignedThisAutoPropertySupportedVersion:
case ErrorCode.WRN_UnassignedThisSupportedVersion:
return 1;
default:
return 0;
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ internal enum MessageID
IDS_FeatureRawStringLiterals = MessageBase + 12817,
IDS_FeatureSpanCharConstantPattern = MessageBase + 12818,
IDS_FeatureDisposalPattern = MessageBase + 12819,
IDS_FeatureAutoDefaultStructs = MessageBase + 12820,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -363,6 +364,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureCacheStaticMethodGroupConversion: // lowering check
case MessageID.IDS_ParameterNullChecking: // syntax check
case MessageID.IDS_FeatureSpanCharConstantPattern:
case MessageID.IDS_FeatureAutoDefaultStructs: // semantic check
return LanguageVersion.Preview;

// C# 10.0 features.
Expand Down
9 changes: 9 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ public override string GetErrorDisplayString(ISymbol symbol)
return SymbolDisplay.ToDisplayString(symbol, SymbolDisplayFormat.CSharpShortErrorMessageFormat);
}

public override bool GetIsEnabledByDefault(int code) =>
(ErrorCode)code is not (
ErrorCode.WRN_UseDefViolationPropertySupportedVersion
or ErrorCode.WRN_UseDefViolationFieldSupportedVersion
or ErrorCode.WRN_UseDefViolationThisSupportedVersion
or ErrorCode.WRN_UnassignedThisAutoPropertySupportedVersion
or ErrorCode.WRN_UnassignedThisSupportedVersion
);

public override ReportDiagnostic GetDiagnosticReport(DiagnosticInfo diagnosticInfo, CompilationOptions options)
{
bool hasPragmaSuppression;
Expand Down
Loading

0 comments on commit 65d5234

Please sign in to comment.