From b6fff3115ec2907782050ce3d39150b535e121d0 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 25 Jul 2023 09:50:13 -0700 Subject: [PATCH 1/2] Ensure proper definite assignment analysis for an inline array that is not supported by language. --- .../FlowAnalysis/DefiniteAssignment.cs | 6 +- .../Portable/FlowAnalysis/FlowAnalysisPass.cs | 2 +- .../Test/Emit2/Semantics/InlineArrayTests.cs | 210 +++++++++++++++++- 3 files changed, 205 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index 2b25df8da8f90..587caa072d918 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -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)) { @@ -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 @@ -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; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs index d56594ef677e3..f8d3ae04207f2 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs @@ -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()); diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs index dbd7b1ec38943..21831087d49f8 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs @@ -11821,6 +11821,10 @@ static void M() C c; c.F._element0 = 1; _ = c.F; + + Buffer2Ref b; + b._element0 = ref (new [] { 1 })[0]; + _ = b; } } @@ -11829,13 +11833,25 @@ public struct Buffer2 { 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) ); } @@ -11855,6 +11871,10 @@ static void M() C c; c.F._element0 = 1; _ = c.F; + + Buffer2Ref b; + b._element0 = ref (new [] { 1 })[0]; + _ = b; } } @@ -11863,10 +11883,20 @@ public struct Buffer1 { 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] @@ -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", @" @@ -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) ); } @@ -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", @" @@ -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 +} "); } @@ -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", @" @@ -12962,11 +13074,42 @@ .maxstack 2 } "); + + 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) ); } @@ -13057,9 +13200,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", @" @@ -13076,6 +13235,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); @@ -13088,7 +13268,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) ); } From 19d357f91edf795d318c07bf5468b770e2819b28 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 25 Jul 2023 10:59:09 -0700 Subject: [PATCH 2/2] Formatting --- src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs index 21831087d49f8..3abddb9db8e5c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs @@ -13074,7 +13074,6 @@ .maxstack 2 } "); - verifier.VerifyIL("Buffer2Ref..ctor", @" {