diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 9815b57177882da5cf942186773e168c457d4ac4..4f3c13969a7cb0a7f2b0f755ed7dece0f33d108f 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -4552,7 +4552,16 @@ private void ParseVariableDeclarators(TypeSyntax type, VariableFlags flags, Sepa (parentKind != SyntaxKind.CompilationUnit || IsScript); LocalFunctionStatementSyntax localFunction; - ParseVariableDeclarators(type, flags, variables, variableDeclarationsExpected, false, default(SyntaxList), out localFunction); + ParseVariableDeclarators( + type, + flags, + variables, + variableDeclarationsExpected, + false, + default(SyntaxList), + default(SyntaxToken), + out localFunction); + Debug.Assert(localFunction == null); } @@ -4563,9 +4572,19 @@ private void ParseVariableDeclarators(TypeSyntax type, VariableFlags flags, Sepa bool variableDeclarationsExpected, bool allowLocalFunctions, SyntaxList mods, + SyntaxToken refTokenOpt, out LocalFunctionStatementSyntax localFunction) { - variables.Add(this.ParseVariableDeclarator(type, flags, isFirst: true, allowLocalFunctions: allowLocalFunctions, mods: mods, localFunction: out localFunction)); + variables.Add( + this.ParseVariableDeclarator( + type, + flags, + isFirst: true, + allowLocalFunctions: allowLocalFunctions, + mods: mods, + refTokenOpt: refTokenOpt, + localFunction: out localFunction)); + if (localFunction != null) { // ParseVariableDeclarator returns null, so it is not added to variables @@ -4582,7 +4601,15 @@ private void ParseVariableDeclarators(TypeSyntax type, VariableFlags flags, Sepa else if (this.CurrentToken.Kind == SyntaxKind.CommaToken) { variables.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - variables.Add(this.ParseVariableDeclarator(type, flags, isFirst: false, allowLocalFunctions: false, mods: mods, localFunction: out localFunction)); + variables.Add( + this.ParseVariableDeclarator( + type, + flags, + isFirst: false, + allowLocalFunctions: false, + mods: mods, + refTokenOpt: refTokenOpt, + localFunction: out localFunction)); } else if (!variableDeclarationsExpected || this.SkipBadVariableListTokens(variables, SyntaxKind.CommaToken) == PostSkipAction.Abort) { @@ -4700,6 +4727,7 @@ private static bool CanReuseVariableDeclarator(CSharp.Syntax.VariableDeclaratorS bool isFirst, bool allowLocalFunctions, SyntaxList mods, + SyntaxToken refTokenOpt, out LocalFunctionStatementSyntax localFunction, bool isExpressionContext = false) { @@ -4835,7 +4863,7 @@ private static bool CanReuseVariableDeclarator(CSharp.Syntax.VariableDeclaratorS case SyntaxKind.LessThanToken: if (allowLocalFunctions && isFirst) { - localFunction = TryParseLocalFunctionStatementBody(mods, parentType, name); + localFunction = TryParseLocalFunctionStatementBody(mods, refTokenOpt, parentType, name); if (localFunction != null) { return null; @@ -4846,7 +4874,7 @@ private static bool CanReuseVariableDeclarator(CSharp.Syntax.VariableDeclaratorS case SyntaxKind.OpenParenToken: if (allowLocalFunctions && isFirst) { - localFunction = TryParseLocalFunctionStatementBody(mods, parentType, name); + localFunction = TryParseLocalFunctionStatementBody(mods, refTokenOpt, parentType, name); if (localFunction != null) { return null; @@ -8100,7 +8128,7 @@ private StatementSyntax ParseLocalDeclarationStatement() TypeSyntax type; LocalFunctionStatementSyntax localFunction; - this.ParseDeclaration(out type, variables, true, mods.ToTokenList(), out localFunction); + this.ParseDeclaration(out type, variables, true, mods.ToTokenList(), refKeyword, out localFunction); if (localFunction != null) { Debug.Assert(variables.Count == 0); @@ -8134,7 +8162,7 @@ private StatementSyntax ParseLocalDeclarationStatement() SeparatedSyntaxListBuilder variables) { LocalFunctionStatementSyntax localFunction; - ParseDeclaration(out type, variables, false, default(SyntaxList), out localFunction); + ParseDeclaration(out type, variables, false, default(SyntaxList), default(SyntaxToken), out localFunction); Debug.Assert(localFunction == null); } @@ -8143,6 +8171,7 @@ private StatementSyntax ParseLocalDeclarationStatement() SeparatedSyntaxListBuilder variables, bool allowLocalFunctions, SyntaxList mods, + SyntaxToken refTokenOpt, out LocalFunctionStatementSyntax localFunction) { type = allowLocalFunctions ? ParseReturnType() : this.ParseType(false); @@ -8161,6 +8190,7 @@ private StatementSyntax ParseLocalDeclarationStatement() variables, variableDeclarationsExpected: true, allowLocalFunctions: allowLocalFunctions, + refTokenOpt: refTokenOpt, mods: mods, localFunction: out localFunction); _termState = saveTerm; @@ -8259,7 +8289,11 @@ private static bool IsAdditionalLocalFunctionModifier(SyntaxKind kind) } } - private LocalFunctionStatementSyntax TryParseLocalFunctionStatementBody(SyntaxList modifiers, TypeSyntax type, SyntaxToken identifier) + private LocalFunctionStatementSyntax TryParseLocalFunctionStatementBody( + SyntaxList modifiers, + SyntaxToken refTokenOpt, + TypeSyntax type, + SyntaxToken identifier) { // This may potentially be an ambiguous parse until very far into the token stream, so we may have to backtrack. // For example, "await x()" is ambiguous at the current point of parsing (right now we're right after the x). @@ -8349,6 +8383,7 @@ private LocalFunctionStatementSyntax TryParseLocalFunctionStatementBody(SyntaxLi var decl = _syntaxFactory.LocalFunctionStatement( modifiers, + refTokenOpt, type, identifier, typeParameterListOpt, diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 1fbfcba9d9cebf3ad87dbd7216a056091e48a171..ae9cc37d053a6ead4ac3af40f370de891443108d 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -14,16 +14,18 @@ Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ExpressionBody Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Modifiers.get -> Microsoft.CodeAnalysis.SyntaxTokenList Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.RefKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.ReturnType.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.TypeParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.Update(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithBody(Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithConstraintClauses(Microsoft.CodeAnalysis.SyntaxList constraintClauses) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithExpressionBody(Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithIdentifier(Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithRefKeyword(Microsoft.CodeAnalysis.SyntaxToken refKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithReturnType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax.WithTypeParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax @@ -35,7 +37,7 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DocumentationComment(params M static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, string identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.LocalFunctionStatement(Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken refKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax returnType, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.XmlCrefAttribute(Microsoft.CodeAnalysis.CSharp.Syntax.CrefSyntax cref) -> Microsoft.CodeAnalysis.CSharp.Syntax.XmlCrefAttributeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.XmlCrefAttribute(Microsoft.CodeAnalysis.CSharp.Syntax.CrefSyntax cref, Microsoft.CodeAnalysis.CSharp.SyntaxKind quoteKind) -> Microsoft.CodeAnalysis.CSharp.Syntax.XmlCrefAttributeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.XmlElement(Microsoft.CodeAnalysis.CSharp.Syntax.XmlNameSyntax name, Microsoft.CodeAnalysis.SyntaxList content) -> Microsoft.CodeAnalysis.CSharp.Syntax.XmlElementSyntax diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 83c10fa91bfbb9b80361a59cb0592b7761f084a2..c292462eed906a98ccd33fafc38f5278ee7ae15d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -21,6 +21,7 @@ internal class LocalFunctionSymbol : MethodSymbol private readonly ImmutableArray _typeParameters; private ImmutableArray _parameters; private ImmutableArray _lazyTypeParameterConstraints; + private readonly RefKind _refKind; private TypeSymbol _returnType; private bool _isVararg; private TypeSymbol _iteratorElementType; @@ -39,6 +40,7 @@ internal class LocalFunctionSymbol : MethodSymbol { _syntax = syntax; _containingSymbol = containingSymbol; + _refKind = syntax.RefKeyword.Kind().GetRefKind(); _declarationModifiers = DeclarationModifiers.Private | @@ -142,7 +144,7 @@ internal override RefKind RefKind { get { - return RefKind.None; + return _refKind; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 0f457902a4ff3641ffb7c555760bc1cf26bf83cf..6d8d35c294dbf9d25a145aec7df021903807ee0b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -17,7 +17,7 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol private readonly TypeSymbol _explicitInterfaceType; private readonly string _name; private readonly bool _isExpressionBodied; - private readonly RefKind refKind; + private readonly RefKind _refKind; private ImmutableArray _lazyExplicitInterfaceImplementations; private ImmutableArray _lazyReturnTypeCustomModifiers; @@ -132,7 +132,7 @@ internal sealed class SourceMemberMethodSymbol : SourceMethodSymbol CheckModifiersForBody(location, diagnostics); } - this.refKind = syntax.RefKeyword.Kind().GetRefKind(); + _refKind = syntax.RefKeyword.Kind().GetRefKind(); var info = ModifierUtils.CheckAccessibility(this.DeclarationModifiers); if (info != null) @@ -576,7 +576,7 @@ public override ImmutableArray Parameters internal override RefKind RefKind { - get { return this.refKind; } + get { return _refKind; } } public override TypeSymbol ReturnType diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 1e24d3d5fc94472de470f6d354cad9f4c007d046..449f0161201b67bee9e8bb4e33c67edcd499680a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1647,6 +1647,9 @@ + + + diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs index 9634cde5b047d5c3d2b6256848c1d1194d3087d5..bcdf003b7439176215380481c6f19a751365837e 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefReturnTests.cs @@ -1486,5 +1486,191 @@ .maxstack 4 IL_00a7: ret }"); } + + [Fact] + public void RefReturnArrayAccessNested() + { + var text = @" +class Program +{ + static ref int M() + { + ref int N() + { + return ref (new int[1])[0]; + } + + return ref N(); + } +} +"; + + CompileAndVerifyExperimental(text).VerifyIL("Program.M()", @" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: call ""ref int Program.g__N0_0()"" + IL_0005: ret +}").VerifyIL("Program.g__N0_0", @" +{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldc.i4.1 + IL_0001: newarr ""int"" + IL_0006: ldc.i4.0 + IL_0007: ldelema ""int"" + IL_000c: ret +}"); + } + + [Fact] + public void RefReturnArrayAccessNested1() + { + var text = @" +class Program +{ + static ref int M() + { + var arr = new int[1]{40}; + + ref int N() + { + ref int NN(ref int arg) => ref arg; + + ref var r = ref NN(ref arr[0]); + r += 2; + + return ref r; + } + + return ref N(); + } + + static void Main() + { + System.Console.WriteLine(M()); + } +} +"; + + CompileAndVerifyExperimental(text, expectedOutput: "42", verify: false).VerifyIL("Program.M()", @" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (Program.<>c__DisplayClass0_0 V_0) //CS$<>8__locals0 + IL_0000: ldloca.s V_0 + IL_0002: initobj ""Program.<>c__DisplayClass0_0"" + IL_0008: ldloca.s V_0 + IL_000a: ldc.i4.1 + IL_000b: newarr ""int"" + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: ldc.i4.s 40 + IL_0014: stelem.i4 + IL_0015: stfld ""int[] Program.<>c__DisplayClass0_0.arr"" + IL_001a: ldloca.s V_0 + IL_001c: call ""ref int Program.g__N0_0(ref Program.<>c__DisplayClass0_0)"" + IL_0021: ret +}").VerifyIL("Program.g__N0_0", @" +{ + // Code size 24 (0x18) + .maxstack 4 + IL_0000: ldarg.0 + IL_0001: ldfld ""int[] Program.<>c__DisplayClass0_0.arr"" + IL_0006: ldc.i4.0 + IL_0007: ldelema ""int"" + IL_000c: call ""ref int Program.g__NN0_1(ref int)"" + IL_0011: dup + IL_0012: dup + IL_0013: ldind.i4 + IL_0014: ldc.i4.2 + IL_0015: add + IL_0016: stind.i4 + IL_0017: ret +}").VerifyIL("Program.g__NN0_1", @" +{ + // Code size 2 (0x2) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ret +}"); + } + + [Fact] + public void RefReturnArrayAccessNested2() + { + var text = @" +class Program +{ + delegate ref int D(); + + static D M() + { + var arr = new int[1]{40}; + + ref int N() + { + ref int NN(ref int arg) => ref arg; + + ref var r = ref NN(ref arr[0]); + r += 2; + + return ref r; + } + + return N; + } + + static void Main() + { + System.Console.WriteLine(M()()); + } +} +"; + + CompileAndVerifyExperimental(text, expectedOutput: "42", verify: false).VerifyIL("Program.M()", @" +{ + // Code size 36 (0x24) + .maxstack 5 + .locals init (Program.<>c__DisplayClass1_0 V_0) //CS$<>8__locals0 + IL_0000: newobj ""Program.<>c__DisplayClass1_0..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldc.i4.1 + IL_0008: newarr ""int"" + IL_000d: dup + IL_000e: ldc.i4.0 + IL_000f: ldc.i4.s 40 + IL_0011: stelem.i4 + IL_0012: stfld ""int[] Program.<>c__DisplayClass1_0.arr"" + IL_0017: ldloc.0 + IL_0018: ldftn ""ref int Program.<>c__DisplayClass1_0.g__N0()"" + IL_001e: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_0023: ret +}").VerifyIL("Program.<>c__DisplayClass1_0.g__N0()", @" +{ + // Code size 24 (0x18) + .maxstack 4 + IL_0000: ldarg.0 + IL_0001: ldfld ""int[] Program.<>c__DisplayClass1_0.arr"" + IL_0006: ldc.i4.0 + IL_0007: ldelema ""int"" + IL_000c: call ""ref int Program.g__NN1_1(ref int)"" + IL_0011: dup + IL_0012: dup + IL_0013: ldind.i4 + IL_0014: ldc.i4.2 + IL_0015: add + IL_0016: stind.i4 + IL_0017: ret +}").VerifyIL("Program.g__NN1_1(ref int)", @" +{ + // Code size 2 (0x2) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: ret +}"); + } + } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs index 218423b31e176e0ce25e0435c67d2233ba9f5831..92e37329808b24aee9555d101e4df2688d23f830 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturns.cs @@ -804,5 +804,111 @@ public struct S1 ); } + [Fact] + public void RefReturnNested() + { + var text = @" +public class Test +{ + public static void Main() + { + ref char Foo(ref char a, ref char b) + { + // valid + return ref a; + } + + char Foo1(ref char a, ref char b) + { + return ref b; + } + + ref char Foo2(ref char c, ref char b) + { + return c; + } + } +}"; + var comp = CreateExperimentalCompilationWithMscorlib45(text); + comp.VerifyDiagnostics( + // (14,13): error CS8894: By-reference returns may only be used in methods that return by reference + // return ref b; + Diagnostic(ErrorCode.ERR_MustNotHaveRefReturn, "return").WithLocation(14, 13), + // (19,13): error CS8895: By-value returns may only be used in methods that return by value + // return c; + Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "return").WithLocation(19, 13), + // (6,18): warning CS0168: The variable 'Foo' is declared but never used + // ref char Foo(ref char a, ref char b) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Foo").WithArguments("Foo").WithLocation(6, 18), + // (12,14): warning CS0168: The variable 'Foo1' is declared but never used + // char Foo1(ref char a, ref char b) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Foo1").WithArguments("Foo1").WithLocation(12, 14), + // (17,18): warning CS0168: The variable 'Foo2' is declared but never used + // ref char Foo2(ref char c, ref char b) + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Foo2").WithArguments("Foo2").WithLocation(17, 18) + + ); + } + + [Fact] + public void RefReturnNestedArrow() + { + var text = @" +public class Test +{ + public static void Main() + { + // valid + ref char Foo(ref char a, ref char b) => ref a; + + char Foo1(ref char a, ref char b) => ref b; + + ref char Foo2(ref char c, ref char b) => c; + + var arr = new int[1]; + ref var r = ref arr[0]; + + ref char Moo1(ref char a, ref char b) => ref r; + char Moo3(ref char a, ref char b) => r; + } +}"; + var comp = CreateExperimentalCompilationWithMscorlib45(text); + comp.VerifyDiagnostics( + // (9,50): error CS8894: By-reference returns may only be used in methods that return by reference + // char Foo1(ref char a, ref char b) => ref b; + Diagnostic(ErrorCode.ERR_MustNotHaveRefReturn, "b").WithLocation(9, 50), + // (11,50): error CS8895: By-value returns may only be used in methods that return by value + // ref char Foo2(ref char c, ref char b) => c; + Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "c").WithLocation(11, 50), + // (16,54): error CS8940: Cannot use ref local 'r' inside an anonymous method, lambda expression, or query expression + // ref char Moo1(ref char a, ref char b) => ref r; + Diagnostic(ErrorCode.ERR_AnonDelegateCantUseLocal, "r").WithArguments("r").WithLocation(16, 54), + // (16,54): error CS8896: The return expression must be of type 'char' because this method returns by reference + // ref char Moo1(ref char a, ref char b) => ref r; + Diagnostic(ErrorCode.ERR_RefReturnMustHaveIdentityConversion, "r").WithArguments("char").WithLocation(16, 54), + // (17,46): error CS8940: Cannot use ref local 'r' inside an anonymous method, lambda expression, or query expression + // char Moo3(ref char a, ref char b) => r; + Diagnostic(ErrorCode.ERR_AnonDelegateCantUseLocal, "r").WithArguments("r").WithLocation(17, 46), + // (17,46): error CS0266: Cannot implicitly convert type 'int' to 'char'. An explicit conversion exists (are you missing a cast?) + // char Moo3(ref char a, ref char b) => r; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "r").WithArguments("int", "char").WithLocation(17, 46), + // (7,18): warning CS0168: The variable 'Foo' is declared but never used + // ref char Foo(ref char a, ref char b) => ref a; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Foo").WithArguments("Foo").WithLocation(7, 18), + // (9,14): warning CS0168: The variable 'Foo1' is declared but never used + // char Foo1(ref char a, ref char b) => ref b; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Foo1").WithArguments("Foo1").WithLocation(9, 14), + // (11,18): warning CS0168: The variable 'Foo2' is declared but never used + // ref char Foo2(ref char c, ref char b) => c; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Foo2").WithArguments("Foo2").WithLocation(11, 18), + // (16,18): warning CS0168: The variable 'Moo1' is declared but never used + // ref char Moo1(ref char a, ref char b) => ref r; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Moo1").WithArguments("Moo1").WithLocation(16, 18), + // (17,14): warning CS0168: The variable 'Moo3' is declared but never used + // char Moo3(ref char a, ref char b) => r; + Diagnostic(ErrorCode.WRN_UnreferencedVar, "Moo3").WithArguments("Moo3").WithLocation(17, 14) + ); + } + } }