提交 96b9bed9 编写于 作者: V Vladimir Sadov 提交者: GitHub

Merge pull request #22405 from VSadov/inAtCallSites

Allow "in" at call sites as a way to request strict "in" parameter passing.
......@@ -1193,7 +1193,6 @@ bool isRefEscape
var refKind = argRefKindsOpt.IsDefault ? RefKind.None : argRefKindsOpt[argIndex];
if (refKind != RefKind.None && argument.Type?.IsByRefLikeType == true)
{
Debug.Assert(refKind == RefKind.Ref || refKind == RefKind.Out);
escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression));
}
}
......@@ -1209,7 +1208,6 @@ bool isRefEscape
var refKind = argListRefKindsOpt.IsDefault ? RefKind.None : argListRefKindsOpt[argIndex];
if (refKind != RefKind.None && argument.Type?.IsByRefLikeType == true)
{
Debug.Assert(refKind == RefKind.Ref || refKind == RefKind.Out);
escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression));
}
}
......
......@@ -2233,7 +2233,8 @@ private bool RefMustBeObeyed(bool isDelegateCreation, ArgumentSyntax argumentSyn
{
case BoundKind.PropertyAccess:
case BoundKind.IndexerAccess:
hadError = !CheckValueKind(argumentSyntax, arg, BindValueKind.RefOrOut, false, diagnostics);
var requiredValueKind = origRefKind == RefKind.In ? BindValueKind.ReadonlyRef : BindValueKind.RefOrOut;
hadError = !CheckValueKind(argumentSyntax, arg, requiredValueKind, false, diagnostics);
return;
}
}
......@@ -2483,7 +2484,12 @@ internal GlobalExpressionVariable LookupDeclaredField(SyntaxNode node, string id
/// </summary>
private BoundExpression BindArgumentExpression(DiagnosticBag diagnostics, ExpressionSyntax argumentExpression, RefKind refKind, bool allowArglist)
{
BindValueKind valueKind = refKind == RefKind.None ? BindValueKind.RValue : BindValueKind.RefOrOut;
BindValueKind valueKind =
refKind == RefKind.None ?
BindValueKind.RValue :
refKind == RefKind.In ?
BindValueKind.ReadonlyRef:
BindValueKind.RefOrOut;
BoundExpression argument;
if (allowArglist)
......
......@@ -2575,16 +2575,15 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
{
var paramRefKind = parameter.RefKind;
if (paramRefKind == RefKind.In)
// 'None' argument is allowed to match 'In' parameter and should behave like 'None' for the purpose of overload resolution
if (argRefKind == RefKind.None && paramRefKind == RefKind.In)
{
// "in" parameters are effectively None for the purpose of overload resolution.
paramRefKind = RefKind.None;
return RefKind.None;
}
// Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type.
// We must ignore the 'ref' on the parameter while determining the applicability of argument for the given method call.
// During argument rewriting, we will replace the argument value with a temporary local and pass that local by reference.
if (allowRefOmittedArguments && paramRefKind == RefKind.Ref && argRefKind == RefKind.None && !_binder.InAttributeArgument)
{
hasAnyRefOmittedArgument = true;
......@@ -3116,9 +3115,13 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
// exists from the argument to the type of the corresponding parameter, or
// - for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter.
// RefKind has to match unless the ref kind is None and argument expression is of the type dynamic. This is a bug in Dev11 which we also implement.
// The spec is correct, this is not an intended behavior. We don't fix the bug to avoid a breaking change.
if (argRefKind != parRefKind && !(argRefKind == RefKind.None && argument.HasDynamicType()))
// RefKind has to match unless
// the ref kind is None and either:
// 1) parameter is an 'In' or
// 2) argument expression is of the type dynamic. This is a bug in Dev11 which we also implement.
// The spec is correct, this is not an intended behavior. We don't fix the bug to avoid a breaking change.
if (!(argRefKind == parRefKind ||
(argRefKind == RefKind.None && (parRefKind == RefKind.In || argument.HasDynamicType()))))
{
return Conversion.NoConversion;
}
......
......@@ -8344,9 +8344,9 @@ internal class CSharpResources {
/// <summary>
/// Looks up a localized string similar to The first parameter of the readonly reference extension method &apos;{0}&apos; must be a value type..
/// </summary>
internal static string ERR_RefReadOnlyExtensionMustBeValueType {
internal static string ERR_InExtensionMustBeValueType {
get {
return ResourceManager.GetString("ERR_RefReadOnlyExtensionMustBeValueType", resourceCulture);
return ResourceManager.GetString("ERR_InExtensionMustBeValueType", resourceCulture);
}
}
......@@ -8450,7 +8450,7 @@ internal class CSharpResources {
}
/// <summary>
/// Looks up a localized string similar to An expression cannot be used in this context because it may not be returned by reference.
/// Looks up a localized string similar to An expression cannot be used in this context because it may not be passed or returned by reference.
/// </summary>
internal static string ERR_RefReturnLvalueExpected {
get {
......
......@@ -4815,7 +4815,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>An expression tree lambda may not contain a call to a method, property, or indexer that returns by reference</value>
</data>
<data name="ERR_RefReturnLvalueExpected" xml:space="preserve">
<value>An expression cannot be used in this context because it may not be returned by reference</value>
<value>An expression cannot be used in this context because it may not be passed or returned by reference</value>
</data>
<data name="ERR_RefReturnNonreturnableLocal" xml:space="preserve">
<value>Cannot return '{0}' by reference because it was initialized to a value that cannot be returned by reference</value>
......@@ -5186,7 +5186,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_RefExtensionMustBeValueTypeOrConstrainedToOne" xml:space="preserve">
<value>The first parameter of the reference extension method '{0}' must be a value type or a generic type constrained to struct.</value>
</data>
<data name="ERR_RefReadOnlyExtensionMustBeValueType" xml:space="preserve">
<data name="ERR_InExtensionMustBeValueType" xml:space="preserve">
<value>The first parameter of the readonly reference extension method '{0}' must be a value type.</value>
</data>
<data name="ERR_BadParameterModifiersOrder" xml:space="preserve">
......
......@@ -1516,7 +1516,7 @@ internal enum ErrorCode
ERR_ExplicitReservedAttr = 8335,
ERR_TypeReserved = 8336,
ERR_RefExtensionMustBeValueTypeOrConstrainedToOne = 8337,
ERR_RefReadOnlyExtensionMustBeValueType = 8338,
ERR_InExtensionMustBeValueType = 8338,
ERR_BadParameterModifiersOrder = 8339,
ERR_FieldsInRoStruct = 8340,
......
......@@ -38868,6 +38868,7 @@ public ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken refOrOutKe
{
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.None:
break;
default:
......@@ -45792,6 +45793,7 @@ public static ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken ref
{
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.None:
break;
default:
......@@ -5593,6 +5593,7 @@ public static ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken ref
{
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.None:
break;
default:
......
......@@ -378,6 +378,13 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS
case BoundKind.Call:
var call = (BoundCall)expression;
// NOTE: There are two kinds of 'In' arguments that we may see at this point:
// - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier)
// - `RefKind.In` (specified with no modifiers and matched an 'In' parameter)
//
// It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible.
// The "strict" ones do not permit implicit copying, so the same situation should result in an error.
if (refKind != RefKind.None && refKind != RefKind.In)
{
Debug.Assert(call.Method.RefKind != RefKind.None);
......@@ -389,6 +396,12 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS
case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expression;
// NOTE: There are two kinds of 'In' arguments that we may see at this point:
// - `RefKindExtensions.StrictIn` (originally specified with 'In' modifier)
// - `RefKind.In` (specified with no modifiers and matched an 'In' parameter)
//
// It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible.
// The "strict" ones do not permit implicit copying, so the same situation should result in an error.
if (refKind != RefKind.None && refKind != RefKind.RefReadOnly)
{
Debug.Assert(conditional.IsByRef);
......
......@@ -495,7 +495,12 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind
}
/// <summary>
/// Patch refKinds for "in" arguments to have effective RefKind.In
/// Patch refKinds for arguments that match 'In' parameters to have effective RefKind.
/// For the purpose of further analysis we will mark the arguments as -
/// - In if was originally passed as None
/// - StrictIn if was originally passed as In
/// Here and in the layers after the lowering we only care about None/notNone differences for the arguments
/// Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule.
/// </summary>
private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArray<RefKind> argumentRefKindsOpt, ImmutableArray<ParameterSymbol> parameters)
{
......@@ -505,6 +510,8 @@ private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArr
var paramRefKind = parameters[i].RefKind;
if (paramRefKind == RefKind.In)
{
var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i];
if (refKindsBuilder == null)
{
if (!argumentRefKindsOpt.IsDefault)
......@@ -519,7 +526,7 @@ private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArr
}
}
refKindsBuilder[i] = paramRefKind;
refKindsBuilder[i] = argRefKind == RefKind.None ? paramRefKind : RefKindExtensions.StrictIn;
}
}
......@@ -660,10 +667,18 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
{
BoundExpression argument = rewrittenArguments[a];
int p = (!argsToParamsOpt.IsDefault) ? argsToParamsOpt[a] : a;
RefKind refKind = argumentRefKinds.RefKinds(a);
if (refKind == RefKind.None && parameters[p].RefKind == RefKind.In)
RefKind argRefKind = argumentRefKinds.RefKinds(a);
RefKind paramRefKind = parameters[p].RefKind;
// Patch refKinds for arguments that match 'In' parameters to have effective RefKind
// For the purpose of further analysis we will mark the arguments as -
// - In if was originally passed as None
// - StrictIn if was originally passed as In
// Here and in the layers after the lowering we only care about None/notNone differences for the arguments
// Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule.
if (paramRefKind == RefKind.In)
{
refKind = RefKind.In;
argRefKind = argRefKind == RefKind.None ? paramRefKind : RefKindExtensions.StrictIn;
}
Debug.Assert(arguments[p] == null);
......@@ -695,15 +710,15 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
return;
}
if (IsSafeForReordering(argument, refKind))
if (IsSafeForReordering(argument, argRefKind))
{
arguments[p] = argument;
refKinds[p] = refKind;
refKinds[p] = argRefKind;
}
else
{
BoundAssignmentOperator assignment;
var temp = _factory.StoreToTemp(argument, out assignment, refKind: refKind);
var temp = _factory.StoreToTemp(argument, out assignment, refKind: argRefKind);
storesToTemps.Add(assignment);
arguments[p] = temp;
}
......
......@@ -9570,13 +9570,19 @@ private bool IsEndOfArgumentList()
private bool IsPossibleArgumentExpression()
{
switch (this.CurrentToken.Kind)
return IsValidArgumentRefKindKeyword(this.CurrentToken.Kind) || this.IsPossibleExpression();
}
private static bool IsValidArgumentRefKindKeyword(SyntaxKind kind)
{
switch (kind)
{
case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
return true;
default:
return this.IsPossibleExpression();
return false;
}
}
......@@ -9591,10 +9597,10 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer)
nameColon = CheckFeatureAvailability(nameColon, MessageID.IDS_FeatureNamedArgument);
}
SyntaxToken refOrOutKeyword = null;
if (this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword)
SyntaxToken refKindKeyword = null;
if (IsValidArgumentRefKindKeyword(this.CurrentToken.Kind))
{
refOrOutKeyword = this.EatToken();
refKindKeyword = this.EatToken();
}
ExpressionSyntax expression;
......@@ -9609,18 +9615,23 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer)
}
else
{
if (refKindKeyword?.Kind == SyntaxKind.InKeyword)
{
refKindKeyword = this.CheckFeatureAvailability(refKindKeyword, MessageID.IDS_FeatureReadOnlyReferences);
}
// According to Language Specification, section 7.6.7 Element access
// The argument-list of an element-access is not allowed to contain ref or out arguments.
// However, we actually do support ref indexing of indexed properties in COM interop
// scenarios, and when indexing an object of static type "dynamic". So we enforce
// that the ref/out of the argument must match the parameter when binding the argument list.
expression = (refOrOutKeyword?.Kind == SyntaxKind.OutKeyword)
expression = (refKindKeyword?.Kind == SyntaxKind.OutKeyword)
? ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureOutVar, permitTupleDesignation: false)
: ParseSubExpression(Precedence.Expression);
}
return _syntaxFactory.Argument(nameColon, refOrOutKeyword, expression);
return _syntaxFactory.Argument(nameColon, refKindKeyword, expression);
}
private TypeOfExpressionSyntax ParseTypeOfExpression()
......
......@@ -22,6 +22,8 @@ public static RefKind GetRefKind(this SyntaxKind syntaxKind)
return RefKind.Ref;
case SyntaxKind.OutKeyword:
return RefKind.Out;
case SyntaxKind.InKeyword:
return RefKind.In;
case SyntaxKind.None:
return RefKind.None;
default:
......
......@@ -228,7 +228,7 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
}
else if (parameter0RefKind == RefKind.In && parameter0Type.TypeKind != TypeKind.Struct)
{
diagnostics.Add(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, location, Name);
diagnostics.Add(ErrorCode.ERR_InExtensionMustBeValueType, location, Name);
}
else if ((object)ContainingType.ContainingType != null)
{
......
......@@ -1100,6 +1100,7 @@
<Field Name="RefOrOutKeyword" Type="SyntaxToken" Optional="true">
<Kind Name="RefKeyword"/>
<Kind Name="OutKeyword"/>
<Kind Name="InKeyword"/>
<PropertyComment>
<summary>SyntaxToken representing the optional ref or out keyword.</summary>
</PropertyComment>
......
......@@ -2168,7 +2168,7 @@ public class Test
CreateStandardCompilation(text).VerifyEmitDiagnostics(
// (11,12): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor'
// public ref readonly int Method(ref readonly int x) => ref x;
// public ref readonly int Method(in int x) => ref x;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "ref readonly int").WithArguments("System.Runtime.CompilerServices.IsReadOnlyAttribute", ".ctor").WithLocation(11, 12),
// (11,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor'
// public ref readonly int Method(in int x) => ref x;
......
......@@ -752,6 +752,52 @@ public static void M1(in int arg1, in int arg2, in int arg3)
CompileAndVerify(comp, verify: false, expectedOutput: @"6");
}
[Fact]
public void ReadonlyParamAsyncSpillIn()
{
var text = @"
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Test().Wait();
}
public static async Task Test()
{
int local = 1;
M1(in RefReturning(ref local), await GetT(2), 3);
}
private static ref int RefReturning(ref int arg)
{
return ref arg;
}
public static async Task<T> GetT<T>(T val)
{
await Task.Yield();
return val;
}
public static void M1(in int arg1, in int arg2, in int arg3)
{
System.Console.WriteLine(arg1 + arg2 + arg3);
}
}
";
var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe);
comp.VerifyEmitDiagnostics(
// (14,44): error CS8178: 'await' cannot be used in an expression containing a call to 'Program.RefReturning(ref int)' because it returns by reference
// M1(in RefReturning(ref local), await GetT(2), 3);
Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "await GetT(2)").WithArguments("Program.RefReturning(ref int)").WithLocation(14, 44)
);
}
[Fact]
public void InParamAsyncSpill2()
{
......
......@@ -6,6 +6,7 @@
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;
using Xunit;
......@@ -9426,32 +9427,33 @@ public static void Main()
CreateStandardCompilation(code).VerifyDiagnostics(
// (11,20): error CS1525: Invalid expression term 'readonly'
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(11, 20),
// (11,20): error CS1026: ) expected
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(11, 20),
// (11,20): error CS1002: ; expected
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(11, 20),
// (11,20): error CS0106: The modifier 'readonly' is not valid for this item
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(11, 20),
// (11,30): error CS1001: Identifier expected
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(11, 30),
// (11,30): error CS1002: ; expected
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(11, 30),
// (11,30): error CS1513: } expected
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(11, 30),
// (11,29): error CS0118: 'x' is a variable but is used like a type
// Method(in x);
// Method(ref readonly x);
Diagnostic(ErrorCode.ERR_BadSKknown, "x").WithArguments("x", "variable", "type").WithLocation(11, 29),
// (10,13): warning CS0219: The variable 'x' is assigned but its value is never used
// int x = 5;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(10, 13));
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(10, 13)
);
}
[Fact]
......@@ -9468,13 +9470,17 @@ public static void Main()
{
int x = 5;
Method(in x);
byte y = 5;
Method(in y);
}
}";
CreateStandardCompilation(code).VerifyDiagnostics(
// (11,16): error CS1041: Identifier expected; 'in' is a keyword
// Method(in x);
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "in").WithArguments("", "in").WithLocation(11, 16));
// (14,19): error CS1503: Argument 1: cannot convert from 'in byte' to 'in int'
// Method(in y);
Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "in byte", "in int").WithLocation(14, 19)
);
}
[Fact]
......@@ -9499,5 +9505,312 @@ public static void Main()
// Method(out x);
Diagnostic(ErrorCode.ERR_BadArgExtraRef, "x").WithArguments("1", "out").WithLocation(11, 20));
}
[Fact]
public void PassingInArgumentsOverloadedOnIn()
{
var code = @"
public static class Program
{
public static void Method(in int inP)
{
System.Console.WriteLine(""in: "" + inP);
}
public static void Method(int valP)
{
System.Console.WriteLine(""val: "" + valP);
}
public static void Main()
{
int x = 5;
Method(in x);
Method(valP: 3);
Method(inP: 2);
}
}";
CompileAndVerify(code, expectedOutput: @"
in: 5
val: 3
in: 2
");
}
[Fact]
public void PassingInArgumentsOverloadedOnInIndexer()
{
var code = @"
public class Program
{
public int this[in int inP]
{
get
{
System.Console.WriteLine(""in: "" + inP);
return 1;
}
}
public int this[int valP]
{
get
{
System.Console.WriteLine(""val: "" + valP);
return 1;
}
}
public static void Main()
{
var p = new Program();
int x = 5;
_ = p[in x];
_ = p[valP: 3];
_ = p[inP: 2];
}
}
";
CompileAndVerify(code, expectedOutput: @"
in: 5
val: 3
in: 2
");
}
[Fact]
public void PassingInArgumentsOverloadedOnInErr()
{
var code = @"
public static class Program
{
public static void Method(in int inP)
{
System.Console.WriteLine(""in: "" + inP);
}
public static void Method(int valP)
{
System.Console.WriteLine(""val: "" + valP);
}
public static void Main()
{
byte x = 5;
Method(in x);
Method('Q');
Method(3);
Method(valP: out 2);
Method(valP: in 2);
}
}";
CreateStandardCompilation(code).VerifyDiagnostics(
// (17,19): error CS1503: Argument 1: cannot convert from 'in byte' to 'in int'
// Method(in x);
Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "in byte", "in int").WithLocation(17, 19),
// (18,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Method(in int)' and 'Program.Method(int)'
// Method('Q');
Diagnostic(ErrorCode.ERR_AmbigCall, "Method").WithArguments("Program.Method(in int)", "Program.Method(int)").WithLocation(18, 9),
// (19,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Method(in int)' and 'Program.Method(int)'
// Method(3);
Diagnostic(ErrorCode.ERR_AmbigCall, "Method").WithArguments("Program.Method(in int)", "Program.Method(int)").WithLocation(19, 9),
// (20,26): error CS1510: A ref or out value must be an assignable variable
// Method(valP: out 2);
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "2").WithLocation(20, 26),
// (21,25): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference
// Method(valP: in 2);
Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "2").WithLocation(21, 25)
);
}
[Fact]
public void GenericInferenceOnIn()
{
var code = @"
using System;
class Program
{
public static void M1<T>(in T arg1, in T arg2)
{
System.Console.WriteLine(typeof(T).ToString());
}
static void Main()
{
int x = 1;
byte y = 2;
M1(null, (string)null);
M1(default, 1);
M1(new Object(), new Exception());
M1(new Object(), 1);
M1(in x, in x); // valid, same type
M1(y, in x); // valid, byval x sets lower bound, byte converts to int
}
}
";
CompileAndVerify(code, expectedOutput: @"
System.String
System.Int32
System.Object
System.Object
System.Int32
System.Int32
");
}
[Fact]
public void GenericInferenceOnInErr()
{
var code = @"
class Program
{
public static void M1<T>(in T arg1, in T arg2)
{
System.Console.WriteLine(typeof(T).ToString());
}
static void Main()
{
int x = 1;
byte y = 2;
var rl = default(RefLike);
M1(null, null);
M1(null, 1);
M1(new object(), default(RefLike));
M1(rl, rl);
M1(in rl, in rl);
M1(in y, in x);
M1(in y, x);
}
ref struct RefLike{}
}
";
CreateStandardCompilation(code).VerifyDiagnostics(
// (15,9): error CS0411: The type arguments for method 'Program.M1<T>(in T, in T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// M1(null, null);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("Program.M1<T>(in T, in T)").WithLocation(15, 9),
// (16,12): error CS1503: Argument 1: cannot convert from '<null>' to 'in int'
// M1(null, 1);
Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "<null>", "in int").WithLocation(16, 12),
// (17,9): error CS0411: The type arguments for method 'Program.M1<T>(in T, in T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// M1(new object(), default(RefLike));
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("Program.M1<T>(in T, in T)").WithLocation(17, 9),
// (19,9): error CS0306: The type 'Program.RefLike' may not be used as a type argument
// M1(rl, rl);
Diagnostic(ErrorCode.ERR_BadTypeArgument, "M1").WithArguments("Program.RefLike").WithLocation(19, 9),
// (20,9): error CS0306: The type 'Program.RefLike' may not be used as a type argument
// M1(in rl, in rl);
Diagnostic(ErrorCode.ERR_BadTypeArgument, "M1").WithArguments("Program.RefLike").WithLocation(20, 9),
// (22,9): error CS0411: The type arguments for method 'Program.M1<T>(in T, in T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// M1(in y, in x);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("Program.M1<T>(in T, in T)").WithLocation(22, 9),
// (23,9): error CS0411: The type arguments for method 'Program.M1<T>(in T, in T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// M1(in y, x);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("Program.M1<T>(in T, in T)").WithLocation(23, 9)
);
}
[Fact]
public void GenericInferenceLambdaVariance()
{
var code = @"
class Program
{
public delegate void D1<T>(in T arg1, in T arg2);
public static void M1<T>(T arg1, T arg2)
{
System.Console.WriteLine(typeof(T).ToString());
}
static void Main()
{
M1((in int arg1, in int arg2) => throw null, (in int arg1, in int arg2) => throw null);
}
}
";
CreateStandardCompilation(code).VerifyDiagnostics(
// (13,9): error CS0411: The type arguments for method 'Program.M1<T>(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// M1((in int arg1, in int arg2) => throw null, (in int arg1, in int arg2) => throw null);
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M1").WithArguments("Program.M1<T>(T, T)").WithLocation(13, 9)
);
}
[Fact]
public void DelegateConversions()
{
var librarySrc = @"
public class C
{
public void RR_input(in int x) => throw null;
public ref readonly int RR_output() => throw null;
public ref readonly int P => throw null;
public ref readonly int this[in int i] => throw null;
public delegate ref readonly int Delegate(in int i);
}
public static class Extensions
{
public static void RR_extension(in this int x) => throw null;
public static void R_extension(ref this int x) => throw null;
}
";
var libComp = CreateStandardCompilation(librarySrc, references: new[] { SystemCoreRef }).VerifyDiagnostics();
var code = @"
class D
{
void M(C c, in int y)
{
c.RR_input(y);
VerifyRR(c.RR_output());
VerifyRR(c.P);
VerifyRR(c[y]);
C.Delegate x = VerifyDelegate;
y.RR_extension();
1.RR_extension();
y.R_extension(); // error 1
1.R_extension(); // error 2
}
void VerifyRR(in int y) => throw null;
ref readonly int VerifyDelegate(in int y) => throw null;
}
";
CreateStandardCompilation(code, references: new[] { libComp.EmitToImageReference() }).VerifyDiagnostics(
// (13,10): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable
// y.R_extension(); // error 1
Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "in int").WithLocation(13, 10),
// (14,10): error CS1510: A ref or out value must be an assignable variable
// 1.R_extension(); // error 2
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(14, 10)
);
CreateStandardCompilation(code, references: new[] {libComp.ToMetadataReference() }).VerifyDiagnostics(
// (13,10): error CS8329: Cannot use variable 'in int' as a ref or out value because it is a readonly variable
// y.R_extension(); // error 1
Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "y").WithArguments("variable", "in int").WithLocation(13, 10),
// (14,10): error CS1510: A ref or out value must be an assignable variable
// 1.R_extension(); // error 2
Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(14, 10)
);
}
}
}
......@@ -2497,7 +2497,7 @@ public static class Extensions
);
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/22442")]
[Fact]
public void DeconstructionAssignmentWithRefReadonlyExtension()
{
var text = @"
......@@ -2515,7 +2515,7 @@ public void M(ref Span<int> global)
}
public static class Extensions
{
public static void Deconstruct(ref readonly this Span<int> self, out Span<int> x, out Span<int> y) => throw null;
public static void Deconstruct(in this Span<int> self, out Span<int> x, out Span<int> y) => throw null;
}
";
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
......
......@@ -762,7 +762,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type.
// public static void PrintValue(in this string p)
Diagnostic(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
// (14,11): error CS1061: 'string' does not contain a definition for 'PrintValue' and no extension method 'PrintValue' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
// x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
......@@ -806,7 +806,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type.
// public static void PrintValue(in this System.IComparable p)
Diagnostic(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
// (14,11): error CS1061: 'IComparable' does not contain a definition for 'PrintValue' and no extension method 'PrintValue' accepting a first argument of type 'IComparable' could be found (are you missing a using directive or an assembly reference?)
// x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("System.IComparable", "PrintValue").WithLocation(14, 11));
......@@ -850,7 +850,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type.
// public static void PrintValue<T>(in this T p)
Diagnostic(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
// (14,11): error CS1061: 'string' does not contain a definition for 'PrintValue' and no extension method 'PrintValue' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
// x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
......@@ -894,7 +894,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type.
// public static void PrintValue<T>(in this T p) where T : struct
Diagnostic(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
// (14,11): error CS1061: 'int' does not contain a definition for 'PrintValue' and no extension method 'PrintValue' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?)
// x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("int", "PrintValue").WithLocation(14, 11));
......@@ -938,7 +938,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type.
// public static void PrintValue<T>(in this T p) where T : class
Diagnostic(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
// (14,11): error CS1061: 'string' does not contain a definition for 'PrintValue' and no extension method 'PrintValue' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
// x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
......@@ -982,7 +982,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type.
// public static void PrintValue<T>(in this T p) where T : System.IComparable
Diagnostic(ErrorCode.ERR_RefReadOnlyExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "PrintValue").WithArguments("PrintValue").WithLocation(4, 24),
// (14,11): error CS1061: 'string' does not contain a definition for 'PrintValue' and no extension method 'PrintValue' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
// x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
......
......@@ -55,6 +55,51 @@ unsafe class Program
);
}
[Fact]
public void InArgs_CSharp7()
{
var text = @"
class Program
{
static void M(in int x)
{
}
int this[in int x]
{
get
{
return 1;
}
}
static void Test1()
{
int x = 1;
M(in x);
_ = (new Program())[in x];
}
}
";
var comp = CreateCompilationWithMscorlib45(text, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1), options: TestOptions.UnsafeDebugDll);
comp.VerifyDiagnostics(
// (4,19): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater.
// static void M(in int x)
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "in").WithArguments("readonly references", "7.2").WithLocation(4, 19),
// (8,14): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater.
// int this[in int x]
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "in").WithArguments("readonly references", "7.2").WithLocation(8, 14),
// (19,11): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater.
// M(in x);
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "in").WithArguments("readonly references", "7.2").WithLocation(19, 11),
// (21,29): error CS8302: Feature 'readonly references' is not available in C# 7.1. Please use language version 7.2 or greater.
// _ = (new Program())[in x];
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_1, "in").WithArguments("readonly references", "7.2").WithLocation(21, 29)
);
}
[Fact]
public void RefReadonlyReturn_Unexpected()
{
......@@ -328,10 +373,7 @@ void N()
int x = 0;
M(in x);
}
}").GetParseDiagnostics().Verify(
// (10,11): error CS1041: Identifier expected; 'in' is a keyword
// M(in x);
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "in").WithArguments("", "in").WithLocation(10, 11));
}").GetParseDiagnostics().Verify();
}
[Fact]
......
......@@ -34,6 +34,10 @@ public enum RefKind : byte
/// Indicates a "ref readonly" return type.
/// </summary>
RefReadOnly = 3,
// NOTE: There is an additional value of this enum type - RefKindExtensions.StrictIn == RefKind.In + 1
// It is used internally during lowering.
// Consider that when adding values or changing this enum in some other way.
}
internal static class RefKindExtensions
......@@ -55,6 +59,7 @@ internal static string ToArgumentDisplayString(this RefKind kind)
{
case RefKind.Out: return "out";
case RefKind.Ref: return "ref";
case RefKind.In: return "in";
default: throw ExceptionUtilities.UnexpectedValue(kind);
}
}
......@@ -70,5 +75,10 @@ internal static string ToParameterPrefix(this RefKind kind)
default: throw ExceptionUtilities.UnexpectedValue(kind);
}
}
// used internally to track `In` arguments that were specified with `In` modifier
// as opposed to those that were specified with no modifiers and matched `In` parameter
// There is at least one kind of anlysis that cares about this distinction - async stack spilling
internal const RefKind StrictIn = RefKind.In + 1;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册