Skip to content

Commit

Permalink
Ensure proper definite assignment analysis for an inline array that i…
Browse files Browse the repository at this point in the history
…s not supported by language. (#69212)
  • Loading branch information
AlekseyTs authored Jul 26, 2023
1 parent a02ccf2 commit d0c8767
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ protected virtual void ReportUnassignedOutParameter(ParameterSymbol parameter, S

if (!reported)
{
if (parameterType.HasInlineArrayAttribute(out int length) && length > 1 && parameterType.TryGetInlineArrayElementField() is FieldSymbol elementField)
if (parameterType.HasInlineArrayAttribute(out int length) && length > 1 && parameterType.TryGetPossiblyUnsupportedByLanguageInlineArrayElementField() is FieldSymbol elementField)
{
if (!compilation.IsFeatureEnabled(MessageID.IDS_FeatureAutoDefaultStructs))
{
Expand Down Expand Up @@ -1269,7 +1269,7 @@ void addDiagnosticForStructThis(Symbol thisParameter, int thisSlot)
}
}

if (!foundUnassignedField && containingType.HasInlineArrayAttribute(out int length) && length > 1 && containingType.TryGetInlineArrayElementField() is FieldSymbol elementField)
if (!foundUnassignedField && containingType.HasInlineArrayAttribute(out int length) && length > 1 && containingType.TryGetPossiblyUnsupportedByLanguageInlineArrayElementField() is FieldSymbol elementField)
{
// Add the element field to the set of fields requiring initialization to indicate that the whole instance needs initialization.
// This is done explicitly only for unreported cases, because, if something was reported, then we already have added the
Expand Down Expand Up @@ -1657,7 +1657,7 @@ private bool FieldsAllSet(int containingSlot, LocalState state)
VariableIdentifier variable = variableBySlot[containingSlot];
TypeSymbol structType = variable.Symbol.GetTypeOrReturnType().Type;

if (structType.HasInlineArrayAttribute(out int length) && length > 1 && structType.TryGetInlineArrayElementField() is object)
if (structType.HasInlineArrayAttribute(out int length) && length > 1 && structType.TryGetPossiblyUnsupportedByLanguageInlineArrayElementField() is object)
{
// An inline array of length > 1 cannot be considered fully initialized judging only based on fields.
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private static BoundBlock PrependImplicitInitializations(BoundBlock body, Method

// Inline arrays of length > 1 must be initialized completely, simply initializing the element field is not sufficient.
// For arrays of length == 1, initializing the element field is sufficient.
if (containingType.HasInlineArrayAttribute(out int length) && length > 1 && containingType.TryGetInlineArrayElementField() is FieldSymbol elementField)
if (containingType.HasInlineArrayAttribute(out int length) && length > 1 && containingType.TryGetPossiblyUnsupportedByLanguageInlineArrayElementField() is FieldSymbol elementField)
{
Debug.Assert(elementField == implicitlyInitializedFields.Single());

Expand Down
209 changes: 200 additions & 9 deletions src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11821,6 +11821,10 @@ static void M()
C c;
c.F._element0 = 1;
_ = c.F;

Buffer2Ref b;
b._element0 = ref (new [] { 1 })[0];
_ = b;
}
}

Expand All @@ -11829,13 +11833,25 @@ public struct Buffer2<T>
{
public T _element0;
}

[System.Runtime.CompilerServices.InlineArray(2)]
public ref struct Buffer2Ref
{
public ref int _element0;
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll);

comp.VerifyDiagnostics(
// (13,13): error CS0170: Use of possibly unassigned field 'F'
// _ = c.F;
Diagnostic(ErrorCode.ERR_UseDefViolationField, "c.F").WithArguments("F").WithLocation(13, 13)
Diagnostic(ErrorCode.ERR_UseDefViolationField, "c.F").WithArguments("F").WithLocation(13, 13),
// (17,13): error CS0165: Use of unassigned local variable 'b'
// _ = b;
Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(17, 13),
// (30,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element0;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element0").WithLocation(30, 20)
);
}

Expand All @@ -11855,6 +11871,10 @@ static void M()
C c;
c.F._element0 = 1;
_ = c.F;

Buffer2Ref b;
b._element0 = ref (new [] { 1 })[0];
_ = b;
}
}

Expand All @@ -11863,10 +11883,20 @@ public struct Buffer1<T>
{
public T _element0;
}

[System.Runtime.CompilerServices.InlineArray(1)]
public ref struct Buffer2Ref
{
public ref int _element0;
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll);

comp.VerifyDiagnostics();
comp.VerifyDiagnostics(
// (30,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element0;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element0").WithLocation(30, 20)
);
}

[Fact]
Expand Down Expand Up @@ -12822,9 +12852,24 @@ public Buffer1()
_element0 = 1;
}
}

[System.Runtime.CompilerServices.InlineArray(1)]
public ref struct Buffer1Ref
{
public ref int _element2;

public Buffer1Ref()
{
_element2 = ref (new [] { 1 })[0];
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "1").VerifyDiagnostics(
// (25,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element2;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element2").WithLocation(25, 20)
);

verifier.VerifyIL("Buffer1..ctor",
@"
Expand All @@ -12836,12 +12881,37 @@ .maxstack 2
IL_0002: stfld ""int Buffer1._element0""
IL_0007: ret
}
");

verifier.VerifyIL("Buffer1Ref..ctor",
@"
{
// Code size 23 (0x17)
.maxstack 5
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: newarr ""int""
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldc.i4.1
IL_000a: stelem.i4
IL_000b: ldc.i4.0
IL_000c: ldelema ""int""
IL_0011: stfld ""ref int Buffer1Ref._element2""
IL_0016: ret
}
");
comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
// (7,30): error CS8652: The feature 'inline arrays' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// System.Console.Write(f[0]);
Diagnostic(ErrorCode.ERR_FeatureInPreview, "f[0]").WithArguments("inline arrays").WithLocation(7, 30)
Diagnostic(ErrorCode.ERR_FeatureInPreview, "f[0]").WithArguments("inline arrays").WithLocation(7, 30),
// (25,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater.
// public ref int _element2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref int").WithArguments("ref fields", "11.0").WithLocation(25, 12),
// (25,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element2;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element2").WithLocation(25, 20)
);
}

Expand Down Expand Up @@ -12913,9 +12983,23 @@ public Buffer1()
{
}
}

[System.Runtime.CompilerServices.InlineArray(1)]
public ref struct Buffer1Ref
{
public ref int _element0;

public Buffer1Ref()
{
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: "0 1 0").VerifyDiagnostics();
var verifier = CompileAndVerify(comp, expectedOutput: "0 1 0", verify: Verification.Fails).VerifyDiagnostics(
// (31,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element0;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element0").WithLocation(31, 20)
);

verifier.VerifyIL("Buffer1..ctor",
@"
Expand All @@ -12927,6 +13011,19 @@ .maxstack 2
IL_0002: stfld ""int Buffer1._element0""
IL_0007: ret
}
");

verifier.VerifyIL("Buffer1Ref..ctor",
@"
{
// Code size 9 (0x9)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldc.i4.0
IL_0002: conv.u
IL_0003: stfld ""ref int Buffer1Ref._element0""
IL_0008: ret
}
");
}

Expand All @@ -12944,9 +13041,24 @@ public Buffer2()
_element0 = 1;
}
}

[System.Runtime.CompilerServices.InlineArray(2)]
public ref struct Buffer2Ref
{
public ref int _element2;

public Buffer2Ref()
{
_element2 = ref (new [] { 1 })[0];
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll);
var verifier = CompileAndVerify(comp, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics();
var verifier = CompileAndVerify(comp, verify: VerifyOnMonoOrCoreClr).VerifyDiagnostics(
// (16,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element2;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element2").WithLocation(16, 20)
);

verifier.VerifyIL("Buffer2..ctor",
@"
Expand All @@ -12960,13 +13072,43 @@ .maxstack 2
IL_0009: stfld ""int Buffer2._element0""
IL_000e: ret
}
");

verifier.VerifyIL("Buffer2Ref..ctor",
@"
{
// Code size 30 (0x1e)
.maxstack 5
IL_0000: ldarg.0
IL_0001: initobj ""Buffer2Ref""
IL_0007: ldarg.0
IL_0008: ldc.i4.1
IL_0009: newarr ""int""
IL_000e: dup
IL_000f: ldc.i4.0
IL_0010: ldc.i4.1
IL_0011: stelem.i4
IL_0012: ldc.i4.0
IL_0013: ldelema ""int""
IL_0018: stfld ""ref int Buffer2Ref._element2""
IL_001d: ret
}
");

comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular10);
comp.VerifyDiagnostics(
// (7,12): error CS0177: The out parameter 'this' must be assigned to before control leaves the current method
// public Buffer2()
Diagnostic(ErrorCode.ERR_ParamUnassigned, "Buffer2").WithArguments("this").WithLocation(7, 12)
Diagnostic(ErrorCode.ERR_ParamUnassigned, "Buffer2").WithArguments("this").WithLocation(7, 12),
// (16,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater.
// public ref int _element2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref int").WithArguments("ref fields", "11.0").WithLocation(16, 12),
// (16,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element2;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element2").WithLocation(16, 20),
// (18,12): error CS0177: The out parameter 'this' must be assigned to before control leaves the current method
// public Buffer2Ref()
Diagnostic(ErrorCode.ERR_ParamUnassigned, "Buffer2Ref").WithArguments("this").WithLocation(18, 12)
);
}

Expand Down Expand Up @@ -13057,9 +13199,25 @@ public Buffer2()
_ = this[0];
}
}

[System.Runtime.CompilerServices.InlineArray(2)]
public ref struct Buffer2Ref
{
public ref int _element2;

public Buffer2Ref()
{
_element2 = ref (new [] { 1 })[0];
_ = this;
}
}
";
var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll);
var verifier = CompileAndVerify(comp).VerifyDiagnostics();
var verifier = CompileAndVerify(comp).VerifyDiagnostics(
// (17,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element2;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element2").WithLocation(17, 20)
);

verifier.VerifyIL("Buffer2..ctor",
@"
Expand All @@ -13076,6 +13234,27 @@ .maxstack 2
IL_0014: pop
IL_0015: ret
}
");

verifier.VerifyIL("Buffer2Ref..ctor",
@"
{
// Code size 30 (0x1e)
.maxstack 5
IL_0000: ldarg.0
IL_0001: initobj ""Buffer2Ref""
IL_0007: ldarg.0
IL_0008: ldc.i4.1
IL_0009: newarr ""int""
IL_000e: dup
IL_000f: ldc.i4.0
IL_0010: ldc.i4.1
IL_0011: stelem.i4
IL_0012: ldc.i4.0
IL_0013: ldelema ""int""
IL_0018: stfld ""ref int Buffer2Ref._element2""
IL_001d: ret
}
");

comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular10);
Expand All @@ -13088,7 +13267,19 @@ .maxstack 2
Diagnostic(ErrorCode.ERR_FeatureInPreview, "this[0]").WithArguments("inline arrays").WithLocation(10, 13),
// (10,13): error CS0188: The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '11.0' to auto-default the unassigned fields.
// _ = this[0];
Diagnostic(ErrorCode.ERR_UseDefViolationThisUnsupportedVersion, "this").WithArguments("11.0").WithLocation(10, 13)
Diagnostic(ErrorCode.ERR_UseDefViolationThisUnsupportedVersion, "this").WithArguments("11.0").WithLocation(10, 13),
// (17,12): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater.
// public ref int _element2;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "ref int").WithArguments("ref fields", "11.0").WithLocation(17, 12),
// (17,20): warning CS9184: 'Inline arrays' language feature is not supported for inline array types with element field which is either a 'ref' field, or has type that is not valid as a type argument.
// public ref int _element2;
Diagnostic(ErrorCode.WRN_InlineArrayNotSupportedByLanguage, "_element2").WithLocation(17, 20),
// (19,12): error CS0177: The out parameter 'this' must be assigned to before control leaves the current method
// public Buffer2Ref()
Diagnostic(ErrorCode.ERR_ParamUnassigned, "Buffer2Ref").WithArguments("this").WithLocation(19, 12),
// (22,13): error CS0188: The 'this' object cannot be used before all of its fields have been assigned. Consider updating to language version '11.0' to auto-default the unassigned fields.
// _ = this;
Diagnostic(ErrorCode.ERR_UseDefViolationThisUnsupportedVersion, "this").WithArguments("11.0").WithLocation(22, 13)
);
}

Expand Down

0 comments on commit d0c8767

Please sign in to comment.