提交 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 ...@@ -1193,7 +1193,6 @@ bool isRefEscape
var refKind = argRefKindsOpt.IsDefault ? RefKind.None : argRefKindsOpt[argIndex]; var refKind = argRefKindsOpt.IsDefault ? RefKind.None : argRefKindsOpt[argIndex];
if (refKind != RefKind.None && argument.Type?.IsByRefLikeType == true) if (refKind != RefKind.None && argument.Type?.IsByRefLikeType == true)
{ {
Debug.Assert(refKind == RefKind.Ref || refKind == RefKind.Out);
escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression)); escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression));
} }
} }
...@@ -1209,7 +1208,6 @@ bool isRefEscape ...@@ -1209,7 +1208,6 @@ bool isRefEscape
var refKind = argListRefKindsOpt.IsDefault ? RefKind.None : argListRefKindsOpt[argIndex]; var refKind = argListRefKindsOpt.IsDefault ? RefKind.None : argListRefKindsOpt[argIndex];
if (refKind != RefKind.None && argument.Type?.IsByRefLikeType == true) if (refKind != RefKind.None && argument.Type?.IsByRefLikeType == true)
{ {
Debug.Assert(refKind == RefKind.Ref || refKind == RefKind.Out);
escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression)); escapeTo = Math.Min(escapeTo, GetValEscape(argument, scopeOfTheContainingExpression));
} }
} }
......
...@@ -2233,7 +2233,8 @@ private bool RefMustBeObeyed(bool isDelegateCreation, ArgumentSyntax argumentSyn ...@@ -2233,7 +2233,8 @@ private bool RefMustBeObeyed(bool isDelegateCreation, ArgumentSyntax argumentSyn
{ {
case BoundKind.PropertyAccess: case BoundKind.PropertyAccess:
case BoundKind.IndexerAccess: 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; return;
} }
} }
...@@ -2483,7 +2484,12 @@ internal GlobalExpressionVariable LookupDeclaredField(SyntaxNode node, string id ...@@ -2483,7 +2484,12 @@ internal GlobalExpressionVariable LookupDeclaredField(SyntaxNode node, string id
/// </summary> /// </summary>
private BoundExpression BindArgumentExpression(DiagnosticBag diagnostics, ExpressionSyntax argumentExpression, RefKind refKind, bool allowArglist) 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; BoundExpression argument;
if (allowArglist) if (allowArglist)
......
...@@ -2575,16 +2575,15 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind ...@@ -2575,16 +2575,15 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
{ {
var paramRefKind = 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. return RefKind.None;
paramRefKind = 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. // 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. // 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. // 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) if (allowRefOmittedArguments && paramRefKind == RefKind.Ref && argRefKind == RefKind.None && !_binder.InAttributeArgument)
{ {
hasAnyRefOmittedArgument = true; hasAnyRefOmittedArgument = true;
...@@ -3116,9 +3115,13 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind ...@@ -3116,9 +3115,13 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind
// exists from the argument to the type of the corresponding parameter, or // 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. // - 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. // RefKind has to match unless
// The spec is correct, this is not an intended behavior. We don't fix the bug to avoid a breaking change. // the ref kind is None and either:
if (argRefKind != parRefKind && !(argRefKind == RefKind.None && argument.HasDynamicType())) // 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; return Conversion.NoConversion;
} }
......
...@@ -8344,9 +8344,9 @@ internal class CSharpResources { ...@@ -8344,9 +8344,9 @@ internal class CSharpResources {
/// <summary> /// <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.. /// 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> /// </summary>
internal static string ERR_RefReadOnlyExtensionMustBeValueType { internal static string ERR_InExtensionMustBeValueType {
get { get {
return ResourceManager.GetString("ERR_RefReadOnlyExtensionMustBeValueType", resourceCulture); return ResourceManager.GetString("ERR_InExtensionMustBeValueType", resourceCulture);
} }
} }
...@@ -8450,7 +8450,7 @@ internal class CSharpResources { ...@@ -8450,7 +8450,7 @@ internal class CSharpResources {
} }
/// <summary> /// <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> /// </summary>
internal static string ERR_RefReturnLvalueExpected { internal static string ERR_RefReturnLvalueExpected {
get { get {
......
...@@ -4815,7 +4815,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ ...@@ -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> <value>An expression tree lambda may not contain a call to a method, property, or indexer that returns by reference</value>
</data> </data>
<data name="ERR_RefReturnLvalueExpected" xml:space="preserve"> <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>
<data name="ERR_RefReturnNonreturnableLocal" xml:space="preserve"> <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> <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 ...@@ -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"> <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> <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>
<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> <value>The first parameter of the readonly reference extension method '{0}' must be a value type.</value>
</data> </data>
<data name="ERR_BadParameterModifiersOrder" xml:space="preserve"> <data name="ERR_BadParameterModifiersOrder" xml:space="preserve">
......
...@@ -1516,7 +1516,7 @@ internal enum ErrorCode ...@@ -1516,7 +1516,7 @@ internal enum ErrorCode
ERR_ExplicitReservedAttr = 8335, ERR_ExplicitReservedAttr = 8335,
ERR_TypeReserved = 8336, ERR_TypeReserved = 8336,
ERR_RefExtensionMustBeValueTypeOrConstrainedToOne = 8337, ERR_RefExtensionMustBeValueTypeOrConstrainedToOne = 8337,
ERR_RefReadOnlyExtensionMustBeValueType = 8338, ERR_InExtensionMustBeValueType = 8338,
ERR_BadParameterModifiersOrder = 8339, ERR_BadParameterModifiersOrder = 8339,
ERR_FieldsInRoStruct = 8340, ERR_FieldsInRoStruct = 8340,
......
...@@ -38868,6 +38868,7 @@ public ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken refOrOutKe ...@@ -38868,6 +38868,7 @@ public ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken refOrOutKe
{ {
case SyntaxKind.RefKeyword: case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword: case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.None: case SyntaxKind.None:
break; break;
default: default:
...@@ -45792,6 +45793,7 @@ public static ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken ref ...@@ -45792,6 +45793,7 @@ public static ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken ref
{ {
case SyntaxKind.RefKeyword: case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword: case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.None: case SyntaxKind.None:
break; break;
default: default:
...@@ -5593,6 +5593,7 @@ public static ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken ref ...@@ -5593,6 +5593,7 @@ public static ArgumentSyntax Argument(NameColonSyntax nameColon, SyntaxToken ref
{ {
case SyntaxKind.RefKeyword: case SyntaxKind.RefKeyword:
case SyntaxKind.OutKeyword: case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
case SyntaxKind.None: case SyntaxKind.None:
break; break;
default: default:
......
...@@ -378,6 +378,13 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS ...@@ -378,6 +378,13 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS
case BoundKind.Call: case BoundKind.Call:
var call = (BoundCall)expression; 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) if (refKind != RefKind.None && refKind != RefKind.In)
{ {
Debug.Assert(call.Method.RefKind != RefKind.None); Debug.Assert(call.Method.RefKind != RefKind.None);
...@@ -389,6 +396,12 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS ...@@ -389,6 +396,12 @@ private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundS
case BoundKind.ConditionalOperator: case BoundKind.ConditionalOperator:
var conditional = (BoundConditionalOperator)expression; 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) if (refKind != RefKind.None && refKind != RefKind.RefReadOnly)
{ {
Debug.Assert(conditional.IsByRef); Debug.Assert(conditional.IsByRef);
......
...@@ -495,7 +495,12 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind ...@@ -495,7 +495,12 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind
} }
/// <summary> /// <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> /// </summary>
private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArray<RefKind> argumentRefKindsOpt, ImmutableArray<ParameterSymbol> parameters) private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArray<RefKind> argumentRefKindsOpt, ImmutableArray<ParameterSymbol> parameters)
{ {
...@@ -505,6 +510,8 @@ private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArr ...@@ -505,6 +510,8 @@ private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArr
var paramRefKind = parameters[i].RefKind; var paramRefKind = parameters[i].RefKind;
if (paramRefKind == RefKind.In) if (paramRefKind == RefKind.In)
{ {
var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i];
if (refKindsBuilder == null) if (refKindsBuilder == null)
{ {
if (!argumentRefKindsOpt.IsDefault) if (!argumentRefKindsOpt.IsDefault)
...@@ -519,7 +526,7 @@ private static ImmutableArray<RefKind> GetEffectiveArgumentRefKinds(ImmutableArr ...@@ -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 ...@@ -660,10 +667,18 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
{ {
BoundExpression argument = rewrittenArguments[a]; BoundExpression argument = rewrittenArguments[a];
int p = (!argsToParamsOpt.IsDefault) ? argsToParamsOpt[a] : a; int p = (!argsToParamsOpt.IsDefault) ? argsToParamsOpt[a] : a;
RefKind refKind = argumentRefKinds.RefKinds(a); RefKind argRefKind = argumentRefKinds.RefKinds(a);
if (refKind == RefKind.None && parameters[p].RefKind == RefKind.In) 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); Debug.Assert(arguments[p] == null);
...@@ -695,15 +710,15 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r ...@@ -695,15 +710,15 @@ private static ImmutableArray<RefKind> GetRefKindsOrNull(ArrayBuilder<RefKind> r
return; return;
} }
if (IsSafeForReordering(argument, refKind)) if (IsSafeForReordering(argument, argRefKind))
{ {
arguments[p] = argument; arguments[p] = argument;
refKinds[p] = refKind; refKinds[p] = argRefKind;
} }
else else
{ {
BoundAssignmentOperator assignment; BoundAssignmentOperator assignment;
var temp = _factory.StoreToTemp(argument, out assignment, refKind: refKind); var temp = _factory.StoreToTemp(argument, out assignment, refKind: argRefKind);
storesToTemps.Add(assignment); storesToTemps.Add(assignment);
arguments[p] = temp; arguments[p] = temp;
} }
......
...@@ -9570,13 +9570,19 @@ private bool IsEndOfArgumentList() ...@@ -9570,13 +9570,19 @@ private bool IsEndOfArgumentList()
private bool IsPossibleArgumentExpression() 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.RefKeyword:
case SyntaxKind.OutKeyword: case SyntaxKind.OutKeyword:
case SyntaxKind.InKeyword:
return true; return true;
default: default:
return this.IsPossibleExpression(); return false;
} }
} }
...@@ -9591,10 +9597,10 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) ...@@ -9591,10 +9597,10 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer)
nameColon = CheckFeatureAvailability(nameColon, MessageID.IDS_FeatureNamedArgument); nameColon = CheckFeatureAvailability(nameColon, MessageID.IDS_FeatureNamedArgument);
} }
SyntaxToken refOrOutKeyword = null; SyntaxToken refKindKeyword = null;
if (this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword) if (IsValidArgumentRefKindKeyword(this.CurrentToken.Kind))
{ {
refOrOutKeyword = this.EatToken(); refKindKeyword = this.EatToken();
} }
ExpressionSyntax expression; ExpressionSyntax expression;
...@@ -9609,18 +9615,23 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer) ...@@ -9609,18 +9615,23 @@ private ArgumentSyntax ParseArgumentExpression(bool isIndexer)
} }
else else
{ {
if (refKindKeyword?.Kind == SyntaxKind.InKeyword)
{
refKindKeyword = this.CheckFeatureAvailability(refKindKeyword, MessageID.IDS_FeatureReadOnlyReferences);
}
// According to Language Specification, section 7.6.7 Element access // 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. // 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 // 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 // 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. // 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) ? ParseExpressionOrDeclaration(ParseTypeMode.Normal, feature: MessageID.IDS_FeatureOutVar, permitTupleDesignation: false)
: ParseSubExpression(Precedence.Expression); : ParseSubExpression(Precedence.Expression);
} }
return _syntaxFactory.Argument(nameColon, refOrOutKeyword, expression); return _syntaxFactory.Argument(nameColon, refKindKeyword, expression);
} }
private TypeOfExpressionSyntax ParseTypeOfExpression() private TypeOfExpressionSyntax ParseTypeOfExpression()
......
...@@ -22,6 +22,8 @@ public static RefKind GetRefKind(this SyntaxKind syntaxKind) ...@@ -22,6 +22,8 @@ public static RefKind GetRefKind(this SyntaxKind syntaxKind)
return RefKind.Ref; return RefKind.Ref;
case SyntaxKind.OutKeyword: case SyntaxKind.OutKeyword:
return RefKind.Out; return RefKind.Out;
case SyntaxKind.InKeyword:
return RefKind.In;
case SyntaxKind.None: case SyntaxKind.None:
return RefKind.None; return RefKind.None;
default: default:
......
...@@ -228,7 +228,7 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB ...@@ -228,7 +228,7 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB
} }
else if (parameter0RefKind == RefKind.In && parameter0Type.TypeKind != TypeKind.Struct) 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) else if ((object)ContainingType.ContainingType != null)
{ {
......
...@@ -1100,6 +1100,7 @@ ...@@ -1100,6 +1100,7 @@
<Field Name="RefOrOutKeyword" Type="SyntaxToken" Optional="true"> <Field Name="RefOrOutKeyword" Type="SyntaxToken" Optional="true">
<Kind Name="RefKeyword"/> <Kind Name="RefKeyword"/>
<Kind Name="OutKeyword"/> <Kind Name="OutKeyword"/>
<Kind Name="InKeyword"/>
<PropertyComment> <PropertyComment>
<summary>SyntaxToken representing the optional ref or out keyword.</summary> <summary>SyntaxToken representing the optional ref or out keyword.</summary>
</PropertyComment> </PropertyComment>
......
...@@ -2168,7 +2168,7 @@ public class Test ...@@ -2168,7 +2168,7 @@ public class Test
CreateStandardCompilation(text).VerifyEmitDiagnostics( CreateStandardCompilation(text).VerifyEmitDiagnostics(
// (11,12): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor' // (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), 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' // (11,36): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.IsReadOnlyAttribute..ctor'
// public ref readonly int Method(in int x) => ref x; // 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) ...@@ -752,6 +752,52 @@ public static void M1(in int arg1, in int arg2, in int arg3)
CompileAndVerify(comp, verify: false, expectedOutput: @"6"); 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] [Fact]
public void InParamAsyncSpill2() public void InParamAsyncSpill2()
{ {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities; using Roslyn.Test.Utilities;
using Xunit; using Xunit;
...@@ -9426,32 +9427,33 @@ public static void Main() ...@@ -9426,32 +9427,33 @@ public static void Main()
CreateStandardCompilation(code).VerifyDiagnostics( CreateStandardCompilation(code).VerifyDiagnostics(
// (11,20): error CS1525: Invalid expression term 'readonly' // (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), Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(11, 20),
// (11,20): error CS1026: ) expected // (11,20): error CS1026: ) expected
// Method(in x); // Method(ref readonly x);
Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(11, 20), Diagnostic(ErrorCode.ERR_CloseParenExpected, "readonly").WithLocation(11, 20),
// (11,20): error CS1002: ; expected // (11,20): error CS1002: ; expected
// Method(in x); // Method(ref readonly x);
Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(11, 20), Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(11, 20),
// (11,20): error CS0106: The modifier 'readonly' is not valid for this item // (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), Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(11, 20),
// (11,30): error CS1001: Identifier expected // (11,30): error CS1001: Identifier expected
// Method(in x); // Method(ref readonly x);
Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(11, 30), Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(11, 30),
// (11,30): error CS1002: ; expected // (11,30): error CS1002: ; expected
// Method(in x); // Method(ref readonly x);
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(11, 30), Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(11, 30),
// (11,30): error CS1513: } expected // (11,30): error CS1513: } expected
// Method(in x); // Method(ref readonly x);
Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(11, 30), Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(11, 30),
// (11,29): error CS0118: 'x' is a variable but is used like a type // (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), 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 // (10,13): warning CS0219: The variable 'x' is assigned but its value is never used
// int x = 5; // int x = 5;
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(10, 13)); Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(10, 13)
);
} }
[Fact] [Fact]
...@@ -9468,13 +9470,17 @@ public static void Main() ...@@ -9468,13 +9470,17 @@ public static void Main()
{ {
int x = 5; int x = 5;
Method(in x); Method(in x);
byte y = 5;
Method(in y);
} }
}"; }";
CreateStandardCompilation(code).VerifyDiagnostics( CreateStandardCompilation(code).VerifyDiagnostics(
// (11,16): error CS1041: Identifier expected; 'in' is a keyword // (14,19): error CS1503: Argument 1: cannot convert from 'in byte' to 'in int'
// Method(in x); // Method(in y);
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "in").WithArguments("", "in").WithLocation(11, 16)); Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "in byte", "in int").WithLocation(14, 19)
);
} }
[Fact] [Fact]
...@@ -9499,5 +9505,312 @@ public static void Main() ...@@ -9499,5 +9505,312 @@ public static void Main()
// Method(out x); // Method(out x);
Diagnostic(ErrorCode.ERR_BadArgExtraRef, "x").WithArguments("1", "out").WithLocation(11, 20)); 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 ...@@ -2497,7 +2497,7 @@ public static class Extensions
); );
} }
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/22442")] [Fact]
public void DeconstructionAssignmentWithRefReadonlyExtension() public void DeconstructionAssignmentWithRefReadonlyExtension()
{ {
var text = @" var text = @"
...@@ -2515,7 +2515,7 @@ public void M(ref Span<int> global) ...@@ -2515,7 +2515,7 @@ public void M(ref Span<int> global)
} }
public static class Extensions 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( CreateCompilationWithMscorlibAndSpan(text).VerifyDiagnostics(
......
...@@ -762,7 +762,7 @@ public static void Main() ...@@ -762,7 +762,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics( var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type. // (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) // 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?) // (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(); // x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
...@@ -806,7 +806,7 @@ public static void Main() ...@@ -806,7 +806,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics( var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type. // (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) // 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?) // (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(); // x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("System.IComparable", "PrintValue").WithLocation(14, 11)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("System.IComparable", "PrintValue").WithLocation(14, 11));
...@@ -850,7 +850,7 @@ public static void Main() ...@@ -850,7 +850,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics( var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type. // (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) // 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?) // (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(); // x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
...@@ -894,7 +894,7 @@ public static void Main() ...@@ -894,7 +894,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics( var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type. // (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 // 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?) // (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(); // x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("int", "PrintValue").WithLocation(14, 11)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("int", "PrintValue").WithLocation(14, 11));
...@@ -938,7 +938,7 @@ public static void Main() ...@@ -938,7 +938,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics( var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type. // (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 // 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?) // (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(); // x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
...@@ -982,7 +982,7 @@ public static void Main() ...@@ -982,7 +982,7 @@ public static void Main()
var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics( var reference = CreateCompilationWithMscorlibAndSystemCore(code).VerifyDiagnostics(
// (4,24): error CS8338: The first parameter of the readonly reference extension method 'PrintValue' must be a value type. // (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 // 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?) // (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(); // x.PrintValue();
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "PrintValue").WithArguments("string", "PrintValue").WithLocation(14, 11));
......
...@@ -55,6 +55,51 @@ unsafe class Program ...@@ -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] [Fact]
public void RefReadonlyReturn_Unexpected() public void RefReadonlyReturn_Unexpected()
{ {
...@@ -328,10 +373,7 @@ void N() ...@@ -328,10 +373,7 @@ void N()
int x = 0; int x = 0;
M(in x); M(in x);
} }
}").GetParseDiagnostics().Verify( }").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));
} }
[Fact] [Fact]
......
...@@ -34,6 +34,10 @@ public enum RefKind : byte ...@@ -34,6 +34,10 @@ public enum RefKind : byte
/// Indicates a "ref readonly" return type. /// Indicates a "ref readonly" return type.
/// </summary> /// </summary>
RefReadOnly = 3, 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 internal static class RefKindExtensions
...@@ -55,6 +59,7 @@ internal static string ToArgumentDisplayString(this RefKind kind) ...@@ -55,6 +59,7 @@ internal static string ToArgumentDisplayString(this RefKind kind)
{ {
case RefKind.Out: return "out"; case RefKind.Out: return "out";
case RefKind.Ref: return "ref"; case RefKind.Ref: return "ref";
case RefKind.In: return "in";
default: throw ExceptionUtilities.UnexpectedValue(kind); default: throw ExceptionUtilities.UnexpectedValue(kind);
} }
} }
...@@ -70,5 +75,10 @@ internal static string ToParameterPrefix(this RefKind kind) ...@@ -70,5 +75,10 @@ internal static string ToParameterPrefix(this RefKind kind)
default: throw ExceptionUtilities.UnexpectedValue(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.
先完成此消息的编辑!
想要评论请 注册