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; } }