提交 fef4159f 编写于 作者: A Andy Gocke 提交者: GitHub

Support for ref-readonly locals (#22269)

Ref-readonly locals are mostly identical to ref-readonly parameters. The
most important difference is that when possibly mutating methods are
called on ref-readonly locals of struct type, a proper temporary is
created before calling the method.
上级 917edc18
......@@ -773,7 +773,10 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost
}
else
{
valueKind = BindValueKind.RefOrOut;
valueKind = variableRefKind == RefKind.RefReadOnly
? BindValueKind.ReadonlyRef
: BindValueKind.RefOrOut;
if (initializer == null)
{
Error(diagnostics, ErrorCode.ERR_ByReferenceVariableMustBeInitialized, node);
......@@ -836,19 +839,10 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost
// might own nested scope.
bool hasErrors = localSymbol.ScopeBinder.ValidateDeclarationNameConflictsInScope(localSymbol, diagnostics);
if (localSymbol.RefKind == RefKind.RefReadOnly)
{
Debug.Assert(typeSyntax.Parent is RefTypeSyntax);
var refKeyword = typeSyntax.Parent.GetFirstToken();
diagnostics.Add(ErrorCode.ERR_UnexpectedToken, refKeyword.GetLocation(), refKeyword.ToString());
}
else
var containingMethod = this.ContainingMemberOrLambda as MethodSymbol;
if (containingMethod != null && containingMethod.IsAsync && localSymbol.RefKind != RefKind.None)
{
var containingMethod = this.ContainingMemberOrLambda as MethodSymbol;
if (containingMethod != null && containingMethod.IsAsync && localSymbol.RefKind != RefKind.None)
{
Error(diagnostics, ErrorCode.ERR_BadAsyncLocalType, declarator);
}
Error(diagnostics, ErrorCode.ERR_BadAsyncLocalType, declarator);
}
EqualsValueClauseSyntax equalsClauseSyntax = declarator.Initializer;
......
......@@ -40,13 +40,11 @@ private LocalDefinition EmitAddress(BoundExpression expression, AddressKind addr
break;
case BoundKind.Local:
EmitLocalAddress((BoundLocal)expression);
break;
return EmitLocalAddress((BoundLocal)expression, addressKind);
case BoundKind.Dup:
Debug.Assert(((BoundDup)expression).RefKind != RefKind.None, "taking address of a stack value?");
_builder.EmitOpCode(ILOpCode.Dup);
break;
return EmitDupAddress((BoundDup)expression, addressKind);
case BoundKind.ConditionalReceiver:
// do nothing receiver ref must be already pushed
......@@ -213,10 +211,18 @@ private void EmitComplexConditionalReceiverAddress(BoundComplexConditionalReceiv
_builder.MarkLabel(doneLabel);
}
private void EmitLocalAddress(BoundLocal localAccess)
/// <summary>
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private LocalDefinition EmitLocalAddress(BoundLocal localAccess, AddressKind addressKind)
{
var local = localAccess.LocalSymbol;
if (!HasHome(localAccess, needWriteable: addressKind != AddressKind.ReadOnly))
{
return EmitAddressOfTempClone(localAccess);
}
if (IsStackLocal(local))
{
if (local.RefKind != RefKind.None)
......@@ -234,6 +240,22 @@ private void EmitLocalAddress(BoundLocal localAccess)
{
_builder.EmitLocalAddress(GetLocal(localAccess));
}
return null;
}
/// <summary>
/// May introduce a temp which it will return. (otherwise returns null)
/// </summary>
private LocalDefinition EmitDupAddress(BoundDup dup, AddressKind addressKind)
{
if (!HasHome(dup, needWriteable: addressKind != AddressKind.ReadOnly))
{
return EmitAddressOfTempClone(dup);
}
_builder.EmitOpCode(ILOpCode.Dup);
return null;
}
private void EmitPseudoVariableAddress(BoundPseudoVariable expression)
......@@ -345,9 +367,11 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
((BoundParameter)expression).ParameterSymbol.RefKind != RefKind.RefReadOnly;
case BoundKind.Local:
// locals have home unless they are byval stack locals
// locals have home unless they are byval stack locals or ref-readonly
// locals in a mutating call
var local = ((BoundLocal)expression).LocalSymbol;
return !IsStackLocal(local) || local.RefKind != RefKind.None;
return !((IsStackLocal(local) && local.RefKind == RefKind.None) ||
(needWriteable && local.RefKind == RefKind.RefReadOnly));
case BoundKind.Call:
var methodRefKind = ((BoundCall)expression).Method.RefKind;
......@@ -356,9 +380,9 @@ private bool HasHome(BoundExpression expression, bool needWriteable)
case BoundKind.Dup:
//NB: Dup represents locals that do not need IL slot
// ref locals are currently always writeable, so we do not need to care about "needWriteable"
Debug.Assert(((BoundDup)expression).RefKind != RefKind.RefReadOnly);
return ((BoundDup)expression).RefKind != RefKind.None;
var dupRefKind = ((BoundDup)expression).RefKind;
return dupRefKind == RefKind.Ref ||
(!needWriteable && dupRefKind == RefKind.RefReadOnly);
case BoundKind.FieldAccess:
return HasHome((BoundFieldAccess)expression, needWriteable);
......@@ -540,7 +564,7 @@ private LocalDefinition EmitParameterAddress(BoundParameter parameter, AddressKi
{
ParameterSymbol parameterSymbol = parameter.ParameterSymbol;
if (!HasHome(parameter, addressKind != AddressKind.ReadOnly))
if (!HasHome(parameter, needWriteable: addressKind != AddressKind.ReadOnly))
{
// accessing a parameter that is not writable
return EmitAddressOfTempClone(parameter);
......
......@@ -971,7 +971,9 @@ private static bool IsIndirectAssignment(BoundAssignmentOperator node)
{
var lhs = node.Left;
Debug.Assert(node.RefKind == RefKind.None || (lhs as BoundLocal)?.LocalSymbol.RefKind == RefKind.Ref,
Debug.Assert(node.RefKind == RefKind.None || lhs is BoundLocal local &&
(local.LocalSymbol.RefKind == node.RefKind ||
local.LocalSymbol.RefKind == RefKind.RefReadOnly),
"only ref locals can be a target of a ref assignment");
switch (lhs.Kind)
......@@ -1034,6 +1036,7 @@ private static bool IsIndirectAssignment(BoundAssignmentOperator node)
throw ExceptionUtilities.UnexpectedValue(lhs.Kind);
}
}
private static bool IsIndirectOrInstanceFieldAssignment(BoundAssignmentOperator node)
{
var lhs = node.Left;
......
......@@ -268,7 +268,7 @@ internal virtual bool IsWritable
case LocalDeclarationKind.UsingVariable:
return false;
default:
return true;
return RefKind != RefKind.RefReadOnly;
}
}
}
......
// Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
......@@ -15,6 +10,377 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
[CompilerTrait(CompilerFeature.ReadOnlyReferences)]
public class CodeGenRefReadOnlyReturnTests : CompilingTestBase
{
[Fact]
public void RefReadonlyLocalToField()
{
var comp = CompileAndVerify(@"
struct S
{
public int X;
public S(int x) => X = x;
public void AddOne() => this.X++;
}
readonly struct S2
{
public readonly int X;
public S2(int x) => X = x;
public void AddOne() { }
}
class C
{
static S s1 = new S(0);
readonly static S s2 = new S(0);
static S2 s3 = new S2(0);
readonly S2 s4 = new S2(0);
ref readonly S M()
{
ref readonly S rs1 = ref s1;
rs1.AddOne();
ref readonly S rs2 = ref s2;
rs2.AddOne();
ref readonly S2 rs3 = ref s3;
rs3.AddOne();
ref readonly S2 rs4 = ref s4;
rs4.AddOne();
return ref rs1;
}
}");
comp.VerifyIL("C.M", @"
{
// Code size 65 (0x41)
.maxstack 2
.locals init (S V_0,
S V_1,
S2 V_2)
IL_0000: ldsflda ""S C.s1""
IL_0005: dup
IL_0006: ldobj ""S""
IL_000b: stloc.0
IL_000c: ldloca.s V_0
IL_000e: call ""void S.AddOne()""
IL_0013: ldsfld ""S C.s2""
IL_0018: stloc.0
IL_0019: ldloca.s V_0
IL_001b: ldobj ""S""
IL_0020: stloc.1
IL_0021: ldloca.s V_1
IL_0023: call ""void S.AddOne()""
IL_0028: ldsflda ""S2 C.s3""
IL_002d: call ""void S2.AddOne()""
IL_0032: ldarg.0
IL_0033: ldfld ""S2 C.s4""
IL_0038: stloc.2
IL_0039: ldloca.s V_2
IL_003b: call ""void S2.AddOne()""
IL_0040: ret
}");
}
[Fact]
public void CallsOnRefReadonlyCopyReceiver()
{
var comp = CompileAndVerify(@"
using System;
struct S
{
public int X;
public S(int x) => X = x;
public void AddOne() => this.X++;
}
class C
{
public static void Main()
{
S s = new S(0);
ref readonly S rs = ref s;
Console.WriteLine(rs.X);
rs.AddOne();
Console.WriteLine(rs.X);
rs.AddOne();
rs.AddOne();
rs.AddOne();
}
}", expectedOutput: @"0
0");
comp.VerifyIL("C.Main", @"
{
// Code size 88 (0x58)
.maxstack 2
.locals init (S V_0, //s
S V_1)
IL_0000: ldloca.s V_0
IL_0002: ldc.i4.0
IL_0003: call ""S..ctor(int)""
IL_0008: ldloca.s V_0
IL_000a: dup
IL_000b: ldfld ""int S.X""
IL_0010: call ""void System.Console.WriteLine(int)""
IL_0015: dup
IL_0016: ldobj ""S""
IL_001b: stloc.1
IL_001c: ldloca.s V_1
IL_001e: call ""void S.AddOne()""
IL_0023: dup
IL_0024: ldfld ""int S.X""
IL_0029: call ""void System.Console.WriteLine(int)""
IL_002e: dup
IL_002f: ldobj ""S""
IL_0034: stloc.1
IL_0035: ldloca.s V_1
IL_0037: call ""void S.AddOne()""
IL_003c: dup
IL_003d: ldobj ""S""
IL_0042: stloc.1
IL_0043: ldloca.s V_1
IL_0045: call ""void S.AddOne()""
IL_004a: ldobj ""S""
IL_004f: stloc.1
IL_0050: ldloca.s V_1
IL_0052: call ""void S.AddOne()""
IL_0057: ret
}");
// This should generate similar IL to the previous
comp = CompileAndVerify(@"
using System;
struct S
{
public int X;
public S(int x) => X = x;
public void AddOne() => this.X++;
}
class C
{
public static void Main()
{
S s = new S(0);
ref S sr = ref s;
var temp = sr;
temp.AddOne();
Console.WriteLine(temp.X);
temp = sr;
temp.AddOne();
Console.WriteLine(temp.X);
}
}", expectedOutput: @"1
1");
comp.VerifyIL("C.Main", @"
{
// Code size 60 (0x3c)
.maxstack 2
.locals init (S V_0, //s
S V_1) //temp
IL_0000: ldloca.s V_0
IL_0002: ldc.i4.0
IL_0003: call ""S..ctor(int)""
IL_0008: ldloca.s V_0
IL_000a: dup
IL_000b: ldobj ""S""
IL_0010: stloc.1
IL_0011: ldloca.s V_1
IL_0013: call ""void S.AddOne()""
IL_0018: ldloc.1
IL_0019: ldfld ""int S.X""
IL_001e: call ""void System.Console.WriteLine(int)""
IL_0023: ldobj ""S""
IL_0028: stloc.1
IL_0029: ldloca.s V_1
IL_002b: call ""void S.AddOne()""
IL_0030: ldloc.1
IL_0031: ldfld ""int S.X""
IL_0036: call ""void System.Console.WriteLine(int)""
IL_003b: ret
}");
}
[Fact]
public void RefReadOnlyParamCopyReceiver()
{
var comp = CompileAndVerify(@"
using System;
struct S
{
public int X;
public S(int x) => X = x;
public void AddOne() => this.X++;
}
class C
{
public static void Main()
{
M(new S(0));
}
static void M(ref readonly S rs)
{
Console.WriteLine(rs.X);
rs.AddOne();
Console.WriteLine(rs.X);
}
}", expectedOutput: @"0
0");
comp.VerifyIL(@"C.M", @"
{
// Code size 37 (0x25)
.maxstack 1
.locals init (S V_0)
IL_0000: ldarg.0
IL_0001: ldfld ""int S.X""
IL_0006: call ""void System.Console.WriteLine(int)""
IL_000b: ldarg.0
IL_000c: ldobj ""S""
IL_0011: stloc.0
IL_0012: ldloca.s V_0
IL_0014: call ""void S.AddOne()""
IL_0019: ldarg.0
IL_001a: ldfld ""int S.X""
IL_001f: call ""void System.Console.WriteLine(int)""
IL_0024: ret
}");
}
[Fact]
public void CarryThroughLifetime()
{
var comp = CompileAndVerify(@"
class C
{
static ref readonly int M(ref int p)
{
ref readonly int rp = ref p;
return ref rp;
}
}", verify: false);
comp.VerifyIL("C.M", @"
{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ret
}");
}
[Fact]
public void TempForReadonly()
{
var comp = CompileAndVerify(@"
using System;
class C
{
public static void Main()
{
void L(ref readonly int p)
{
Console.WriteLine(p);
}
for (int i = 0; i < 3; i++)
{
L(10);
L(i);
}
}
}", expectedOutput: @"10
0
10
1
10
2");
comp.VerifyIL("C.Main()", @"
{
// Code size 30 (0x1e)
.maxstack 2
.locals init (int V_0, //i
int V_1)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0019
IL_0004: ldc.i4.s 10
IL_0006: stloc.1
IL_0007: ldloca.s V_1
IL_0009: call ""void C.<Main>g__L|0_0(ref readonly int)""
IL_000e: ldloca.s V_0
IL_0010: call ""void C.<Main>g__L|0_0(ref readonly int)""
IL_0015: ldloc.0
IL_0016: ldc.i4.1
IL_0017: add
IL_0018: stloc.0
IL_0019: ldloc.0
IL_001a: ldc.i4.3
IL_001b: blt.s IL_0004
IL_001d: ret
}");
}
[Fact]
public void RefReturnAssign()
{
var verifier = CompileAndVerify(@"
class C
{
static void M()
{
ref readonly int x = ref Helper();
int y = x + 1;
}
static ref readonly int Helper()
=> ref (new int[1])[0];
}");
verifier.VerifyIL("C.M()", @"
{
// Code size 11 (0xb)
.maxstack 1
.locals init (int V_0)
IL_0000: call ""ref readonly int C.Helper()""
IL_0005: ldind.i4
IL_0006: stloc.0
IL_0007: ldloca.s V_0
IL_0009: pop
IL_000a: ret
}");
}
[Fact]
public void RefReturnAssign2()
{
var verifier = CompileAndVerify(@"
class C
{
static void M()
{
ref readonly int x = ref Helper();
int y = x + 1;
}
static ref int Helper()
=> ref (new int[1])[0];
}");
verifier.VerifyIL("C.M()", @"
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: call ""ref int C.Helper()""
IL_0005: pop
IL_0006: ret
}");
}
[Fact]
public void RefReturnArrayAccess()
{
......
......@@ -22,6 +22,361 @@ public class RefLocalsAndReturnsTests : CompilingTestBase
return CreateCompilationWithMscorlib45(text);
}
[Fact]
public void RefReadonlyOnlyIn72()
{
var tree = SyntaxFactory.ParseSyntaxTree(@"
class C
{
void M()
{
int x = 0;
ref readonly int y = ref x;
}
}", options: TestOptions.Regular7_1);
var comp = CreateStandardCompilation(tree);
comp.VerifyDiagnostics(
// (7,13): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater.
// ref readonly int y = ref x;
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "readonly").WithArguments("readonly references", "7.2").WithLocation(7, 13));
}
[Fact]
public void CovariantConversionRefReadonly()
{
var comp = CreateStandardCompilation(@"
class C
{
void M()
{
string s = string.Empty;
ref readonly object x = ref s;
}
}");
comp.VerifyDiagnostics(
// (7,37): error CS8173: The expression must be of type 'object' because it is being assigned by reference
// ref readonly object x = ref s;
Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "s").WithArguments("object").WithLocation(7, 37));
}
[Fact]
public void ImplicitNumericRefReadonlyConversion()
{
var comp = CreateStandardCompilation(@"
class C
{
void M()
{
int x = 0;
ref readonly long y = ref x;
}
}");
comp.VerifyDiagnostics(
// (7,35): error CS8173: The expression must be of type 'long' because it is being assigned by reference
// ref readonly long y = ref x;
Diagnostic(ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion, "x").WithArguments("long").WithLocation(7, 35));
}
[Fact]
public void RefReadonlyLocalToLiteral()
{
var comp = CreateStandardCompilation(@"
class C
{
void M()
{
ref readonly int x = ref 42;
}
}");
comp.VerifyDiagnostics(
// (6,34): error CS8156: An expression cannot be used in this context because it may not be returned by reference
// ref readonly int x = ref 42;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "42").WithLocation(6, 34));
}
[Fact]
public void RefReadonlyNoCaptureInLambda()
{
var comp = CreateStandardCompilation(@"
using System;
class C
{
void M()
{
ref readonly int x = ref (new int[1])[0];
Action a = () =>
{
int i = x;
};
}
}");
comp.VerifyDiagnostics(
// (10,21): error CS8175: Cannot use ref local 'x' inside an anonymous method, lambda expression, or query expression
// int i = x;
Diagnostic(ErrorCode.ERR_AnonDelegateCantUseLocal, "x").WithArguments("x").WithLocation(10, 21));
}
[Fact]
public void RefReadonlyInLambda()
{
var comp = CreateStandardCompilation(@"
using System;
class C
{
void M()
{
Action a = () =>
{
ref readonly int x = ref (new int[1])[0];
int i = x;
};
}
}");
comp.VerifyDiagnostics();
}
[Fact]
public void RefReadonlyNoCaptureInLocalFunction()
{
var comp = CreateStandardCompilation(@"
class C
{
void M()
{
ref readonly int x = ref (new int[1])[0];
void Local()
{
int i = x;
}
Local();
}
}");
comp.VerifyDiagnostics(
// (9,21): error CS8175: Cannot use ref local 'x' inside an anonymous method, lambda expression, or query expression
// int i = x;
Diagnostic(ErrorCode.ERR_AnonDelegateCantUseLocal, "x").WithArguments("x").WithLocation(9, 21));
}
[Fact]
public void RefReadonlyInLocalFunction()
{
var comp = CreateStandardCompilation(@"
class C
{
void M()
{
void Local()
{
ref readonly int x = ref (new int[1])[0];
int i = x;
}
Local();
}
}");
comp.VerifyDiagnostics();
}
[Fact]
public void RefReadonlyInAsync()
{
var comp = CreateCompilationWithMscorlib46(@"
using System.Threading.Tasks;
class C
{
async Task M()
{
ref readonly int x = ref (new int[1])[0];
int i = x;
await Task.FromResult(false);
}
}");
comp.VerifyDiagnostics(
// (7,26): error CS8177: Async methods cannot have by reference locals
// ref readonly int x = ref (new int[1])[0];
Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x = ref (new int[1])[0]").WithLocation(7, 26));
}
[Fact]
public void RefReadonlyInIterator()
{
var comp = CreateStandardCompilation(@"
using System.Collections.Generic;
class C
{
IEnumerable<int> M()
{
ref readonly int x = ref (new int[1])[0];
int i = x;
yield return i;
}
}");
comp.VerifyDiagnostics(
// (7,26): error CS8176: Iterators cannot have by reference locals
// ref readonly int x = ref (new int[1])[0];
Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(7, 26));
}
[Fact]
public void RefReadonlyLocalNotWritable()
{
var comp = CreateStandardCompilation(@"
struct S
{
public int X;
public S(int x) => X = x;
public void AddOne() => this.X++;
}
class C
{
void M()
{
S s = new S(0);
ref readonly S rs = ref s;
s.X++;
rs.X++;
s.AddOne();
rs.AddOne();
s.X = 0;
rs.X = 0;
}
}");
comp.VerifyDiagnostics(
// (17,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer
// rs.X++;
Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "rs.X").WithLocation(17, 9),
// (21,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer
// rs.X = 0;
Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "rs.X").WithLocation(21, 9));
}
[Fact]
public void StripReadonlyInReturn()
{
var comp = CreateStandardCompilation(@"
class C
{
ref int M(ref int p)
{
ref readonly int rp = ref p;
return ref rp;
}
}");
comp.VerifyDiagnostics(
// (7,20): error CS8156: An expression cannot be used in this context because it may not be returned by reference
// return ref rp;
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "rp").WithLocation(7, 20));
}
[Fact]
public void MixingRefParams()
{
var comp = CreateStandardCompilation(@"
class C
{
void M()
{
void L(ref int x, ref readonly int y)
{
L(ref x, y);
L(ref y, x);
L(ref x, ref x);
ref readonly int xr = ref x;
L(ref x, xr);
L(ref x, ref xr);
L(ref xr, y);
}
}
}");
comp.VerifyDiagnostics(
// (9,19): error CS8329: Cannot use variable 'ref readonly int' as a ref or out value because it is a readonly variable
// L(ref y, x);
Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "ref readonly int").WithLocation(9, 19),
// (10,26): error CS1615: Argument 2 may not be passed with the 'ref' keyword
// L(ref x, ref x);
Diagnostic(ErrorCode.ERR_BadArgExtraRef, "x").WithArguments("2", "ref").WithLocation(10, 26),
// (14,26): error CS1510: A ref or out value must be an assignable variable
// L(ref x, ref xr);
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "xr").WithLocation(14, 26),
// (15,19): error CS1510: A ref or out value must be an assignable variable
// L(ref xr, y);
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "xr").WithLocation(15, 19));
}
[Fact]
public void AssignRefReadonlyToRefParam()
{
var comp = CreateCompilationRef(@"
class C
{
void M()
{
void L(ref int p) { }
L(ref 42);
int x = 0;
ref readonly int xr = ref x;
L(xr);
L(ref xr);
ref readonly int L2() => ref (new int[1])[0];
L(L2());
L(ref L2());
}
}");
comp.VerifyDiagnostics(
// (8,15): error CS1510: A ref or out value must be an assignable variable
// L(ref 42);
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "42").WithLocation(8, 15),
// (11,11): error CS1620: Argument 1 must be passed with the 'ref' keyword
// L(xr);
Diagnostic(ErrorCode.ERR_BadArgRef, "xr").WithArguments("1", "ref").WithLocation(11, 11),
// (12,15): error CS1510: A ref or out value must be an assignable variable
// L(ref xr);
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "xr").WithLocation(12, 15),
// (16,11): error CS1620: Argument 1 must be passed with the 'ref' keyword
// L(L2());
Diagnostic(ErrorCode.ERR_BadArgRef, "L2()").WithArguments("1", "ref").WithLocation(16, 11),
// (17,15): error CS8406: Cannot use method 'L2()' as a ref or out value because it is a readonly variable
// L(ref L2());
Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "L2()").WithArguments("method", "L2()").WithLocation(17, 15));
}
[Fact]
public void AssignRefReadonlyLocalToRefLocal()
{
var comp = CreateCompilationRef(@"
class C
{
void M()
{
ref readonly int L() => ref (new int[1])[0];
ref int w = ref L();
ref readonly int x = ref L();
ref int y = x;
ref int z = ref x;
}
}");
comp.VerifyDiagnostics(
// (8,25): error CS8406: Cannot use method 'L()' as a ref or out value because it is a readonly variable
// ref int w = ref L();
Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "L()").WithArguments("method", "L()").WithLocation(8, 25),
// (10,17): error CS8172: Cannot initialize a by-reference variable with a value
// ref int y = x;
Diagnostic(ErrorCode.ERR_InitializeByReferenceVariableWithValue, "y = x").WithLocation(10, 17),
// (10,21): error CS1510: A ref or out value must be an assignable variable
// ref int y = x;
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "x").WithLocation(10, 21),
// (11,25): error CS1510: A ref or out value must be an assignable variable
// ref int z = ref x;
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "x").WithLocation(11, 25)
);
}
[Fact]
public void RefLocalMissingInitializer()
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// 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;
......@@ -138,9 +138,6 @@ static void Use<T>(T dummy)
";
var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef });
comp.VerifyDiagnostics(
// (7,9): error CS1073: Unexpected token 'ref'
// ref readonly int local = ref (new int[1])[0];
Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(7, 9),
// (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),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册