Skip to content

Commit

Permalink
Merge pull request #18419 from VSadov/fix18357
Browse files Browse the repository at this point in the history
"ref readonly" local functions should be treated as "ref readonly"
  • Loading branch information
VSadov authored Apr 5, 2017
2 parents 828a6de + 9a60037 commit 6dd756f
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ internal sealed class LocalFunctionSymbol : MethodSymbol
private readonly Symbol _containingSymbol;
private readonly DeclarationModifiers _declarationModifiers;
private readonly ImmutableArray<LocalFunctionTypeParameterSymbol> _typeParameters;
private readonly RefKind _refKind;

private ImmutableArray<ParameterSymbol> _lazyParameters;
private bool _lazyIsVarArg;
private ImmutableArray<TypeParameterConstraintClause> _lazyTypeParameterConstraints;
private TypeSymbol _lazyReturnType;
private RefKind _lazyRefKind;
private TypeSymbol _iteratorElementType;

// Lock for initializing lazy fields and registering their diagnostics
Expand Down Expand Up @@ -73,7 +73,6 @@ public LocalFunctionSymbol(
}

_binder = binder;
_refKind = (syntax.ReturnType.Kind() == SyntaxKind.RefType) ? RefKind.Ref : RefKind.None;
}

/// <summary>
Expand Down Expand Up @@ -184,7 +183,8 @@ internal override RefKind RefKind
{
get
{
return _refKind;
ComputeReturnType();
return _lazyRefKind;
}
}

Expand All @@ -196,8 +196,7 @@ internal void ComputeReturnType()
}

var diagnostics = DiagnosticBag.GetInstance();
RefKind refKind;
TypeSyntax returnTypeSyntax = _syntax.ReturnType.SkipRef(out refKind);
TypeSyntax returnTypeSyntax = _syntax.ReturnType.SkipRef(out _lazyRefKind);
TypeSymbol returnType = _binder.BindType(returnTypeSyntax, diagnostics);
if (IsAsync &&
returnType.SpecialType != SpecialType.System_Void &&
Expand All @@ -208,7 +207,7 @@ internal void ComputeReturnType()
diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, this.Locations[0]);
}

Debug.Assert(refKind == RefKind.None
Debug.Assert(_lazyRefKind == RefKind.None
|| returnType.SpecialType != SpecialType.System_Void
|| returnTypeSyntax.HasErrors);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private SourceMemberMethodSymbol(

bool hasBlockBody = syntax.Body != null;
_isExpressionBodied = !hasBlockBody && syntax.ExpressionBody != null;
syntax.ReturnType.SkipRef(out _refKind);
_refKind = syntax.ReturnType.GetRefKind();

if (hasBlockBody || _isExpressionBodied)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private SourcePropertySymbol(
_location = location;
_containingType = containingType;
_syntaxRef = syntax.GetReference();
syntax.Type.SkipRef(out _refKind);
_refKind = syntax.Type.GetRefKind();

SyntaxTokenList modifiers = syntax.Modifiers;
bodyBinder = bodyBinder.WithUnsafeRegionIfNecessary(modifiers);
Expand Down
8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ internal static SyntaxToken ExtractAnonymousTypeMemberName(this ExpressionSyntax
}
}

internal static RefKind GetRefKind(this TypeSyntax syntax)
{
syntax.SkipRef(out var refKind);
return refKind;
}

internal static TypeSyntax SkipRef(this TypeSyntax syntax, out RefKind refKind)
{
refKind = RefKind.None;
Expand All @@ -158,7 +164,7 @@ internal static TypeSyntax SkipRef(this TypeSyntax syntax, out RefKind refKind)
RefKind.RefReadOnly :
RefKind.Ref;

syntax = ((RefTypeSyntax)syntax).Type;
syntax = refType.Type;
}

return syntax;
Expand Down
81 changes: 81 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,87 @@ .maxstack 1
}");
}

[Fact, WorkItem(18357, "https://github.com/dotnet/roslyn/issues/18357")]
public void ReadonlyParamCanReturnByRefReadonlyNested()
{
var text = @"
class Program
{
static ref readonly int M(in int arg1, in (int Alice, int Bob) arg2)
{
ref readonly int M1(in int arg11, in (int Alice, int Bob) arg21)
{
bool b = true;
if (b)
{
return ref arg11;
}
else
{
return ref arg21.Alice;
}
}
return ref M1(arg1, arg2);
}
}
";

var comp = CompileAndVerify(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular, verify: false);

comp.VerifyIL("Program.<M>g__M10_0(in int, in (int Alice, int Bob))", @"
{
// Code size 12 (0xc)
.maxstack 1
IL_0000: ldc.i4.1
IL_0001: brfalse.s IL_0005
IL_0003: ldarg.0
IL_0004: ret
IL_0005: ldarg.1
IL_0006: ldflda ""int System.ValueTuple<int, int>.Item1""
IL_000b: ret
}");
}

[Fact, WorkItem(18357, "https://github.com/dotnet/roslyn/issues/18357")]
public void ReadonlyParamCannotReturnByRefNested()
{
var text = @"
class Program
{
static ref readonly int M(in int arg1, in (int Alice, int Bob) arg2)
{
ref int M1(in int arg11, in (int Alice, int Bob) arg21)
{
bool b = true;
if (b)
{
return ref arg11;
}
else
{
return ref arg21.Alice;
}
}
return ref M1(arg1, arg2);
}
}
";

var comp = CreateCompilationWithMscorlib45(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef });
comp.VerifyDiagnostics(
// (12,28): error CS8406: Cannot use variable 'in int' as a ref or out value because it is a readonly variable
// return ref arg11;
Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "arg11").WithArguments("variable", "in int").WithLocation(12, 28),
// (16,28): error CS8407: Members of variable 'in (int Alice, int Bob)' cannot be used as a ref or out value because it is a readonly variable
// return ref arg21.Alice;
Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "arg21.Alice").WithArguments("variable", "in (int Alice, int Bob)").WithLocation(16, 28)
);
}

[Fact]
public void ReadonlyParamOptional()
{
Expand Down

0 comments on commit 6dd756f

Please sign in to comment.