Skip to content

Commit

Permalink
Create tests to cover test plans #19216 and #20127 (#22284)
Browse files Browse the repository at this point in the history
* Create tests to cover test plans #19216 and #20127

* clean up
  • Loading branch information
OmarTawfik committed Sep 28, 2017
1 parent 2dfc006 commit f99832a
Show file tree
Hide file tree
Showing 13 changed files with 939 additions and 213 deletions.
54 changes: 27 additions & 27 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ private bool CheckParameterValueKind(SyntaxNode node, BoundParameter parameter,
ParameterSymbol parameterSymbol = parameter.ParameterSymbol;

// all parameters can be passed by ref/out or assigned to
// except "in" parameters, which are readonly
// except "ref readonly" parameters, which are readonly
if (parameterSymbol.RefKind == RefKind.RefReadOnly && RequiresAssignableVariable(valueKind))
{
ReportReadOnlyError(parameterSymbol, node, valueKind, checkingReceiver, diagnostics);
Expand Down Expand Up @@ -959,7 +959,7 @@ bool isRefEscape
//by default it is safe to escape
uint escapeScope = Binder.ExternalScope;

ArrayBuilder<bool> inParametersMatchedWithArgs = null;
ArrayBuilder<bool> refReadOnlyParametersMatchedWithArgs = null;

if (!argsOpt.IsDefault)
{
Expand All @@ -983,7 +983,7 @@ bool isRefEscape
goto moreArguments;
}

RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs);
RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref refReadOnlyParametersMatchedWithArgs);

// ref escape scope is the narrowest of
// - ref escape of all byref arguments
Expand All @@ -1001,20 +1001,20 @@ bool isRefEscape
if (escapeScope >= scopeOfTheContainingExpression)
{
// no longer needed
inParametersMatchedWithArgs?.Free();
refReadOnlyParametersMatchedWithArgs?.Free();

// can't get any worse
return escapeScope;
}
}
}

// handle omitted optional "in" parameters if there are any
ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs);
// handle omitted optional "ref readonly" parameters if there are any
ParameterSymbol unmatchedRefReadOnlyParameter = TryGetUnmatchedRefReadOnlyParameterAndFreeMatchedArgs(parameters, ref refReadOnlyParametersMatchedWithArgs);

// unmatched "in" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// unmatched "ref readonly" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// its val escape is ExternalScope (does not affect overal result)
if (unmatchedInParameter != null && isRefEscape)
if (unmatchedRefReadOnlyParameter != null && isRefEscape)
{
return scopeOfTheContainingExpression;
}
Expand Down Expand Up @@ -1063,7 +1063,7 @@ bool isRefEscape
receiverOpt = null;
}

ArrayBuilder<bool> inParametersMatchedWithArgs = null;
ArrayBuilder<bool> refReadOnlyParametersMatchedWithArgs = null;

if (!argsOpt.IsDefault)
{
Expand All @@ -1088,7 +1088,7 @@ bool isRefEscape
goto moreArguments;
}

RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref inParametersMatchedWithArgs);
RefKind effectiveRefKind = GetEffectiveRefKind(argIndex, argRefKindsOpt, parameters, argsToParamsOpt, ref refReadOnlyParametersMatchedWithArgs);

// ref escape scope is the narrowest of
// - ref escape of all byref arguments
Expand All @@ -1103,7 +1103,7 @@ bool isRefEscape
if (!valid)
{
// no longer needed
inParametersMatchedWithArgs?.Free();
refReadOnlyParametersMatchedWithArgs?.Free();

ErrorCode errorCode = GetStandardCallEscapeError(checkingReceiver);

Expand All @@ -1124,14 +1124,14 @@ bool isRefEscape
}
}

// handle omitted optional "in" parameters if there are any
ParameterSymbol unmatchedInParameter = TryGetUnmatchedInParameterAndFreeMatchedArgs(parameters, ref inParametersMatchedWithArgs);
// handle omitted optional "ref readonly" parameters if there are any
ParameterSymbol unmatchedRefReadOnlyParameter = TryGetUnmatchedRefReadOnlyParameterAndFreeMatchedArgs(parameters, ref refReadOnlyParametersMatchedWithArgs);

// unmatched "in" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// unmatched "ref readonly" parameter is the same as a literal, its ref escape is scopeOfTheContainingExpression (can't get any worse)
// its val escape is ExternalScope (does not affect overal result)
if (unmatchedInParameter != null && isRefEscape)
if (unmatchedRefReadOnlyParameter != null && isRefEscape)
{
Error(diagnostics, GetStandardCallEscapeError(checkingReceiver), syntax, symbol, unmatchedInParameter.Name);
Error(diagnostics, GetStandardCallEscapeError(checkingReceiver), syntax, symbol, unmatchedRefReadOnlyParameter.Name);
return false;
}

Expand Down Expand Up @@ -1276,15 +1276,15 @@ bool isRefEscape
/// <summary>
/// Gets "effective" ref kind of an argument.
/// Generally we know if a formal argument is passed as ref/out by looking at the call site.
/// However, to distinguish "in" and regular "val" parameters we need to take a look at corresponding parameter, if such exists.
/// NOTE: there are cases like params/vararg, when a corresponding parameter may not exist, then it cannot be an "in".
/// However, to distinguish "ref readonly" and regular "val" parameters we need to take a look at corresponding parameter, if such exists.
/// NOTE: there are cases like params/vararg, when a corresponding parameter may not exist, then it cannot be a "ref readonly".
/// </summary>
private static RefKind GetEffectiveRefKind(
int argIndex,
ImmutableArray<RefKind> argRefKindsOpt,
ImmutableArray<ParameterSymbol> parameters,
ImmutableArray<int> argsToParamsOpt,
ref ArrayBuilder<bool> inParametersMatchedWithArgs)
ref ArrayBuilder<bool> refReadOnlyParametersMatchedWithArgs)
{
var effectiveRefKind = argRefKindsOpt.IsDefault ? RefKind.None : argRefKindsOpt[argIndex];
if (effectiveRefKind == RefKind.None && argIndex < parameters.Length)
Expand All @@ -1294,20 +1294,20 @@ bool isRefEscape
if (parameters[paramIndex].RefKind == RefKind.RefReadOnly)
{
effectiveRefKind = RefKind.RefReadOnly;
inParametersMatchedWithArgs = inParametersMatchedWithArgs ?? ArrayBuilder<bool>.GetInstance(parameters.Length, fillWithValue: false);
inParametersMatchedWithArgs[paramIndex] = true;
refReadOnlyParametersMatchedWithArgs = refReadOnlyParametersMatchedWithArgs ?? ArrayBuilder<bool>.GetInstance(parameters.Length, fillWithValue: false);
refReadOnlyParametersMatchedWithArgs[paramIndex] = true;
}
}

return effectiveRefKind;
}

/// <summary>
/// Gets an "in" parameter for which there is no argument supplied, if such exists.
/// That indicates an optional "in" parameter. We treat it as an RValue passed by reference via a temporary.
/// Gets a "ref readonly" parameter for which there is no argument supplied, if such exists.
/// That indicates an optional "ref readonly" parameter. We treat it as an RValue passed by reference via a temporary.
/// The effective scope of such variable is the immediately containing scope.
/// </summary>
private static ParameterSymbol TryGetUnmatchedInParameterAndFreeMatchedArgs(ImmutableArray<ParameterSymbol> parameters, ref ArrayBuilder<bool> inParametersMatchedWithArgs)
private static ParameterSymbol TryGetUnmatchedRefReadOnlyParameterAndFreeMatchedArgs(ImmutableArray<ParameterSymbol> parameters, ref ArrayBuilder<bool> refReadOnlyParametersMatchedWithArgs)
{
try
{
Expand All @@ -1322,7 +1322,7 @@ private static ParameterSymbol TryGetUnmatchedInParameterAndFreeMatchedArgs(Immu
}

if (parameter.RefKind == RefKind.RefReadOnly &&
inParametersMatchedWithArgs?[i] != true &&
refReadOnlyParametersMatchedWithArgs?[i] != true &&
parameter.Type.IsByRefLikeType == false)
{
return parameter;
Expand All @@ -1334,9 +1334,9 @@ private static ParameterSymbol TryGetUnmatchedInParameterAndFreeMatchedArgs(Immu
}
finally
{
inParametersMatchedWithArgs?.Free();
refReadOnlyParametersMatchedWithArgs?.Free();
// make sure noone uses it after.
inParametersMatchedWithArgs = null;
refReadOnlyParametersMatchedWithArgs = null;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2577,7 +2577,7 @@ private RefKind GetEffectiveParameterRefKind(ParameterSymbol parameter, RefKind

if (paramRefKind == RefKind.RefReadOnly)
{
// "in" parameters are effectively None for the purpose of overload resolution.
// "ref readonly" parameters are effectively None for the purpose of overload resolution.
paramRefKind = RefKind.None;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ internal static bool IsLegalSpanStackAllocPosition(this SyntaxNode node)
{
Debug.Assert(node != null);

while (node.Parent.IsKind(SyntaxKind.CastExpression))
if (node.Parent.IsKind(SyntaxKind.CastExpression))
{
node = node.Parent;
}

if (node.Parent.IsKind(SyntaxKind.ConditionalExpression))
while (node.Parent.IsKind(SyntaxKind.ConditionalExpression))
{
node = node.Parent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1563,5 +1563,24 @@ .locals init (T V_0)
}");
}

[Fact]
public void RefReadOnlyOptionalParameters()
{
CompileAndVerify(@"
using System;
class Program
{
static void Print(ref readonly int p = 5)
{
Console.Write(p);
}
static void Main()
{
Print();
Console.Write(""-"");
Print(9);
}
}", expectedOutput: "5-9");
}
}
}
Loading

0 comments on commit f99832a

Please sign in to comment.