diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 174cd7d4b74ea..de7070338d218 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -517,7 +517,7 @@ private bool CheckParameterValueKind(SyntaxNode node, BoundParameter parameter, ParameterSymbol parameterSymbol = parameter.ParameterSymbol; // all parameters can be passed by ref/out or assigned to - // except "in" parameters, which are readonly + // except "ref readonly" parameters, which are readonly if (parameterSymbol.RefKind == RefKind.RefReadOnly && RequiresAssignableVariable(valueKind)) { ReportReadOnlyError(parameterSymbol, node, valueKind, checkingReceiver, diagnostics); @@ -959,7 +959,7 @@ bool isRefEscape //by default it is safe to escape uint escapeScope = Binder.ExternalScope; - ArrayBuilder inParametersMatchedWithArgs = null; + ArrayBuilder refReadOnlyParametersMatchedWithArgs = null; if (!argsOpt.IsDefault) { @@ -983,7 +983,7 @@ bool isRefEscape goto moreArguments; } - RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs); + RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref refReadOnlyParametersMatchedWithArgs); // ref escape scope is the narrowest of // - ref escape of all byref arguments @@ -1001,7 +1001,7 @@ bool isRefEscape if (escapeScope >= scopeOfTheContainingExpression) { // no longer needed - inParametersMatchedWithArgs?.Free(); + refReadOnlyParametersMatchedWithArgs?.Free(); // can't get any worse return escapeScope; @@ -1009,12 +1009,12 @@ bool isRefEscape } } - // handle omitted optional "in" parameters if there are any - ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs); + // handle omitted optional "ref readonly" parameters if there are any + ParameterSymbol unmatchedRefReadOnlyParameter = TryGetUnmatchedRefReadOnlyParameterAndFreeMatchedArgs(parameters, ref refReadOnlyParametersMatchedWithArgs); - // unmatched "in" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse) + // unmatched "ref readonly" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse) // its val escape is ExternalScope (does not affect overal result) - if (unmatchedInParameter != null && isRefEscape) + if (unmatchedRefReadOnlyParameter != null && isRefEscape) { return scopeOfTheContainingExpression; } @@ -1063,7 +1063,7 @@ bool isRefEscape receiverOpt = null; } - ArrayBuilder inParametersMatchedWithArgs = null; + ArrayBuilder refReadOnlyParametersMatchedWithArgs = null; if (!argsOpt.IsDefault) { @@ -1088,7 +1088,7 @@ bool isRefEscape goto moreArguments; } - RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs); + RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref refReadOnlyParametersMatchedWithArgs); // ref escape scope is the narrowest of // - ref escape of all byref arguments @@ -1103,7 +1103,7 @@ bool isRefEscape if (!valid) { // no longer needed - inParametersMatchedWithArgs?.Free(); + refReadOnlyParametersMatchedWithArgs?.Free(); ErrorCode errorCode = GetStandardCallEscapeError(checkingReceiver); @@ -1124,14 +1124,14 @@ bool isRefEscape } } - // handle omitted optional "in" parameters if there are any - ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs); + // handle omitted optional "ref readonly" parameters if there are any + ParameterSymbol unmatchedRefReadOnlyParameter = TryGetUnmatchedRefReadOnlyParameterAndFreeMatchedArgs(parameters, ref refReadOnlyParametersMatchedWithArgs); - // unmatched "in" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse) + // unmatched "ref readonly" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse) // its val escape is ExternalScope (does not affect overal result) - if (unmatchedInParameter != null && isRefEscape) + if (unmatchedRefReadOnlyParameter != null && isRefEscape) { - Error(diagnostics, GetStandardCallEscapeError(checkingReceiver), syntax, symbol, unmatchedInParameter.Name); + Error(diagnostics, GetStandardCallEscapeError(checkingReceiver), syntax, symbol, unmatchedRefReadOnlyParameter.Name); return false; } @@ -1276,15 +1276,15 @@ private static bool CheckInvocationArgMixing( /// /// Gets "effective" ref kind of an argument. /// Generally we know if a formal argument is passed as ref/out by looking at the call site. - /// However, to distinguish "in" and regular "val" parameters we need to take a look at corresponding parameter, if such exists. - /// NOTE: there are cases like params/vararg, when a corresponding parameter may not exist, then it cannot be an "in". + /// However, to distinguish "ref readonly" and regular "val" parameters we need to take a look at corresponding parameter, if such exists. + /// NOTE: there are cases like params/vararg, when a corresponding parameter may not exist, then it cannot be a "ref readonly". /// private static RefKind GetEffectiveRefKind( int argIndex, ImmutableArray argRefKindsOpt, ImmutableArray parameters, ImmutableArray argsToParamsOpt, - ref ArrayBuilder inParametersMatchedWithArgs) + ref ArrayBuilder refReadOnlyParametersMatchedWithArgs) { var effectiveRefKind = argRefKindsOpt.IsDefault ? RefKind.None : argRefKindsOpt[argIndex]; if (effectiveRefKind == RefKind.None && argIndex < parameters.Length) @@ -1294,8 +1294,8 @@ private static RefKind GetEffectiveRefKind( if (parameters[paramIndex].RefKind == RefKind.RefReadOnly) { effectiveRefKind = RefKind.RefReadOnly; - inParametersMatchedWithArgs = inParametersMatchedWithArgs ?? ArrayBuilder.GetInstance(parameters.Length, fillWithValue: false); - inParametersMatchedWithArgs[paramIndex] = true; + refReadOnlyParametersMatchedWithArgs = refReadOnlyParametersMatchedWithArgs ?? ArrayBuilder.GetInstance(parameters.Length, fillWithValue: false); + refReadOnlyParametersMatchedWithArgs[paramIndex] = true; } } @@ -1303,11 +1303,11 @@ private static RefKind GetEffectiveRefKind( } /// - /// Gets an "in" parameter for which there is no argument supplied, if such exists. - /// That indicates an optional "in" parameter. We treat it as an RValue passed by reference via a temporary. + /// Gets a "ref readonly" parameter for which there is no argument supplied, if such exists. + /// That indicates an optional "ref readonly" parameter. We treat it as an RValue passed by reference via a temporary. /// The effective scope of such variable is the immediately containing scope. /// - private static ParameterSymbol TryGetUnmatchedInParameterAndFreeMatchedArgs(ImmutableArray parameters, ref ArrayBuilder inParametersMatchedWithArgs) + private static ParameterSymbol TryGetUnmatchedRefReadOnlyParameterAndFreeMatchedArgs(ImmutableArray parameters, ref ArrayBuilder refReadOnlyParametersMatchedWithArgs) { try { @@ -1322,7 +1322,7 @@ private static ParameterSymbol TryGetUnmatchedInParameterAndFreeMatchedArgs(Immu } if (parameter.RefKind == RefKind.RefReadOnly && - inParametersMatchedWithArgs?[i] != true && + refReadOnlyParametersMatchedWithArgs?[i] != true && parameter.Type.IsByRefLikeType == false) { return parameter; @@ -1334,9 +1334,9 @@ private static ParameterSymbol TryGetUnmatchedInParameterAndFreeMatchedArgs(Immu } finally { - inParametersMatchedWithArgs?.Free(); + refReadOnlyParametersMatchedWithArgs?.Free(); // make sure noone uses it after. - inParametersMatchedWithArgs = null; + refReadOnlyParametersMatchedWithArgs = null; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index a0ba5e2462970..6742c711fdd7c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -2577,7 +2577,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind if (paramRefKind == RefKind.RefReadOnly) { - // "in" parameters are effectively None for the purpose of overload resolution. + // "ref readonly" parameters are effectively None for the purpose of overload resolution. paramRefKind = RefKind.None; } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index 5b163542dbb61..e5d65c9153164 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -130,12 +130,12 @@ internal static bool IsLegalSpanStackAllocPosition(this SyntaxNode node) { Debug.Assert(node != null); - while (node.Parent.IsKind(SyntaxKind.CastExpression)) + if (node.Parent.IsKind(SyntaxKind.CastExpression)) { node = node.Parent; } - if (node.Parent.IsKind(SyntaxKind.ConditionalExpression)) + while (node.Parent.IsKind(SyntaxKind.ConditionalExpression)) { node = node.Parent; } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadOnlyParametersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadOnlyParametersTests.cs index 2ed43f5c115e9..c1033b9540c12 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadOnlyParametersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadOnlyParametersTests.cs @@ -1563,5 +1563,24 @@ .locals init (T V_0) }"); } + [Fact] + public void RefReadOnlyOptionalParameters() + { + CompileAndVerify(@" +using System; +class Program +{ + static void Print(ref readonly int p = 5) + { + Console.Write(p); + } + static void Main() + { + Print(); + Console.Write(""-""); + Print(9); + } +}", expectedOutput: "5-9"); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs index a0eb06901ae81..52a22b962ad36 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReadonlyReturnTests.cs @@ -1158,5 +1158,220 @@ .maxstack 1 IL_0001: throw }"); } + + [Fact] + public void RefExtensionMethod_PassThrough_LocalNoCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref int M(ref this int p) => ref p; +} +class Test +{ + void M() + { + int x = 5; + x.M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 11 (0xb) + .maxstack 1 + .locals init (int V_0) //x + IL_0000: ldc.i4.5 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""ref int Ext.M(ref int)"" + IL_0009: pop + IL_000a: ret +}"); + } + + [Fact] + public void RefExtensionMethod_PassThrough_FieldNoCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref int M(ref this int p) => ref p; +} +class Test +{ + private int x = 5; + void M() + { + x.M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldflda ""int Test.x"" + IL_0006: call ""ref int Ext.M(ref int)"" + IL_000b: pop + IL_000c: ret +}"); + } + + [Fact] + public void RefExtensionMethod_PassThrough_ChainNoCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref int M(ref this int p) => ref p; +} +class Test +{ + private int x = 5; + void M() + { + x.M().M().M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 23 (0x17) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldflda ""int Test.x"" + IL_0006: call ""ref int Ext.M(ref int)"" + IL_000b: call ""ref int Ext.M(ref int)"" + IL_0010: call ""ref int Ext.M(ref int)"" + IL_0015: pop + IL_0016: ret +}"); + } + + [Fact] + public void RefReadOnlyExtensionMethod_PassThrough_TempCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref readonly int M(ref readonly this int p) => ref p; +} +class Test +{ + void M() + { + 5.M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 11 (0xb) + .maxstack 1 + .locals init (int V_0) + IL_0000: ldc.i4.5 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""ref readonly int Ext.M(ref readonly int)"" + IL_0009: pop + IL_000a: ret +}"); + } + + [Fact] + public void RefReadOnlyExtensionMethod_PassThrough_LocalNoCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref readonly int M(ref readonly this int p) => ref p; +} +class Test +{ + void M() + { + int x = 5; + x.M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 11 (0xb) + .maxstack 1 + .locals init (int V_0) //x + IL_0000: ldc.i4.5 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call ""ref readonly int Ext.M(ref readonly int)"" + IL_0009: pop + IL_000a: ret +}"); + } + + [Fact] + public void RefReadOnlyExtensionMethod_PassThrough_FieldNoCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref readonly int M(ref readonly this int p) => ref p; +} +class Test +{ + private int x = 5; + void M() + { + x.M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 13 (0xd) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldflda ""int Test.x"" + IL_0006: call ""ref readonly int Ext.M(ref readonly int)"" + IL_000b: pop + IL_000c: ret +}"); + } + + [Fact] + public void RefReadOnlyExtensionMethod_PassThrough_ChainNoCopying() + { + CompileAndVerify(@" +public static class Ext +{ + public static ref readonly int M(ref readonly this int p) => ref p; +} +class Test +{ + private int x = 5; + void M() + { + x.M().M().M(); + } +}", additionalRefs: new[] { SystemCoreRef }, verify: false).VerifyIL("Test.M", @" +{ + // Code size 23 (0x17) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ldflda ""int Test.x"" + IL_0006: call ""ref readonly int Ext.M(ref readonly int)"" + IL_000b: call ""ref readonly int Ext.M(ref readonly int)"" + IL_0010: call ""ref readonly int Ext.M(ref readonly int)"" + IL_0015: pop + IL_0016: ret +}"); + } + + [Fact] + public void RefReadOnlyReturnOptionalValue() + { + CompileAndVerify(@" +class Program +{ + static ref readonly string M(ref readonly string s = ""optional"") => ref s; + + static void Main() + { + System.Console.Write(M()); + System.Console.Write(""-""); + System.Console.Write(M(""provided"")); + } +}", verify: false, expectedOutput: "optional-provided"); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 203360f3361c4..7a64be353844d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -1326,5 +1326,22 @@ public static void Main(string[] args) var compilation = CreateCompilationWithMscorlib45(source, null, new CSharpCompilationOptions(OutputKind.ConsoleApplication).WithAllowUnsafe(true)); CompileAndVerify(compilation, expectedOutput: "normalField fixedField").VerifyDiagnostics(); } + + [Fact] + public void PassingNameOfToRefReadOnlyShouldCopy() + { + CompileAndVerify(@" +class Program +{ + public static void Main() + { + M(nameof(Main)); + } + private static void M(ref readonly string value) + { + System.Console.WriteLine(value); + } +}", expectedOutput: "Main"); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index fb3573e2dd8fd..bb2273b1b1af9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -2696,5 +2696,77 @@ async Task TestMethod() Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "await Task.FromResult(1)").WithArguments("TestClass.this[int, int].get").WithLocation(36, 22) ); } + + [Fact] + public void RefReadOnlyInAsyncMethodDisallowed() + { + CreateCompilationWithMscorlib45(@" +using System.Threading.Tasks; +class Test +{ + async Task Method(ref readonly int p) + { + await Task.FromResult(0); + } +}").VerifyDiagnostics( + // (5,40): error CS1988: Async methods cannot have ref or out parameters + // async Task Method(ref readonly int p) + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "p").WithLocation(5, 40)); + } + + [Fact] + public void RefReadOnlyInIteratorMethodsDisallowed() + { + CreateCompilationWithMscorlib45(@" +using System.Collections.Generic; +class Test +{ + IEnumerable Method(ref readonly int p) + { + yield return 0; + yield return 1; + yield return 2; + } +}").VerifyDiagnostics( + // (5,46): error CS1623: Iterators cannot have ref or out parameters + // IEnumerable Method(ref readonly int p) + Diagnostic(ErrorCode.ERR_BadIteratorArgType, "p").WithLocation(5, 46)); + } + + [Fact] + public void RefReadOnlyInEnumeratorMethodsDisallowed() + { + CreateStandardCompilation(@" +using System.Collections.Generic; +class Test +{ + public IEnumerator GetEnumerator(ref readonly int p) + { + yield return 0; + } +}").VerifyDiagnostics( + // (5,60): error CS1623: Iterators cannot have ref or out parameters + // public IEnumerator GetEnumerator(ref readonly int p) + Diagnostic(ErrorCode.ERR_BadIteratorArgType, "p").WithLocation(5, 60)); + } + + [Fact] + public void CannotCallRefReadOnlyMethodsUsingDiscardParameter() + { + CreateStandardCompilation(@" +class Test +{ + void M(ref readonly int p) + { + } + void N() + { + M(_); + } +}").VerifyDiagnostics( + // (9,11): error CS0103: The name '_' does not exist in the current context + // M(_); + Diagnostic(ErrorCode.ERR_NameNotInContext, "_").WithArguments("_").WithLocation(9, 11)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index e9c95622e828e..72562a7948b79 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -8536,41 +8536,51 @@ unsafe public static void Main() } [Fact] - public void CS0255ERR_StackallocInCatchFinally() + public void CS0255ERR_StackallocInFinally() { var text = @" -using System; - -public class TestTryFinally +unsafe class Test { - public static unsafe void Test() - { - int i = 123; - string s = ""Some string""; - object o = s; - - try - { - // Conversion is not valid; o contains a string not an int - i = (int) o; - } - - finally - { - Console.Write(""i = {0}"", i); - int* fib = stackalloc int[100]; // CS0255 - } - } + void M() + { + try + { + // Something + } + finally + { + int* fib = stackalloc int[100]; + } + } +}"; + CreateStandardCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (12,24): error CS0255: stackalloc may not be used in a catch or finally block + // int* fib = stackalloc int[100]; + Diagnostic(ErrorCode.ERR_StackallocInCatchFinally, "stackalloc int[100]").WithLocation(12, 24)); + } - public static void Main() - { - } -} -"; + [Fact] + public void CS0255ERR_StackallocInCatch() + { + var text = @" +unsafe class Test +{ + void M() + { + try + { + // Something + } + catch + { + int* fib = stackalloc int[100]; + } + } +}"; CreateStandardCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (21,21): error CS0255: stackalloc may not be used in a catch or finally block - // int* fib = stackalloc int[100]; // CS0255 - Diagnostic(ErrorCode.ERR_StackallocInCatchFinally, "stackalloc int[100]")); + // (12,24): error CS0255: stackalloc may not be used in a catch or finally block + // int* fib = stackalloc int[100]; + Diagnostic(ErrorCode.ERR_StackallocInCatchFinally, "stackalloc int[100]").WithLocation(12, 24)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs index 5f55c9f2bec66..15e546e292181 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocSpanExpressionsTests.cs @@ -303,6 +303,29 @@ void M() Diagnostic(ErrorCode.ERR_InvalidQM, "true ? stackalloc int [10] : a").WithArguments("stackalloc int[10]", "System.Span").WithLocation(8, 17)); } + [Fact] + public void ConditionalExpressionOnSpan_Nested() + { + CreateCompilationWithMscorlibAndSpan(@" +class Test +{ + bool N() => true; + + void M() + { + var x = N() + ? N() + ? stackalloc int [1] + : stackalloc int [2] + : N() + ? stackalloc int[3] + : N() + ? stackalloc int[4] + : stackalloc int[5]; + } +}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + [Fact] public void BooleanOperatorOnSpan_NoTargetTyping() { @@ -342,6 +365,22 @@ void M() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "stackalloc int[10]").WithArguments("ref structs", "7.2").WithLocation(7, 23)); } + [Fact] + public void StackAllocSyntaxProducesUnsafeErrorInSafeCode() + { + CreateStandardCompilation(@" +class Test +{ + void M() + { + var x = stackalloc int[10]; + } +}", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (6,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var x = stackalloc int[10]; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "stackalloc int[10]").WithLocation(6, 17)); + } + [Fact] public void StackAllocInUsing1() { @@ -508,5 +547,110 @@ static void Main() Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(7, 16) ); } + + [Fact] + public void StackAllocWithDynamic() + { + CreateStandardCompilation(@" +class Program +{ + static void Main() + { + var d = stackalloc dynamic[10]; + } +}").VerifyDiagnostics( + // (6,33): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('dynamic') + // var d = stackalloc dynamic[10]; + Diagnostic(ErrorCode.ERR_ManagedAddr, "dynamic").WithArguments("dynamic").WithLocation(6, 28)); + } + + [Fact] + public void StackAllocWithDynamicSpan() + { + CreateCompilationWithMscorlibAndSpan(@" +using System; +class Program +{ + static void Main() + { + Span d = stackalloc dynamic[10]; + } +}").VerifyDiagnostics( + // (7,38): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('dynamic') + // Span d = stackalloc dynamic[10]; + Diagnostic(ErrorCode.ERR_ManagedAddr, "dynamic").WithArguments("dynamic").WithLocation(7, 38)); + } + + [Fact] + public void StackAllocAsArgument() + { + CreateStandardCompilation(@" +class Program +{ + static void N(object p) { } + + static void Main() + { + N(stackalloc int[10]); + } +}").VerifyDiagnostics( + // (8,11): error CS1525: Invalid expression term 'stackalloc' + // N(stackalloc int[10]); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(8, 11)); + } + + [Fact] + public void StackAllocInParenthesis() + { + CreateStandardCompilation(@" +class Program +{ + static void Main() + { + var x = (stackalloc int[10]); + } +}").VerifyDiagnostics( + // (6,18): error CS1525: Invalid expression term 'stackalloc' + // var x = (stackalloc int[10]); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 18)); + } + + [Fact] + public void StackAllocInNullConditionalOperator() + { + CreateStandardCompilation(@" +class Program +{ + static void Main() + { + var x = stackalloc int[1] ?? stackalloc int[2]; + } +}").VerifyDiagnostics( + // (6,17): error CS1525: Invalid expression term 'stackalloc' + // var x = stackalloc int[1] ?? stackalloc int[2]; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 17), + // (6,38): error CS1525: Invalid expression term 'stackalloc' + // var x = stackalloc int[1] ?? stackalloc int[2]; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "stackalloc").WithArguments("stackalloc").WithLocation(6, 38)); + } + + [Fact] + public void StackAllocInCastAndConditionalOperator() + { + CreateCompilationWithMscorlibAndSpan(@" +using System; +class Test +{ + public void Method() + { + Test value = true ? new Test() : (Test)stackalloc int[10]; + } + + public static explicit operator Test(Span value) + { + return new Test(); + } +}", TestOptions.ReleaseDll).VerifyDiagnostics(); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 559c9c11a431a..7c0dadb31601b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -8561,6 +8561,22 @@ static unsafe void Main() CompileAndVerify(text, options: TestOptions.UnsafeReleaseExe, expectedOutput: @"4812"); } + [Fact] + public void CannotTakeAddressOfRefReadOnlyParameter() + { + CreateStandardCompilation(@" +public class Test +{ + unsafe void M(ref readonly int p) + { + int* pointer = &p; + } +}", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (6,25): error CS0211: Cannot take the address of the given expression + // int* pointer = &p; + Diagnostic(ErrorCode.ERR_InvalidAddrOp, "p").WithLocation(6, 25)); + } + #endregion } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs index 16db7be15c2a0..5793ce918b456 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs @@ -627,5 +627,31 @@ interface I : // interface I : Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T").WithArguments("I", "T", "covariant", "contravariantly").WithLocation(14, 17)); } + + [Fact] + public void CovarianceBoundariesForRefReadOnly_Parameters() + { + CreateStandardCompilation(@" +interface ITest +{ + void M(ref readonly T p); +}").VerifyDiagnostics( + // (4,25): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'ITest.M(ref readonly T)'. 'T' is contravariant. + // void M(ref readonly T p); + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "T").WithArguments("ITest.M(ref readonly T)", "T", "contravariant", "invariantly").WithLocation(4, 25)); + } + + [Fact] + public void CovarianceBoundariesForRefReadOnly_ReturnType() + { + CreateStandardCompilation(@" +interface ITest +{ + ref readonly T M(); +}").VerifyDiagnostics( + // (4,5): error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'ITest.M()'. 'T' is contravariant. + // ref readonly T M(); + Diagnostic(ErrorCode.ERR_UnexpectedVariance, "ref readonly T").WithArguments("ITest.M()", "T", "contravariant", "invariantly").WithLocation(4, 5)); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyReturnsTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyReturnsTests.cs deleted file mode 100644 index b33ce2f9a2b39..0000000000000 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyReturnsTests.cs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Xunit; -using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; -using Xunit.Abstractions; - -namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Parsing -{ - [CompilerTrait(CompilerFeature.ReadOnlyReferences)] - public class RefReadonlyReturnsTests : ParsingTests - { - public RefReadonlyReturnsTests(ITestOutputHelper output) : base(output) { } - - protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) - { - return SyntaxFactory.ParseSyntaxTree(text, options: options); - } - - [Fact] - public void RefReadonlyReturn_CSharp7() - { - var text = @" -unsafe class Program -{ - delegate ref readonly int D1(); - - static ref readonly T M() - { - return ref (new T[1])[0]; - } - - public virtual ref readonly int* P1 => throw null; - - public ref readonly int[][] this[int i] => throw null; -} -"; - - var comp = CreateCompilationWithMscorlib45(text, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1), options: TestOptions.UnsafeDebugDll); - comp.VerifyDiagnostics( - // (4,18): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. - // delegate ref readonly int D1(); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(4, 18), - // (6,16): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. - // static ref readonly T M() - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(6, 16), - // (11,24): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. - // public virtual ref readonly int* P1 => throw null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(11, 24), - // (13,16): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. - // public ref readonly int[][] this[int i] => throw null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(13, 16) - ); - } - - [Fact] - public void RefReadonlyReturn_Unexpected() - { - var text = @" - -class Program -{ - static void Main() - { - } - - ref readonly int Field; - - public static ref readonly Program operator +(Program x, Program y) - { - throw null; - } - - // this parses fine - static async ref readonly Task M() - { - throw null; - } - - public ref readonly virtual int* P1 => throw null; - -} -"; - - ParseAndValidate(text, TestOptions.Regular, - // (9,27): error CS1003: Syntax error, '(' expected - // ref readonly int Field; - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("(", ";").WithLocation(9, 27), - // (9,27): error CS1026: ) expected - // ref readonly int Field; - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(9, 27), - // (11,41): error CS1519: Invalid token 'operator' in class, struct, or interface member declaration - // public static ref readonly Program operator +(Program x, Program y) - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(11, 41), - // (11,41): error CS1519: Invalid token 'operator' in class, struct, or interface member declaration - // public static ref readonly Program operator +(Program x, Program y) - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(11, 41), - // (12,5): error CS1519: Invalid token '{' in class, struct, or interface member declaration - // { - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(12, 5), - // (12,5): error CS1519: Invalid token '{' in class, struct, or interface member declaration - // { - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(12, 5), - // (22,25): error CS1031: Type expected - // public ref readonly virtual int* P1 => throw null; - Diagnostic(ErrorCode.ERR_TypeExpected, "virtual").WithLocation(22, 25), - // (24,1): error CS1022: Type or namespace definition, or end-of-file expected - // } - Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(24, 1)); - } - - [Fact] - public void RefReadonlyReturn_UnexpectedBindTime() - { - var text = @" - -class Program -{ - static void Main() - { - ref readonly int local = ref (new int[1])[0]; - - (ref readonly int, ref readonly int Alice)? t = null; - - System.Collections.Generic.List x = null; - - Use(local); - Use(t); - Use(x); - } - - static void Use(T dummy) - { - } -} -"; - var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); - comp.VerifyDiagnostics( - // (9,10): error CS1073: Unexpected token 'ref' - // (ref readonly int, ref readonly int Alice)? t = null; - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 10), - // (9,28): error CS1073: Unexpected token 'ref' - // (ref readonly int, ref readonly int Alice)? t = null; - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 28), - // (11,41): error CS1073: Unexpected token 'ref' - // System.Collections.Generic.List x = null; - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(11, 41) - ); - } - } -} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs new file mode 100644 index 0000000000000..72ec9b178a76b --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs @@ -0,0 +1,360 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Xunit; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Parsing +{ + [CompilerTrait(CompilerFeature.ReadOnlyReferences)] + public class RefReadonlyTests : ParsingTests + { + public RefReadonlyTests(ITestOutputHelper output) : base(output) { } + + protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) + { + return SyntaxFactory.ParseSyntaxTree(text, options: options); + } + + [Fact] + public void RefReadonlyReturn_CSharp7() + { + var text = @" +unsafe class Program +{ + delegate ref readonly int D1(); + + static ref readonly T M() + { + return ref (new T[1])[0]; + } + + public virtual ref readonly int* P1 => throw null; + + public ref readonly int[][] this[int i] => throw null; +} +"; + + var comp = CreateCompilationWithMscorlib45(text, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1), options: TestOptions.UnsafeDebugDll); + comp.VerifyDiagnostics( + // (4,18): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. + // delegate ref readonly int D1(); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(4, 18), + // (6,16): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. + // static ref readonly T M() + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(6, 16), + // (11,24): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. + // public virtual ref readonly int* P1 => throw null; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(11, 24), + // (13,16): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater. + // public ref readonly int[][] this[int i] => throw null; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(13, 16) + ); + } + + [Fact] + public void RefReadonlyReturn_Unexpected() + { + var text = @" + +class Program +{ + static void Main() + { + } + + ref readonly int Field; + + public static ref readonly Program operator +(Program x, Program y) + { + throw null; + } + + // this parses fine + static async ref readonly Task M() + { + throw null; + } + + public ref readonly virtual int* P1 => throw null; + +} +"; + + ParseAndValidate(text, TestOptions.Regular, + // (9,27): error CS1003: Syntax error, '(' expected + // ref readonly int Field; + Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments("(", ";").WithLocation(9, 27), + // (9,27): error CS1026: ) expected + // ref readonly int Field; + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(9, 27), + // (11,41): error CS1519: Invalid token 'operator' in class, struct, or interface member declaration + // public static ref readonly Program operator +(Program x, Program y) + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(11, 41), + // (11,41): error CS1519: Invalid token 'operator' in class, struct, or interface member declaration + // public static ref readonly Program operator +(Program x, Program y) + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(11, 41), + // (12,5): error CS1519: Invalid token '{' in class, struct, or interface member declaration + // { + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(12, 5), + // (12,5): error CS1519: Invalid token '{' in class, struct, or interface member declaration + // { + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(12, 5), + // (22,25): error CS1031: Type expected + // public ref readonly virtual int* P1 => throw null; + Diagnostic(ErrorCode.ERR_TypeExpected, "virtual").WithLocation(22, 25), + // (24,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(24, 1)); + } + + [Fact] + public void RefReadonlyReturn_UnexpectedBindTime() + { + var text = @" + +class Program +{ + static void Main() + { + ref readonly int local = ref (new int[1])[0]; + + (ref readonly int, ref readonly int Alice)? t = null; + + System.Collections.Generic.List x = null; + + Use(local); + Use(t); + Use(x); + } + + static void Use(T dummy) + { + } +} +"; + var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + comp.VerifyDiagnostics( + // (9,10): error CS1073: Unexpected token 'ref' + // (ref readonly int, ref readonly int Alice)? t = null; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 10), + // (9,28): error CS1073: Unexpected token 'ref' + // (ref readonly int, ref readonly int Alice)? t = null; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 28), + // (11,41): error CS1073: Unexpected token 'ref' + // System.Collections.Generic.List x = null; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(11, 41)); + } + + [Fact] + public void RefReadOnlyLocalsAreDisallowed() + { + CreateStandardCompilation(@" +class Test +{ + void M() + { + int value = 0; + ref int valid = ref value; + ref readonly int invalid = ref readonly value; + } +}").GetParseDiagnostics().Verify( + // (8,40): error CS1525: Invalid expression term 'readonly' + // ref readonly int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(8, 40), + // (8,40): error CS1002: ; expected + // ref readonly int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(8, 40), + // (8,40): error CS0106: The modifier 'readonly' is not valid for this item + // ref readonly int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(8, 40), + // (8,54): error CS1001: Identifier expected + // ref readonly int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(8, 54)); + } + + [Fact] + public void LocalsWithRefReadOnlyExpressionsAreDisallowed() + { + CreateStandardCompilation(@" +class Test +{ + void M() + { + int value = 0; + ref int valid = ref value; + ref int invalid = ref readonly value; + } +}").GetParseDiagnostics().Verify( + // (8,31): error CS1525: Invalid expression term 'readonly' + // ref int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(8, 31), + // (8,31): error CS1002: ; expected + // ref int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(8, 31), + // (8,31): error CS0106: The modifier 'readonly' is not valid for this item + // ref int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(8, 31), + // (8,45): error CS1001: Identifier expected + // ref int invalid = ref readonly value; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(8, 45)); + } + + [Fact] + public void ReturnRefReadOnlyAreDisallowed() + { + CreateStandardCompilation(@" +class Test +{ + int value = 0; + + ref readonly int Valid() => ref value; + ref readonly int Invalid() => ref readonly value; + +}").GetParseDiagnostics().Verify( + // (7,39): error CS1525: Invalid expression term 'readonly' + // ref readonly int Invalid() => ref readonly value; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(7, 39), + // (7,39): error CS1002: ; expected + // ref readonly int Invalid() => ref readonly value; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(7, 39), + // (7,53): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // ref readonly int Invalid() => ref readonly value; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(7, 53), + // (7,53): error CS1519: Invalid token ';' in class, struct, or interface member declaration + // ref readonly int Invalid() => ref readonly value; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(7, 53)); + } + + [Fact] + public void RefReadOnlyForEachAreDisallowed() + { + CreateStandardCompilation(@" +class Test +{ + void M() + { + var ar = new int[] { 1, 2, 3 }; + + foreach(ref readonly v in ar) + { + } + } +}").GetParseDiagnostics().Verify( + // (8,17): error CS1525: Invalid expression term 'ref' + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref").WithArguments("ref").WithLocation(8, 17), + // (8,17): error CS1515: 'in' expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_InExpected, "ref").WithLocation(8, 17), + // (8,17): error CS0230: Type and identifier are both required in a foreach statement + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_BadForeachDecl, "ref").WithLocation(8, 17), + // (8,17): error CS1525: Invalid expression term 'ref' + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "ref").WithArguments("ref").WithLocation(8, 17), + // (8,17): error CS1026: ) expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "ref").WithLocation(8, 17), + // (8,32): error CS1001: Identifier expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_IdentifierExpected, "in").WithLocation(8, 32), + // (8,32): error CS1003: Syntax error, ',' expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_SyntaxError, "in").WithArguments(",", "in").WithLocation(8, 32), + // (8,35): error CS1002: ; expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "ar").WithLocation(8, 35), + // (8,37): error CS1002: ; expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(8, 37), + // (8,37): error CS1513: } expected + // foreach(ref readonly v in ar) + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(8, 37)); + } + + [Fact] + public void RefReadOnlyAtCallSite() + { + CreateStandardCompilation(@" +class Test +{ + void M(ref readonly int p) + { + } + void N() + { + int x = 0; + M(ref readonly x); + } +}").GetParseDiagnostics().Verify( + // (10,15): error CS1525: Invalid expression term 'readonly' + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(10, 15), + // (10,15): error CS1026: ) expected + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(10, 15), + // (10,15): error CS1002: ; expected + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(10, 15), + // (10,15): error CS0106: The modifier 'readonly' is not valid for this item + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(10, 15), + // (10,25): error CS1001: Identifier expected + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(10, 25), + // (10,25): error CS1002: ; expected + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(10, 25), + // (10,25): error CS1513: } expected + // M(ref readonly x); + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(10, 25)); + } + + [Fact] + public void InverseReadOnlyRefShouldBeIllegal() + { + CreateStandardCompilation(@" +class Test +{ + void M(readonly ref int p) + { + } +}").GetParseDiagnostics().Verify( + // (4,12): error CS1026: ) expected + // void M(readonly ref int p) + Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(4, 12), + // (4,12): error CS1002: ; expected + // void M(readonly ref int p) + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(4, 12), + // (4,30): error CS1003: Syntax error, '(' expected + // void M(readonly ref int p) + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("(", ")").WithLocation(4, 30)); + } + + [Fact] + public void RefReadOnlyReturnIllegalInOperators() + { + CreateStandardCompilation(@" +public class Test +{ + public static ref readonly bool operator!(Test obj) => throw null; +}").GetParseDiagnostics().Verify( + // (4,37): error CS1519: Invalid token 'operator' in class, struct, or interface member declaration + // public static ref readonly bool operator!(Test obj) => throw null; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(4, 37), + // (4,37): error CS1519: Invalid token 'operator' in class, struct, or interface member declaration + // public static ref readonly bool operator!(Test obj) => throw null; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "operator").WithArguments("operator").WithLocation(4, 37), + // (4,55): error CS8124: Tuple must contain at least two elements. + // public static ref readonly bool operator!(Test obj) => throw null; + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(4, 55), + // (4,57): error CS1519: Invalid token '=>' in class, struct, or interface member declaration + // public static ref readonly bool operator!(Test obj) => throw null; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=>").WithArguments("=>").WithLocation(4, 57)); + } + } +}