diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
index 071f6900672e9c19fab195ba49f717a6eb0af61a..9967c45094454361cd175fde2140be2052b6644f 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
@@ -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));
}
}
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
index 03af201893c6a44bd38aa349b73dba1067403940..28f71e9356f2dcd399b860e779b4d209d7641652 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
@@ -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
///
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)
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
index d4c00ba1feae47764708a5dc06e595e9c5dd8860..495cdf11deb9f0ac30111f6800392ab9911df28f 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
@@ -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;
}
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index 5b7171e526c1ff36e4ed0d308e3c27ac7c692fc9..b74f05feecd31234a0c5d15b7aec5ed517b88747 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
+++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
@@ -8344,9 +8344,9 @@ internal class CSharpResources {
///
/// Looks up a localized string similar to The first parameter of the readonly reference extension method '{0}' must be a value type..
///
- 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 {
}
///
- /// 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.
///
internal static string ERR_RefReturnLvalueExpected {
get {
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 9b80f2af3a72582c63290f2078facec579bd9576..7a4eff9e181ea102a874b2933309aa098c0fd389 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -4815,7 +4815,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
An expression tree lambda may not contain a call to a method, property, or indexer that returns by reference
- An expression cannot be used in this context because it may not be returned by reference
+ An expression cannot be used in this context because it may not be passed or returned by reference
Cannot return '{0}' by reference because it was initialized to a value that cannot be returned by reference
@@ -5186,7 +5186,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
The first parameter of the reference extension method '{0}' must be a value type or a generic type constrained to struct.
-
+
The first parameter of the readonly reference extension method '{0}' must be a value type.
diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
index c7bc5620d03c60079b72fd9b28923e4d6ae3d854..ee8f4619ebdbf978ccfc5d83c9b06c10c8aeb03b 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -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,
diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs
index b30f5ac7973851e6d2ec0e24cb31cb992d944a8f..24df2f7382923e3fa544586465f31c6b56870844 100644
--- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs
@@ -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:
diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs
index 386ec9d1a7ed6933505deda723123972a1c43077..35dca66192b6e50551798ea1ffa03d462d1932b1 100644
--- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs
+++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs
@@ -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:
diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs
index fc1c526ef6468d8f14c5df83efe87a7c157ae0e5..66b63983c7945683f708405f4cfb8f7ab9074c7a 100644
--- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs
@@ -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);
diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
index 2ef9574cd00df17bf716e06749f3eaa61a358561..eeed1530d9d4a8a3198bec9198aebcec45600a57 100644
--- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
+++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs
@@ -495,7 +495,12 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind
}
///
- /// 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.
///
private static ImmutableArray GetEffectiveArgumentRefKinds(ImmutableArray argumentRefKindsOpt, ImmutableArray parameters)
{
@@ -505,6 +510,8 @@ private static ImmutableArray 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 GetEffectiveArgumentRefKinds(ImmutableArr
}
}
- refKindsBuilder[i] = paramRefKind;
+ refKindsBuilder[i] = argRefKind == RefKind.None ? paramRefKind : RefKindExtensions.StrictIn;
}
}
@@ -660,10 +667,18 @@ private static ImmutableArray GetRefKindsOrNull(ArrayBuilder 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 GetRefKindsOrNull(ArrayBuilder 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;
}
diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
index d088ccec5e8695e4887ff38fdd20412ba8e72b0a..c3e16a4bbe8ce78a7252bda458f3b1f476f10e34 100644
--- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
+++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
@@ -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()
diff --git a/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs
index 96bdb4bc09d7b94f5542c0480701fed88fa0c32b..cc4511d743d221d8180413d6579b197cb5051fc2 100644
--- a/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/RefKindExtensions.cs
@@ -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:
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs
index c3fa29e3caddf3ab8a976db5ee4760177d2a80bc..0695344a21c26a64e5677fcc84dfe5678de38d50 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs
@@ -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)
{
diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
index db542e99fb252ea703d1321f5452e104afaaf44c..f9602c094885755147b92b1587fdae5a04c40e19 100644
--- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
+++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml
@@ -1100,6 +1100,7 @@
+
SyntaxToken representing the optional ref or out keyword.
diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs
index bdcffed48778b1193cd7583ffc312b6c3b684e41..4128c28287f0e2cd80db2ab587c5991d504ef448 100644
--- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs
+++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_RefReadOnly.cs
@@ -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;
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
index e1847a3a402a3ff8ae11dd61454f3eecb9243496..7b3a68a01d894d7aad1eb6580ee363eb908132e3 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
@@ -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 GetT(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()
{
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs
index f79dacf05de7e7651f4ca2409bd1d4c7dc6c0c97..3ae28fb58b2345fe6472a609ae0d3bc4cf86469a 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs
@@ -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(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(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(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(in T, in T)").WithLocation(15, 9),
+ // (16,12): error CS1503: Argument 1: cannot convert from '' to 'in int'
+ // M1(null, 1);
+ Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "in int").WithLocation(16, 12),
+ // (17,9): error CS0411: The type arguments for method 'Program.M1(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(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(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(in T, in T)").WithLocation(22, 9),
+ // (23,9): error CS0411: The type arguments for method 'Program.M1(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(in T, in T)").WithLocation(23, 9)
+ );
+ }
+
+ [Fact]
+ public void GenericInferenceLambdaVariance()
+ {
+ var code = @"
+class Program
+{
+ public delegate void D1(in T arg1, in T arg2);
+
+ public static void M1(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)' 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)").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)
+ );
+ }
}
}
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs
index 04f404a5ca65e585e330ffe155d4762c4f6fd39a..b742aef6f46a8374fa3796bdc29e5f4c1ee646be 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs
@@ -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 global)
}
public static class Extensions
{
- public static void Deconstruct(ref readonly this Span self, out Span x, out Span y) => throw null;
+ public static void Deconstruct(in this Span self, out Span x, out Span y) => throw null;
}
";
CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs
index 115d8462b54cfd5f0464503b225bfad5c0c7c673..0a05c0ce6eae5647dbe173a714aff22a796f90b3 100644
--- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs
+++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefExtensionMethodsTests.cs
@@ -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(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(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(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(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));
diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs
index e2c100e24d13f0474c79328e4b31fb0dc6ee4e1d..d78ffc762aea10415bda972e848985a829995fcb 100644
--- a/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RefReadonlyTests.cs
@@ -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]
diff --git a/src/Compilers/Core/Portable/Symbols/RefKind.cs b/src/Compilers/Core/Portable/Symbols/RefKind.cs
index 6f4e7bebad15942e9ea4f55072b3e9c465df1709..8deb463cff7088bdb7891600ed1c00b75905c6ef 100644
--- a/src/Compilers/Core/Portable/Symbols/RefKind.cs
+++ b/src/Compilers/Core/Portable/Symbols/RefKind.cs
@@ -34,6 +34,10 @@ public enum RefKind : byte
/// Indicates a "ref readonly" return type.
///
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;
}
}