From f0d3f46579414c948614a9ca5b4c3f1e0f86f7c9 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 4 Dec 2024 12:34:23 +0100 Subject: [PATCH] Remove scope variance exceptions --- .../Compiler Breaking Changes - DotNet 10.md | 19 + .../Portable/Binder/Binder_Conversions.cs | 37 +- ...berContainerSymbol_ImplementationChecks.cs | 82 +---- .../CSharp/Portable/Symbols/TypeSymbol.cs | 36 +- .../Test/Emit3/RefStructInterfacesTests.cs | 12 +- .../Semantic/Semantics/RefEscapingTests.cs | 325 ++++++++++++++++++ .../Test/Semantic/Semantics/RefFieldTests.cs | 47 ++- 7 files changed, 437 insertions(+), 121 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 48f544a359ebc..b8214835d7dc5 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -118,3 +118,22 @@ class C } ``` +## Variance of `scoped` and `[UnscopedRef]` is more strict + +***Introduced in Visual Studio 2022 version 17.13*** + +Scope can be changed when overriding a method, implementing an interface, or converting a lambda/method to a delegate under +[some conditions](https://github.com/dotnet/csharplang/blob/05064c2a9567b7a58a07e526dff403ece1866541/proposals/csharp-11.0/low-level-struct-improvements.md#scoped-mismatch) +(roughly, `scoped` can be added and `[UnscopedRef]` can be removed). +Previously, the compiler did not report an error/warning for such mismatch under some circumstances, but it is now always reported. + +```cs +D1 d1 = (ref int i) => { }; // previously no mismatch error reported, now: + // error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D1'. + +D2 d2 = (ref int i) => ref i; // an error was and continues to be reported: + // error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D2'. + +delegate void D1(scoped ref int x); +delegate ref int D2(scoped ref int x); +``` diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 7df03440b692e..fae2378ed5b04 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -2274,26 +2274,23 @@ private static void CheckParameterModifierMismatchMethodConversion(SyntaxNode sy return; } - if (SourceMemberContainerTypeSymbol.RequiresValidScopedOverrideForRefSafety(delegateMethod)) - { - SourceMemberContainerTypeSymbol.CheckValidScopedOverride( - delegateMethod, - lambdaOrMethod, - diagnostics, - static (diagnostics, delegateMethod, lambdaOrMethod, parameter, _, typeAndSyntax) => - { - diagnostics.Add( - SourceMemberContainerTypeSymbol.ReportInvalidScopedOverrideAsError(delegateMethod, lambdaOrMethod) ? - ErrorCode.ERR_ScopedMismatchInParameterOfTarget : - ErrorCode.WRN_ScopedMismatchInParameterOfTarget, - typeAndSyntax.Syntax.Location, - new FormattedSymbol(parameter, SymbolDisplayFormat.ShortFormat), - typeAndSyntax.Type); - }, - (Type: targetType, Syntax: syntax), - allowVariance: true, - invokedAsExtensionMethod: invokedAsExtensionMethod); - } + SourceMemberContainerTypeSymbol.CheckValidScopedOverride( + delegateMethod, + lambdaOrMethod, + diagnostics, + static (diagnostics, delegateMethod, lambdaOrMethod, parameter, _, typeAndSyntax) => + { + diagnostics.Add( + SourceMemberContainerTypeSymbol.ReportInvalidScopedOverrideAsError(delegateMethod, lambdaOrMethod) ? + ErrorCode.ERR_ScopedMismatchInParameterOfTarget : + ErrorCode.WRN_ScopedMismatchInParameterOfTarget, + typeAndSyntax.Syntax.Location, + new FormattedSymbol(parameter, SymbolDisplayFormat.ShortFormat), + typeAndSyntax.Type); + }, + (Type: targetType, Syntax: syntax), + allowVariance: true, + invokedAsExtensionMethod: invokedAsExtensionMethod); SourceMemberContainerTypeSymbol.CheckRefReadonlyInMismatch( delegateMethod, lambdaOrMethod, diagnostics, diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index 0f2216f4fef46..1addacefa96c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -1149,25 +1149,22 @@ static void checkValidMethodOverride( MethodSymbol overridingMethod, BindingDiagnosticBag diagnostics) { - if (RequiresValidScopedOverrideForRefSafety(overriddenMethod)) - { - CheckValidScopedOverride( - overriddenMethod, - overridingMethod, - diagnostics, - static (diagnostics, overriddenMethod, overridingMethod, overridingParameter, _, location) => - { - diagnostics.Add( - ReportInvalidScopedOverrideAsError(overriddenMethod, overridingMethod) ? - ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation : - ErrorCode.WRN_ScopedMismatchInParameterOfOverrideOrImplementation, - location, - new FormattedSymbol(overridingParameter, SymbolDisplayFormat.ShortFormat)); - }, - overridingMemberLocation, - allowVariance: true, - invokedAsExtensionMethod: false); - } + CheckValidScopedOverride( + overriddenMethod, + overridingMethod, + diagnostics, + static (diagnostics, overriddenMethod, overridingMethod, overridingParameter, _, location) => + { + diagnostics.Add( + ReportInvalidScopedOverrideAsError(overriddenMethod, overridingMethod) ? + ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation : + ErrorCode.WRN_ScopedMismatchInParameterOfOverrideOrImplementation, + location, + new FormattedSymbol(overridingParameter, SymbolDisplayFormat.ShortFormat)); + }, + overridingMemberLocation, + allowVariance: true, + invokedAsExtensionMethod: false); CheckValidNullableMethodOverride(overridingMethod.DeclaringCompilation, overriddenMethod, overridingMethod, diagnostics, ReportBadReturn, @@ -1369,53 +1366,6 @@ static bool isValidNullableConversion( } #nullable enable - /// - /// Returns true if the method signature must match, with respect to scoped for ref safety, - /// in overrides, interface implementations, or delegate conversions. - /// - internal static bool RequiresValidScopedOverrideForRefSafety(MethodSymbol? method) - { - if (method is null) - { - return false; - } - - var parameters = method.Parameters; - - // https://github.com/dotnet/csharplang/blob/main/proposals/csharp-11.0/low-level-struct-improvements.md#scoped-mismatch - // The compiler will report a diagnostic for _unsafe scoped mismatches_ across overrides, interface implementations, and delegate conversions when: - // - The method returns a `ref struct` or returns a `ref` or `ref readonly`, or the method has a `ref` or `out` parameter of `ref struct` type, and - // ... - int nRefParametersRequired; - if (method.ReturnType.IsRefLikeOrAllowsRefLikeType() || - (method.RefKind is RefKind.Ref or RefKind.RefReadOnly)) - { - nRefParametersRequired = 1; - } - else if (parameters.Any(p => (p.RefKind is RefKind.Ref or RefKind.Out) && p.Type.IsRefLikeOrAllowsRefLikeType())) - { - nRefParametersRequired = 2; // including the parameter found above - } - else - { - return false; - } - - // ... - // - The method has at least one additional `ref`, `in`, `ref readonly`, or `out` parameter, or a parameter of `ref struct` type. - int nRefParameters = parameters.Count(p => p.RefKind is RefKind.Ref or RefKind.In or RefKind.RefReadOnlyParameter or RefKind.Out); - if (nRefParameters >= nRefParametersRequired) - { - return true; - } - else if (parameters.Any(p => p.RefKind == RefKind.None && p.Type.IsRefLikeOrAllowsRefLikeType())) - { - return true; - } - - return false; - } - /// /// Returns true if a scoped mismatch should be reported as an error rather than a warning. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 61fd5fa1bc95e..4763f5d7fa071 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -1855,25 +1855,23 @@ static void checkMethodOverride( reportMismatchInParameterType, (implementingType, isExplicit)); - if (SourceMemberContainerTypeSymbol.RequiresValidScopedOverrideForRefSafety(implementedMethod)) - { - SourceMemberContainerTypeSymbol.CheckValidScopedOverride( - implementedMethod, - implementingMethod, - diagnostics, - static (diagnostics, implementedMethod, implementingMethod, implementingParameter, _, arg) => - { - diagnostics.Add( - SourceMemberContainerTypeSymbol.ReportInvalidScopedOverrideAsError(implementedMethod, implementingMethod) ? - ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation : - ErrorCode.WRN_ScopedMismatchInParameterOfOverrideOrImplementation, - GetImplicitImplementationDiagnosticLocation(implementedMethod, arg.implementingType, implementingMethod), - new FormattedSymbol(implementingParameter, SymbolDisplayFormat.ShortFormat)); - }, - (implementingType, isExplicit), - allowVariance: true, - invokedAsExtensionMethod: false); - } + SourceMemberContainerTypeSymbol.CheckValidScopedOverride( + implementedMethod, + implementingMethod, + diagnostics, + static (diagnostics, implementedMethod, implementingMethod, implementingParameter, _, arg) => + { + diagnostics.Add( + SourceMemberContainerTypeSymbol.ReportInvalidScopedOverrideAsError(implementedMethod, implementingMethod) ? + ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation : + ErrorCode.WRN_ScopedMismatchInParameterOfOverrideOrImplementation, + GetImplicitImplementationDiagnosticLocation(implementedMethod, arg.implementingType, implementingMethod), + new FormattedSymbol(implementingParameter, SymbolDisplayFormat.ShortFormat)); + }, + (implementingType, isExplicit), + allowVariance: true, + invokedAsExtensionMethod: false); + SourceMemberContainerTypeSymbol.CheckRefReadonlyInMismatch( implementedMethod, implementingMethod, diagnostics, static (diagnostics, implementedMethod, implementingMethod, implementingParameter, _, arg) => diff --git a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index ec891680f6a61..0ce57b6003d2a 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -21357,7 +21357,7 @@ class Helper delegate void D2(T x); static D1 d11 = M1; - static D1 d12 = M2; + static D1 d12 = M2; // 1 static D2 d21 = M1; static D2 d22 = M2; @@ -21371,7 +21371,7 @@ class Helper delegate void D2(Span x); static D1 d11 = M1; - static D1 d12 = M2; + static D1 d12 = M2; // 2 static D2 d21 = M1; static D2 d22 = M2; @@ -21380,7 +21380,13 @@ static void M2(Span x) {} } "; var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); - comp.VerifyEmitDiagnostics(); + comp.VerifyEmitDiagnostics( + // (11,21): error CS8986: The 'scoped' modifier of parameter 'x' doesn't match target 'Helper.D1'. + // static D1 d12 = M2; // 1 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "M2").WithArguments("x", "Helper.D1").WithLocation(11, 21), + // (25,21): error CS8986: The 'scoped' modifier of parameter 'x' doesn't match target 'Helper.D1'. + // static D1 d12 = M2; // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "M2").WithArguments("x", "Helper.D1").WithLocation(25, 21)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index c0c092edba74b..385e0a0bdd50e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -10358,5 +10358,330 @@ public static void M([UnscopedRef] ref S s) """; CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Disallowed_ImplicitInterface( + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + interface I + { + void M({{modifier}} R r); + } + + class C : I + { + public void M([UnscopedRef] {{modifier}} R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics( + // (10,17): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. + // public void M([UnscopedRef] ref R r) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "M").WithArguments("r").WithLocation(10, 17)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Disallowed_ExplicitInterface( + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + interface I + { + void M({{modifier}} R r); + } + + class C : I + { + void I.M([UnscopedRef] {{modifier}} R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics( + // (10,12): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. + // void I.M([UnscopedRef] ref R r) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "M").WithArguments("r").WithLocation(10, 12)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Disallowed_Override( + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + abstract class B + { + public abstract void M({{modifier}} R r); + } + + class C : B + { + public override void M([UnscopedRef] {{modifier}} R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics( + // (10,26): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. + // public override void M([UnscopedRef] ref R r) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "M").WithArguments("r").WithLocation(10, 26)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Disallowed_DelegateConversion( + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + D d = C.M; + + delegate void D({{modifier}} R r); + + static class C + { + public static void M([UnscopedRef] {{modifier}} R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics( + // (3,7): error CS8986: The 'scoped' modifier of parameter 'r' doesn't match target 'D'. + // D d = C.M; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "C.M").WithArguments("r", "D").WithLocation(3, 7)); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "[System.Diagnostics.CodeAnalysis.UnscopedRef]")] + [InlineData("", "")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Allowed_ImplicitInterface(string attr1, string attr2) + { + var source = $$""" + interface I + { + void M({{attr1}} ref R r); + } + + class C : I + { + public void M({{attr2}} ref R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_ScopedRef_ImplicitInterface( + [CombinatorialValues("scoped", "")] string scoped1, + [CombinatorialValues("scoped", "")] string scoped2, + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + interface I + { + void M({{scoped1}} {{modifier}} R r); + } + + class C : I + { + public void M({{scoped2}} {{modifier}} R r) { } + } + + ref struct R; + """; + + var comp = CreateCompilation(source); + + if (scoped1 == "scoped" && scoped2 == "" && modifier == "ref") + { + comp.VerifyDiagnostics( + // (8,17): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. + // public void M( ref R r) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "M").WithArguments("r").WithLocation(8, 17)); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "[System.Diagnostics.CodeAnalysis.UnscopedRef]")] + [InlineData("", "")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Allowed_ExplicitInterface(string attr1, string attr2) + { + var source = $$""" + interface I + { + void M({{attr1}} ref R r); + } + + class C : I + { + void I.M({{attr2}} ref R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_ScopedRef_ExplicitInterface( + [CombinatorialValues("scoped", "")] string scoped1, + [CombinatorialValues("scoped", "")] string scoped2, + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + interface I + { + void M({{scoped1}} {{modifier}} R r); + } + + class C : I + { + void I.M({{scoped2}} {{modifier}} R r) { } + } + + ref struct R; + """; + + var comp = CreateCompilation(source); + + if (scoped1 == "scoped" && scoped2 == "" && modifier == "ref") + { + comp.VerifyDiagnostics( + // (8,12): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. + // void I.M( ref R r) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "M").WithArguments("r").WithLocation(8, 12)); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "[System.Diagnostics.CodeAnalysis.UnscopedRef]")] + [InlineData("", "")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Allowed_Override(string attr1, string attr2) + { + var source = $$""" + abstract class B + { + public abstract void M({{attr1}} ref R r); + } + + class C : B + { + public override void M({{attr2}} ref R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_ScopedRef_Override( + [CombinatorialValues("scoped", "")] string scoped1, + [CombinatorialValues("scoped", "")] string scoped2, + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + abstract class B + { + public abstract void M({{scoped1}} {{modifier}} R r); + } + + class C : B + { + public override void M({{scoped2}} {{modifier}} R r) { } + } + + ref struct R; + """; + + var comp = CreateCompilation(source); + + if (scoped1 == "scoped" && scoped2 == "" && modifier == "ref") + { + comp.VerifyDiagnostics( + // (8,26): error CS8987: The 'scoped' modifier of parameter 'r' doesn't match overridden or implemented member. + // public override void M( ref R r) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "M").WithArguments("r").WithLocation(8, 26)); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "")] + [InlineData("[System.Diagnostics.CodeAnalysis.UnscopedRef]", "[System.Diagnostics.CodeAnalysis.UnscopedRef]")] + [InlineData("", "")] + public void SelfAssignment_ScopeVariance_UnscopedRef_Allowed_DelegateConversion(string attr1, string attr2) + { + var source = $$""" + D d = C.M; + + delegate void D({{attr1}} ref R r); + + static class C + { + public static void M({{attr2}} ref R r) { } + } + + ref struct R; + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics(); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/76100")] + public void SelfAssignment_ScopeVariance_ScopedRef_DelegateConversion( + [CombinatorialValues("scoped", "")] string scoped1, + [CombinatorialValues("scoped", "")] string scoped2, + [CombinatorialValues("ref", "out")] string modifier) + { + var source = $$""" + D d = C.M; + + delegate void D({{scoped1}} {{modifier}} R r); + + static class C + { + public static void M({{scoped2}} {{modifier}} R r) { } + } + + ref struct R; + """; + + var comp = CreateCompilation(source); + + if (scoped1 == "scoped" && scoped2 == "" && modifier == "ref") + { + comp.VerifyDiagnostics( + // (1,7): error CS8986: The 'scoped' modifier of parameter 'r' doesn't match target 'D'. + // D d = C.M; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "C.M").WithArguments("r", "D").WithLocation(1, 7)); + } + else + { + comp.VerifyDiagnostics(); + } + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 5e094ed174443..1f8a180901abe 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -16249,21 +16249,27 @@ static void Main() {{ D0 d0 = ({refModifier} int i) => {{ }}; D1 d1 = ({refModifier} int i) => F(); - D2 d2 = ({refModifier} int i) => ref F(); // 1 - D3 d3 = ({refModifier} int i) => ref F(); // 2 - D4 d4 = ({refModifier} int i) => new R(); // 3 + D2 d2 = ({refModifier} int i) => ref F(); + D3 d3 = ({refModifier} int i) => ref F(); + D4 d4 = ({refModifier} int i) => new R(); }} }}"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( + // (12,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D0'. + // D0 d0 = (ref int i) => { }; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i) => {{ }}").WithArguments("i", "D0").WithLocation(12, 22), + // (13,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D1'. + // D1 d1 = (ref int i) => F(); + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i) => F()").WithArguments("i", "D1").WithLocation(13, 22), // (14,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D2'. - // D2 d2 = (ref int i) => ref F(); // 1 + // D2 d2 = (ref int i) => ref F(); Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i) => ref F()").WithArguments("i", "D2").WithLocation(14, 22), // (15,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D3'. - // D3 d3 = (ref int i) => ref F(); // 2 + // D3 d3 = (ref int i) => ref F(); Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i) => ref F()").WithArguments("i", "D3").WithLocation(15, 22), // (16,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D4'. - // D4 d4 = (ref int i) => new R(); // 3 + // D4 d4 = (ref int i) => new R(); Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i) => new R()").WithArguments("i", "D4").WithLocation(16, 22)); } @@ -16284,18 +16290,24 @@ class Program static void Main() {{ D0 d0 = ({refModifier} int i, R r) => {{ }}; - D1 d1 = ({refModifier} int i, ref R r) => {{ }}; // 1 + D1 d1 = ({refModifier} int i, ref R r) => {{ }}; D2 d2 = ({refModifier} int i, in R r) => {{ }}; - D3 d3 = ({refModifier} int i, out R r) => {{ r = default; }}; // 2 + D3 d3 = ({refModifier} int i, out R r) => {{ r = default; }}; }} }}"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( + // (11,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D0'. + // D0 d0 = (ref int i, R r) => { }; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i, R r) => {{ }}").WithArguments("i", "D0").WithLocation(11, 22), // (12,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D1'. - // D1 d1 = (ref int i, ref R r) => { }; // 1 + // D1 d1 = (ref int i, ref R r) => { }; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i, ref R r) => {{ }}").WithArguments("i", "D1").WithLocation(12, 22), + // (13,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D2'. + // D2 d2 = (ref int i, in R r) => { }; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i, in R r) => {{ }}").WithArguments("i", "D2").WithLocation(13, 22), // (14,22): error CS8986: The 'scoped' modifier of parameter 'i' doesn't match target 'D3'. - // D3 d3 = (ref int i, out R r) => { r = default; }; // 2 + // D3 d3 = (ref int i, out R r) => { r = default; }; Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, $"({refModifier} int i, out R r) => {{ r = default; }}").WithArguments("i", "D3").WithLocation(14, 22)); } @@ -16765,7 +16777,10 @@ public static void M(object? o) comp.VerifyDiagnostics( // (3,17): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. // delegate R D(scoped T t); - Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T t").WithLocation(3, 17) + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped T t").WithLocation(3, 17), + // (14,27): error CS8986: The 'scoped' modifier of parameter 'o2' doesn't match target 'D'. + // D d = o2 => throw null!; + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfTarget, "o2 => throw null!").WithArguments("o2", "D").WithLocation(14, 27) ); var syntaxTree = comp.SyntaxTrees[0]; @@ -17388,7 +17403,10 @@ public class C : Base Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "get").WithArguments("rs").WithLocation(11, 38), // (12,45): error CS8987: The 'scoped' modifier of parameter 'rs' doesn't match overridden or implemented member. // public override RS this[RS rs, int _] { get => default; set { } } // 2 - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "get").WithArguments("rs").WithLocation(12, 45)); + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "get").WithArguments("rs").WithLocation(12, 45), + // (12,61): error CS8987: The 'scoped' modifier of parameter 'rs' doesn't match overridden or implemented member. + // public override RS this[RS rs, int _] { get => default; set { } } // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "set").WithArguments("rs").WithLocation(12, 61)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73384")] @@ -17417,7 +17435,10 @@ public class C : I Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "get").WithArguments("rs").WithLocation(11, 29), // (12,36): error CS8987: The 'scoped' modifier of parameter 'rs' doesn't match overridden or implemented member. // public RS this[RS rs, int _] { get => default; set { } } // 2 - Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "get").WithArguments("rs").WithLocation(12, 36)); + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "get").WithArguments("rs").WithLocation(12, 36), + // (12,52): error CS8987: The 'scoped' modifier of parameter 'rs' doesn't match overridden or implemented member. + // public RS this[RS rs, int _] { get => default; set { } } // 2 + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfOverrideOrImplementation, "set").WithArguments("rs").WithLocation(12, 52)); } [CombinatorialData]