From: Marek Safar Date: Tue, 19 Sep 2017 13:47:31 +0000 (+0200) Subject: [mcs] Initial by ref returns and variables support X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=mono.git;a=commitdiff_plain;h=34866ac4c20c781f10c40fe6f6fe0c39733fd946 [mcs] Initial by ref returns and variables support --- diff --git a/mcs/errors/cs0131-6.cs b/mcs/errors/cs0131-6.cs new file mode 100644 index 00000000000..8af3e939fbf --- /dev/null +++ b/mcs/errors/cs0131-6.cs @@ -0,0 +1,15 @@ +// CS0131: The left-hand side of an assignment must be a variable, a property or an indexer +// Line: 8 + +class X +{ + void Test () + { + Foo () = 1; + } + + static int Foo () + { + return 1; + } +} \ No newline at end of file diff --git a/mcs/errors/cs0199-2.cs b/mcs/errors/cs0199-2.cs new file mode 100644 index 00000000000..efad7c89367 --- /dev/null +++ b/mcs/errors/cs0199-2.cs @@ -0,0 +1,12 @@ +// CS0199: A static readonly field `X.f' cannot be passed ref or out (except in a static constructor) +// Line: 10 + +class X +{ + static readonly int f = 0; + + public static void Main () + { + ref int j = ref f; + } +} \ No newline at end of file diff --git a/mcs/errors/cs0206-5.cs b/mcs/errors/cs0206-5.cs new file mode 100644 index 00000000000..bd9691ffbc5 --- /dev/null +++ b/mcs/errors/cs0206-5.cs @@ -0,0 +1,12 @@ +// CS0206: A property, indexer or dynamic member access may not be passed as `ref' or `out' parameter +// Line: 10 + +class X +{ + static int P { get; set; } + + static void Main () + { + ref int rl = ref P; + } +} \ No newline at end of file diff --git a/mcs/errors/cs1503-17.cs b/mcs/errors/cs1503-17.cs new file mode 100644 index 00000000000..23921edc939 --- /dev/null +++ b/mcs/errors/cs1503-17.cs @@ -0,0 +1,31 @@ +// CS1503: Argument `#1' cannot convert `ref long' expression to type `ref int' +// Line: 18 + +using System; + +class X +{ + long field; + + static void Main () + { + var x = new X (); + x.Run (); + } + + void Run () + { + Test (ref Prop); + } + + static int Test (ref int y) + { + return y; + } + + ref long Prop { + get { + return ref field; + } + } +} diff --git a/mcs/errors/cs1547-13.cs b/mcs/errors/cs1547-13.cs new file mode 100644 index 00000000000..1b3f8e1c14b --- /dev/null +++ b/mcs/errors/cs1547-13.cs @@ -0,0 +1,7 @@ +// CS1547: Keyword `void' cannot be used in this context +// Line: 6 + +interface IA +{ + ref void Foo (); +} diff --git a/mcs/errors/cs1644-55.cs b/mcs/errors/cs1644-55.cs new file mode 100644 index 00000000000..58384a6f289 --- /dev/null +++ b/mcs/errors/cs1644-55.cs @@ -0,0 +1,11 @@ +// CS1644: Feature `byref locals and returns' cannot be used because it is not part of the C# 6.0 language specification +// Line: 9 +// Compiler options: -langversion:6 + +class Text +{ + static ref long Foo () + { + throw new System.NotImplementedException (); + } +} diff --git a/mcs/errors/cs1715-3.cs b/mcs/errors/cs1715-3.cs new file mode 100644 index 00000000000..d3cd5dab0f7 --- /dev/null +++ b/mcs/errors/cs1715-3.cs @@ -0,0 +1,16 @@ +// CS1715: `B.Foo': type must be `int' to match overridden member `A.Foo' +// Line: 11 + +public abstract class A +{ + public abstract ref int Foo { get; } +} + +public class B : A +{ + public override ref long Foo { + get { + throw null; + } + } +} \ No newline at end of file diff --git a/mcs/errors/cs1764.cs b/mcs/errors/cs1764.cs index f8a89b25659..a1dd2e36d15 100644 --- a/mcs/errors/cs1764.cs +++ b/mcs/errors/cs1764.cs @@ -1,4 +1,4 @@ -// CS1764: Cannot use fixed local `p' inside an anonymous method, lambda expression or query expression +// CS1764: Cannot use fixed variable `p' inside an anonymous method, lambda expression or query expression // Line: 10 // Compiler options: -unsafe diff --git a/mcs/errors/cs8145.cs b/mcs/errors/cs8145.cs new file mode 100644 index 00000000000..cf3c5084722 --- /dev/null +++ b/mcs/errors/cs8145.cs @@ -0,0 +1,7 @@ +// CS8145: Auto-implemented properties cannot return by reference +// Line: 6 + +public class X +{ + ref string TestProp { get; } +} \ No newline at end of file diff --git a/mcs/errors/cs8146.cs b/mcs/errors/cs8146.cs new file mode 100644 index 00000000000..758804c1df6 --- /dev/null +++ b/mcs/errors/cs8146.cs @@ -0,0 +1,7 @@ +// CS8146: `X.TestProp': property and indexer which return by reference must have a get accessor +// Line: 6 + +public class X +{ + ref string TestProp { set; } +} \ No newline at end of file diff --git a/mcs/errors/cs8147-2.cs b/mcs/errors/cs8147-2.cs new file mode 100644 index 00000000000..a4375d2d1a5 --- /dev/null +++ b/mcs/errors/cs8147-2.cs @@ -0,0 +1,14 @@ +// CS8147: `X.this[int]': property and indexer which return by reference cannot have set accessors +// Line: 6 + +public class X +{ + ref string this [int arg] { + set { + + } + get { + + } + } +} \ No newline at end of file diff --git a/mcs/errors/cs8147.cs b/mcs/errors/cs8147.cs new file mode 100644 index 00000000000..a006a4d9727 --- /dev/null +++ b/mcs/errors/cs8147.cs @@ -0,0 +1,14 @@ +// CS8147: `X.TestProp': property and indexer which return by reference cannot have set accessors +// Line: 6 + +public class X +{ + ref string TestProp { + set { + + } + get { + + } + } +} \ No newline at end of file diff --git a/mcs/errors/cs8148-2.cs b/mcs/errors/cs8148-2.cs new file mode 100644 index 00000000000..1961026d0d2 --- /dev/null +++ b/mcs/errors/cs8148-2.cs @@ -0,0 +1,16 @@ +// CS8148: `B.Foo': must return by reference to match overridden member `A.Foo' +// Line: 11 + +public abstract class A +{ + public abstract ref int Foo { get; } +} + +public class B : A +{ + public override long Foo { + get { + throw null; + } + } +} \ No newline at end of file diff --git a/mcs/errors/cs8148.cs b/mcs/errors/cs8148.cs new file mode 100644 index 00000000000..1e9178945c0 --- /dev/null +++ b/mcs/errors/cs8148.cs @@ -0,0 +1,15 @@ +// CS8148: `B.Foo()': must not return by reference to match overridden member `A.Foo()' +// Line: 11 + +public abstract class A +{ + public abstract int Foo (); +} + +public class B : A +{ + public override ref int Foo () + { + + } +} \ No newline at end of file diff --git a/mcs/errors/cs8149-2.cs b/mcs/errors/cs8149-2.cs new file mode 100644 index 00000000000..3514769c702 --- /dev/null +++ b/mcs/errors/cs8149-2.cs @@ -0,0 +1,14 @@ +// CS8149: By-reference returns can only be used in lambda expressions that return by reference +// Line: 12 + +using System; + +class A +{ + int p; + + void Test () + { + Action a = () => ref p; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8149.cs b/mcs/errors/cs8149.cs new file mode 100644 index 00000000000..351dd54235b --- /dev/null +++ b/mcs/errors/cs8149.cs @@ -0,0 +1,12 @@ +// CS8149: By-reference returns can only be used in methods that return by reference +// Line: 10 + +class A +{ + int p; + + int Test () + { + return ref p; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8150.cs b/mcs/errors/cs8150.cs new file mode 100644 index 00000000000..fd4ef7a2db0 --- /dev/null +++ b/mcs/errors/cs8150.cs @@ -0,0 +1,12 @@ +// CS8150: By-reference return is required when method returns by reference +// Line: 10 + +class A +{ + int p; + + ref int Test () + { + return p; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8151.cs b/mcs/errors/cs8151.cs new file mode 100644 index 00000000000..934faefff7c --- /dev/null +++ b/mcs/errors/cs8151.cs @@ -0,0 +1,12 @@ +// CS8151: The return by reference expression must be of type `string' because this method returns by reference +// Line: 10 + +public class X +{ + int field; + + ref string TestMethod () + { + return ref field; + } +} diff --git a/mcs/errors/cs8152.cs b/mcs/errors/cs8152.cs new file mode 100644 index 00000000000..45546ad9d9a --- /dev/null +++ b/mcs/errors/cs8152.cs @@ -0,0 +1,14 @@ +// CS8152: `C' does not implement interface member `IA.Foo()' and the best implementing candidate `C.Foo()' return type `void' does not return by reference +// Line: 11 + +interface IA +{ + ref char Foo (); +} + +public class C : IA +{ + public void Foo () + { + } +} diff --git a/mcs/errors/cs8153.cs b/mcs/errors/cs8153.cs new file mode 100644 index 00000000000..d36f110208f --- /dev/null +++ b/mcs/errors/cs8153.cs @@ -0,0 +1,24 @@ +// CS8153: An expression tree lambda cannot contain a call to a method, property, or indexer that returns by reference +// Line: 11 + +using System; +using System.Linq.Expressions; + +class X +{ + void Foo () + { + Expression> e = () => Test (ref this[0]); + } + + static int Test (ref int y) + { + return y; + } + + ref int this [int y] { + get { + throw null; + } + } +} diff --git a/mcs/errors/cs8154.cs b/mcs/errors/cs8154.cs new file mode 100644 index 00000000000..ed98513e65e --- /dev/null +++ b/mcs/errors/cs8154.cs @@ -0,0 +1,12 @@ +// CS8154: The body of `TestClass.TestFunction()' cannot be an iterator block because the method returns by reference +// Line: 10 + +class TestClass +{ + int x; + + ref int TestFunction() + { + yield return x; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8155.cs b/mcs/errors/cs8155.cs new file mode 100644 index 00000000000..45d8b4e414a --- /dev/null +++ b/mcs/errors/cs8155.cs @@ -0,0 +1,16 @@ +// CS8155: Lambda expressions that return by reference cannot be converted to expression trees +// Line: 14 + +using System.Linq.Expressions; + +class TestClass +{ + static int x; + + delegate ref int D (); + + static void Main () + { + Expression e = () => ref x; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8156-2.cs b/mcs/errors/cs8156-2.cs new file mode 100644 index 00000000000..7507ec475c3 --- /dev/null +++ b/mcs/errors/cs8156-2.cs @@ -0,0 +1,16 @@ +// CS8156: An expression cannot be used in this context because it may not be returned by reference +// Line: 8 + +class X +{ + int Prop { + get { + return 1; + } + } + + ref int Test () + { + return ref Prop; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8156.cs b/mcs/errors/cs8156.cs new file mode 100644 index 00000000000..fe3f6b7c987 --- /dev/null +++ b/mcs/errors/cs8156.cs @@ -0,0 +1,10 @@ +// CS8156: An expression cannot be used in this context because it may not be returned by reference +// Line: 8 + +class Test +{ + ref int Foo () + { + return ref 2; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8157.cs b/mcs/errors/cs8157.cs new file mode 100644 index 00000000000..35d700f19c4 --- /dev/null +++ b/mcs/errors/cs8157.cs @@ -0,0 +1,13 @@ +// CS8157: Cannot return `r' by reference because it was initialized to a value that cannot be returned by reference +// Line: 11 + +struct S +{ + int i; + + ref int M () + { + ref int r = ref i; + return ref r; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8160.cs b/mcs/errors/cs8160.cs new file mode 100644 index 00000000000..2b4ea42c5ab --- /dev/null +++ b/mcs/errors/cs8160.cs @@ -0,0 +1,12 @@ +// CS8160: A readonly field cannot be returned by reference +// Line: 10 + +class X +{ + readonly int f = 0; + + ref int Test () + { + return ref f; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8161.cs b/mcs/errors/cs8161.cs new file mode 100644 index 00000000000..0d028a116ed --- /dev/null +++ b/mcs/errors/cs8161.cs @@ -0,0 +1,12 @@ +// CS8161: A static readonly field cannot be returned by reference +// Line: 10 + +class X +{ + static readonly int f; + + static ref int Test () + { + return ref f; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8170-2.cs b/mcs/errors/cs8170-2.cs new file mode 100644 index 00000000000..dbd76a4da36 --- /dev/null +++ b/mcs/errors/cs8170-2.cs @@ -0,0 +1,12 @@ +// CS8170: +// Line: 10 + +public struct S +{ + int f; + + public ref S Foo () + { + return ref f; + } +} diff --git a/mcs/errors/cs8170.cs b/mcs/errors/cs8170.cs new file mode 100644 index 00000000000..142e1a1943f --- /dev/null +++ b/mcs/errors/cs8170.cs @@ -0,0 +1,10 @@ +// CS8170: +// Line: 8 + +public struct S +{ + public ref S Foo () + { + return ref this; + } +} diff --git a/mcs/errors/cs8171.cs b/mcs/errors/cs8171.cs new file mode 100644 index 00000000000..36eae2c65a4 --- /dev/null +++ b/mcs/errors/cs8171.cs @@ -0,0 +1,12 @@ +// CS8171: Cannot initialize a by-value variable `l' with a reference expression +// Line: 10 + +class Test +{ + int field; + + void Foo () + { + int l = ref field; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8172.cs b/mcs/errors/cs8172.cs new file mode 100644 index 00000000000..7ddd3833e8c --- /dev/null +++ b/mcs/errors/cs8172.cs @@ -0,0 +1,12 @@ +// CS8172: Cannot initialize a by-reference variable `j' with a value +// Line: 10 + +class X +{ + static int f; + + public static void Main () + { + ref int j = f; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8173.cs b/mcs/errors/cs8173.cs new file mode 100644 index 00000000000..7c7d894ad6e --- /dev/null +++ b/mcs/errors/cs8173.cs @@ -0,0 +1,13 @@ +// CS8173: The expression must be of type `long' because it is being assigned by reference +// Line: 11 + +public class X +{ + int field; + + public static void Main () + { + int i = 5; + ref long j = ref i; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8174.cs b/mcs/errors/cs8174.cs new file mode 100644 index 00000000000..888711a0626 --- /dev/null +++ b/mcs/errors/cs8174.cs @@ -0,0 +1,10 @@ +// CS8174: A declaration of a by-reference variable must have an initializer +// Line: 8 + +class X +{ + public static void Main () + { + ref int j; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8175.cs b/mcs/errors/cs8175.cs new file mode 100644 index 00000000000..4ef39db696d --- /dev/null +++ b/mcs/errors/cs8175.cs @@ -0,0 +1,17 @@ +// CS8175: Cannot use by-reference variable `v' inside an anonymous method, lambda expression, or query expression +// Line: 14 + +using System; + +public class Test +{ + public static void Main() + { + var arr = new int [1]; + ref var v = ref arr [0]; + + Action a = delegate { + ref var v2 = ref v; + }; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8176.cs b/mcs/errors/cs8176.cs new file mode 100644 index 00000000000..514c0b2b554 --- /dev/null +++ b/mcs/errors/cs8176.cs @@ -0,0 +1,15 @@ +// CS8176: Iterators cannot use by-reference variables +// Line: 12 + +using System.Collections.Generic; + +class X +{ + int x; + + IEnumerable Test () + { + ref int y = ref x; + yield break; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8177.cs b/mcs/errors/cs8177.cs new file mode 100644 index 00000000000..414ece1b4be --- /dev/null +++ b/mcs/errors/cs8177.cs @@ -0,0 +1,15 @@ +// CS8177: Async methods cannot use by-reference variables +// Line: 12 + +using System.Threading.Tasks; + +class X +{ + int x; + + async Task Test () + { + ref int y = ref x; + await Task.Yield (); + } +} \ No newline at end of file diff --git a/mcs/errors/cs8178-2.cs b/mcs/errors/cs8178-2.cs new file mode 100644 index 00000000000..e61b7d9eb5c --- /dev/null +++ b/mcs/errors/cs8178-2.cs @@ -0,0 +1,20 @@ +// CS8178: `await' cannot be used in an expression containing a call to `X.this[int]' because it returns by reference +// Line: 12 + +using System.Threading.Tasks; + +class X +{ + int x; + + async Task Test () + { + Foo (ref this [await Task.FromResult (1)]); + } + + ref int this [int arg] => ref x; + + static void Foo (ref int arg) + { + } +} \ No newline at end of file diff --git a/mcs/errors/cs8178.cs b/mcs/errors/cs8178.cs new file mode 100644 index 00000000000..6f779621570 --- /dev/null +++ b/mcs/errors/cs8178.cs @@ -0,0 +1,24 @@ +// CS8178: `await' cannot be used in an expression containing a call to `X.Wrap(int)' because it returns by reference +// Line: 12 + +using System.Threading.Tasks; + +class X +{ + int x; + + async Task Test () + { + Foo (ref Wrap (await Task.FromResult (1))) = 4; + } + + ref int Wrap (int arg) + { + return ref x; + } + + static ref int Foo (ref int arg) + { + return ref arg; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8189.cs b/mcs/errors/cs8189.cs new file mode 100644 index 00000000000..909ec3b4fcf --- /dev/null +++ b/mcs/errors/cs8189.cs @@ -0,0 +1,17 @@ +// CS8189: By reference return delegate does not match `C.D()' return type +// Line: 15 + +class C +{ + delegate ref int D (); + + static int M () + { + return 1; + } + + static void Main () + { + D d = new D (M); + } +} \ No newline at end of file diff --git a/mcs/errors/known-issues-net_4_x b/mcs/errors/known-issues-net_4_x index 40f81ded970..c9ed9317350 100644 --- a/mcs/errors/known-issues-net_4_x +++ b/mcs/errors/known-issues-net_4_x @@ -22,3 +22,8 @@ cs8129.cs NO ERROR cs8141.cs cs8141-2.cs cs8144.cs +cs8157.cs NO ERROR +cs8160.cs +cs8161.cs +cs8170.cs NO ERROR +cs8170-2.cs diff --git a/mcs/mcs/argument.cs b/mcs/mcs/argument.cs index 00fd56d8a64..8421b4dfbfc 100644 --- a/mcs/mcs/argument.cs +++ b/mcs/mcs/argument.cs @@ -104,12 +104,17 @@ namespace Mono.CSharp return Clone (Expr.Clone (clonectx)); } - public virtual Expression CreateExpressionTree (ResolveContext ec) + public virtual Expression CreateExpressionTree (ResolveContext rc) { + if (Type.Kind == MemberKind.ByRef) { + rc.Report.Error (8153, Expr.Location, "An expression tree lambda cannot contain a call to a method, property, or indexer that returns by reference"); + return null; + } + if (ArgType == AType.Default) - ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter"); + rc.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter"); - return Expr.CreateExpressionTree (ec); + return Expr.CreateExpressionTree (rc); } @@ -126,12 +131,16 @@ namespace Mono.CSharp return; } + if (Expr.Type.Kind == MemberKind.ByRef) { + Expr.Emit (ec); + return; + } + AddressOp mode = AddressOp.Store; if (ArgType == AType.Ref) mode |= AddressOp.Load; - IMemoryLocation ml = (IMemoryLocation) Expr; - ml.AddressOf (ec, mode); + ((IMemoryLocation)Expr).AddressOf (ec, mode); } public Argument EmitToField (EmitContext ec, bool cloneResult) @@ -421,17 +430,19 @@ namespace Mono.CSharp return all; } - public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e) + public static Arguments CreateForExpressionTree (ResolveContext rc, Arguments args, params Expression[] e) { Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length); for (int i = 0; i < e.Length; ++i) { - if (e [i] != null) - all.Add (new Argument (e[i])); + var expr = e [i]; + if (expr != null) { + all.Add (new Argument (expr)); + } } if (args != null) { foreach (Argument a in args.args) { - Expression tree_arg = a.CreateExpressionTree (ec); + Expression tree_arg = a.CreateExpressionTree (rc); if (tree_arg != null) all.Add (new Argument (tree_arg)); } diff --git a/mcs/mcs/class.cs b/mcs/mcs/class.cs index 48b1c6921f3..07bf45f12ef 100644 --- a/mcs/mcs/class.cs +++ b/mcs/mcs/class.cs @@ -3527,7 +3527,10 @@ namespace Mono.CSharp var base_member_type = ((IInterfaceMemberSpec)base_member).MemberType; if (!TypeSpecComparer.Override.IsEqual (MemberType, base_member_type)) { Report.SymbolRelatedToPreviousError (base_member); - if (this is PropertyBasedMember) { + if (((base_member_type.Kind ^ MemberType.Kind) & MemberKind.ByRef) != 0) { + Report.Error (8148, Location, "`{0}': must {2}return by reference to match overridden member `{1}'", + GetSignatureForError (), base_member.GetSignatureForError (), base_member_type.Kind == MemberKind.ByRef ? "" : "not "); + } else if (this is PropertyBasedMember) { Report.Error (1715, Location, "`{0}': type must be `{1}' to match overridden member `{2}'", GetSignatureForError (), base_member_type.GetSignatureForError (), base_member.GetSignatureForError ()); } else { diff --git a/mcs/mcs/convert.cs b/mcs/mcs/convert.cs index 8fe0e2026ca..b11477c1043 100644 --- a/mcs/mcs/convert.cs +++ b/mcs/mcs/convert.cs @@ -1503,6 +1503,11 @@ namespace Mono.CSharp { return null; } + if (expr is ReferenceExpression) { + // Only identify conversion is allowed + return null; + } + e = ImplicitNumericConversion (expr, expr_type, target_type); if (e != null) return e; diff --git a/mcs/mcs/cs-parser.jay b/mcs/mcs/cs-parser.jay index 5f885a77763..648effd7f10 100644 --- a/mcs/mcs/cs-parser.jay +++ b/mcs/mcs/cs-parser.jay @@ -1161,7 +1161,7 @@ constant_initializer_expr field_declaration : opt_attributes opt_modifiers - member_type IDENTIFIER + ref_member_type IDENTIFIER { lexer.parsing_generic_declaration = false; @@ -1364,10 +1364,29 @@ method_declaration } ; +ref_member_type + : member_type + { + $$ = $1; + } + | REF + { + lexer.parsing_generic_declaration = true; + } + type + { + if (lang_version < LanguageVersion.V_7) { + FeatureIsNotAvailable (GetLocation ($1), "byref locals and returns"); + } + + $$ = new ReferenceTypeExpr ((FullNamedExpression) $3, GetLocation ($1)); + } + ; + method_header : opt_attributes opt_modifiers - member_type + ref_member_type method_declaration_name OPEN_PARENS { valid_param_mod = ParameterModifierType.All; @@ -1452,7 +1471,7 @@ method_header } | opt_attributes opt_modifiers - member_type + ref_member_type modifiers method_declaration_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS { MemberName name = (MemberName) $5; @@ -1473,7 +1492,7 @@ method_header } | opt_attributes opt_modifiers - member_type + ref_member_type method_declaration_name error { Error_SyntaxError (yyToken); @@ -1534,7 +1553,7 @@ expression_block ++lexer.parsing_block; start_block (GetLocation ($1)); } - expression SEMICOLON + lambda_arrow_expression SEMICOLON { lexer.parsing_block = 0; current_block.AddStatement (new ContextualReturn ((Expression) $3)); @@ -1838,7 +1857,7 @@ arglist_modifier property_declaration : opt_attributes opt_modifiers - member_type + ref_member_type member_declaration_name { lexer.parsing_generic_declaration = false; @@ -1865,6 +1884,16 @@ property_declaration if (doc_support) current_property.DocComment = ConsumeStoredComment (); + + if ($3 is ReferenceTypeExpr) { + if (current_property.Get == null) { + report.Error (8146, GetLocation ($4), "`{0}': property and indexer which return by reference must have a get accessor", current_property.GetSignatureForError ()); + } + + if (current_property.Set != null) { + report.Error (8147, GetLocation ($4), "`{0}': property and indexer which return by reference cannot have set accessors", current_property.GetSignatureForError ()); + } + } } CLOSE_BRACE { @@ -1877,7 +1906,7 @@ property_declaration } | opt_attributes opt_modifiers - member_type + ref_member_type member_declaration_name { lexer.parsing_generic_declaration = false; @@ -1939,7 +1968,7 @@ property_initializer indexer_declaration : opt_attributes opt_modifiers - member_type indexer_declaration_name OPEN_BRACKET + ref_member_type indexer_declaration_name OPEN_BRACKET { valid_param_mod = ParameterModifierType.Params | ParameterModifierType.DefaultValue; } @@ -1979,6 +2008,16 @@ indexer_declaration if (doc_support) current_property.DocComment = ConsumeStoredComment (); + + if ($3 is ReferenceTypeExpr) { + if (current_property.Get == null) { + report.Error (8146, GetLocation ($4), "`{0}': property and indexer which return by reference must have a get accessor", current_property.GetSignatureForError ()); + } + + if (current_property.Set != null) { + report.Error (8147, GetLocation ($4), "`{0}': property and indexer which return by reference cannot have set accessors", current_property.GetSignatureForError ()); + } + } current_property = null; } @@ -2938,7 +2977,7 @@ delegate_declaration : opt_attributes opt_modifiers DELEGATE - member_type type_declaration_name + ref_member_type type_declaration_name OPEN_PARENS { valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out | ParameterModifierType.Params | ParameterModifierType.DefaultValue; @@ -5109,7 +5148,7 @@ lambda_expression_body : { start_block (Location.Null); } - expression // All expressions must handle error or current block won't be restored and breaking ast completely + lambda_arrow_expression // All expressions must handle error or current block won't be restored and breaking ast completely { Block b = end_block (Location.Null); b.IsCompilerGenerated = true; @@ -5129,6 +5168,11 @@ lambda_expression_body } ; +lambda_arrow_expression + : expression + | reference_expression + ; + expression_or_error : expression | error @@ -5939,6 +5983,28 @@ block_variable_declaration current_variable = null; lbag.AddLocation ($$, GetLocation ($1), GetLocation ($7)); } + | REF variable_type identifier_inside_body + { + if (lang_version < LanguageVersion.V_7) { + FeatureIsNotAvailable (GetLocation ($1), "byref locals and returns"); + } + + var lt = (LocatedToken) $3; + var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ByRef, lt.Location); + current_block.AddLocalName (li); + current_variable = new BlockVariable ((FullNamedExpression) $2, li); + } + opt_local_variable_initializer opt_variable_declarators SEMICOLON + { + $$ = current_variable; + current_variable = null; + if ($5 != null) { + lbag.AddLocation ($$, PopLocation (), GetLocation ($7)); + } else { + report.Error (8174, GetLocation ($3), "A declaration of a by-reference variable must have an initializer"); + lbag.AddLocation ($$, GetLocation ($7)); + } + } ; opt_local_variable_initializer @@ -6047,6 +6113,18 @@ block_variable_initializer report.Error (1575, GetLocation ($1), "A stackalloc expression requires [] after type"); $$ = new StackAlloc ((Expression) $2, null, GetLocation ($1)); } + | reference_expression + ; + +reference_expression + : REF expression + { + if (lang_version < LanguageVersion.V_7) { + FeatureIsNotAvailable (GetLocation ($1), "byref locals and returns"); + } + + $$ = new ReferenceExpression ((Expression) $2, GetLocation ($1)); + } ; expression_statement @@ -6522,6 +6600,11 @@ return_statement $$ = new Return ((Expression) $2, GetLocation ($1)); lbag.AddStatement ($$, GetLocation ($3)); } + | RETURN reference_expression SEMICOLON + { + $$ = new Return ((Expression) $2, GetLocation ($1)); + lbag.AddStatement ($$, GetLocation ($3)); + } | RETURN expression error { Error_SyntaxError (yyToken); diff --git a/mcs/mcs/delegate.cs b/mcs/mcs/delegate.cs index 368bb19c1b1..80eb7e265f1 100644 --- a/mcs/mcs/delegate.cs +++ b/mcs/mcs/delegate.cs @@ -587,8 +587,7 @@ namespace Mono.CSharp { rt = ec.BuiltinTypes.Object; if (!Delegate.IsTypeCovariant (ec, rt, invoke_method.ReturnType)) { - Expression ret_expr = new TypeExpression (delegate_method.ReturnType, loc); - Error_ConversionFailed (ec, delegate_method, ret_expr); + Error_ConversionFailed (ec, delegate_method, delegate_method.ReturnType); } if (method_group.IsConditionallyExcluded) { @@ -639,7 +638,7 @@ namespace Mono.CSharp { method_group.FlowAnalysis (fc); } - void Error_ConversionFailed (ResolveContext ec, MethodSpec method, Expression return_type) + void Error_ConversionFailed (ResolveContext ec, MethodSpec method, TypeSpec return_type) { var invoke_method = Delegate.GetInvokeMethod (type); string member_name = method_group.InstanceExpression != null ? @@ -661,6 +660,12 @@ namespace Mono.CSharp { return; } + if (invoke_method.ReturnType.Kind == MemberKind.ByRef) { + ec.Report.Error (8189, loc, "By reference return delegate does not match `{0}' return type", + Delegate.FullDelegateDesc (invoke_method)); + return; + } + ec.Report.Error (407, loc, "A method or delegate `{0} {1}' return type does not match delegate `{2} {3}' return type", return_type.GetSignatureForError (), member_name, invoke_method.ReturnType.GetSignatureForError (), Delegate.FullDelegateDesc (invoke_method)); diff --git a/mcs/mcs/ecore.cs b/mcs/mcs/ecore.cs index 94864753054..490bcfb518e 100644 --- a/mcs/mcs/ecore.cs +++ b/mcs/mcs/ecore.cs @@ -303,7 +303,12 @@ namespace Mono.CSharp { return; string from_type = type.GetSignatureForError (); + if (type.Kind == MemberKind.ByRef) + from_type = "ref " + from_type; string to_type = target.GetSignatureForError (); + if (target.Kind == MemberKind.ByRef) + to_type = "ref " + to_type; + if (from_type == to_type) { from_type = type.GetSignatureForErrorIncludingAssemblyName (); to_type = target.GetSignatureForErrorIncludingAssemblyName (); @@ -559,18 +564,18 @@ namespace Mono.CSharp { public Expression ResolveLValue (ResolveContext ec, Expression right_side) { int errors = ec.Report.Errors; - bool out_access = right_side == EmptyExpression.OutAccess; + //bool out_access = right_side == EmptyExpression.OutAccess; Expression e = DoResolveLValue (ec, right_side); - if (e != null && out_access && !(e is IMemoryLocation)) { + //if (e != null && out_access && !(e is IMemoryLocation)) { // FIXME: There's no problem with correctness, the 'Expr = null' handles that. // Enabling this 'throw' will "only" result in deleting useless code elsewhere, //throw new InternalErrorException ("ResolveLValue didn't return an IMemoryLocation: " + // e.GetType () + " " + e.GetSignatureForError ()); - e = null; - } + // e = null; + //} if (e == null) { if (errors == ec.Report.Errors) { @@ -5417,6 +5422,11 @@ namespace Mono.CSharp { if (arg_type == InternalType.VarOutType) return 0; + var ref_arg_type = arg_type as ReferenceContainer; + if (ref_arg_type != null) { + arg_type = ref_arg_type.Element; + } + // // Do full equality check after quick path // @@ -5982,13 +5992,13 @@ namespace Mono.CSharp { int arg_count = args == null ? 0 : args.Count; for (; a_idx < arg_count; a_idx++, ++a_pos) { - a = args[a_idx]; + a = args [a_idx]; if (a == null) continue; if (p_mod != Parameter.Modifier.PARAMS) { p_mod = cpd.FixedParameters [a_idx].ModFlags; - pt = ptypes[a_idx]; + pt = ptypes [a_idx]; has_unsafe_arg |= pt.IsPointer; if (p_mod == Parameter.Modifier.PARAMS) { @@ -6018,6 +6028,14 @@ namespace Mono.CSharp { continue; } + var ref_arg_type = arg_type as ReferenceContainer; + if (ref_arg_type != null) { + if (ref_arg_type.Element != pt) + break; + + return true; + } + if (!TypeSpecComparer.IsEqual (arg_type, pt)) break; } @@ -6525,12 +6543,12 @@ namespace Mono.CSharp { GetSignatureForError ()); } - return null; + return ErrorExpression.Instance; } if (right_side == EmptyExpression.LValueMemberAccess) { // Already reported as CS1648/CS1650 - return null; + return ErrorExpression.Instance; } if (right_side == EmptyExpression.LValueMemberOutAccess) { @@ -6541,7 +6559,7 @@ namespace Mono.CSharp { rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)", GetSignatureForError ()); } - return null; + return ErrorExpression.Instance; } if (IsStatic) { @@ -6552,7 +6570,7 @@ namespace Mono.CSharp { GetSignatureForError ()); } - return null; + return ErrorExpression.Instance; } public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) @@ -7341,6 +7359,15 @@ namespace Mono.CSharp { Error_NullPropagatingLValue (rc); if (right_side == EmptyExpression.OutAccess) { + if (best_candidate?.MemberType.Kind == MemberKind.ByRef) { + if (Arguments?.ContainsEmitWithAwait () == true) { + rc.Report.Error (8178, loc, "`await' cannot be used in an expression containing a call to `{0}' because it returns by reference", + GetSignatureForError ()); + } + + return this; + } + // TODO: best_candidate can be null at this point INamedBlockVariable variable = null; if (best_candidate != null && rc.CurrentBlock.ParametersBlock.TopBlock.GetLocalName (best_candidate.Name, rc.CurrentBlock, ref variable) && variable is Linq.RangeVariable) { diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs index 113ed29910c..3407b9faf88 100644 --- a/mcs/mcs/expression.cs +++ b/mcs/mcs/expression.cs @@ -6562,18 +6562,19 @@ namespace Mono.CSharp return; } + bool dereference = IsRef && !(source is ReferenceExpression); New n_source = source as New; if (n_source != null && n_source.CanEmitOptimizedLocalTarget (ec)) { if (!n_source.Emit (ec, this)) { if (leave_copy) { EmitLoad (ec); - if (IsRef) + if (dereference) ec.EmitLoadFromPtr (type); } return; } } else { - if (IsRef) + if (dereference) EmitLoad (ec); source.Emit (ec); @@ -6581,13 +6582,13 @@ namespace Mono.CSharp if (leave_copy) { ec.Emit (OpCodes.Dup); - if (IsRef) { + if (dereference) { temp = new LocalTemporary (Type); temp.Store (ec); } } - if (IsRef) + if (dereference) ec.EmitStoreFromPtr (type); else Variable.EmitAssign (ec); @@ -6671,7 +6672,7 @@ namespace Mono.CSharp } public override bool IsRef { - get { return false; } + get { return local_info.IsByRef; } } public override string Name { @@ -6712,7 +6713,11 @@ namespace Mono.CSharp AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc); } else if (local_info.IsFixed) { ec.Report.Error (1764, loc, - "Cannot use fixed local `{0}' inside an anonymous method, lambda expression or query expression", + "Cannot use fixed variable `{0}' inside an anonymous method, lambda expression or query expression", + GetSignatureForError ()); + } else if (local_info.IsByRef) { + ec.Report.Error (8175, loc, + "Cannot use by-reference variable `{0}' inside an anonymous method, lambda expression, or query expression", GetSignatureForError ()); } @@ -7134,7 +7139,7 @@ namespace Mono.CSharp protected override Expression DoResolve (ResolveContext rc) { ResolveConditionalAccessReceiver (rc); - return DoResolveInvocation (rc); + return DoResolveInvocation (rc, null); } public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) @@ -7161,10 +7166,21 @@ namespace Mono.CSharp return res.Resolve (rc); } + if (right_side != null) { + if (eclass != ExprClass.Unresolved) + return this; + + var res = DoResolveInvocation (rc, right_side); + if (res == null) + return null; + + return res; + } + return base.DoResolveLValue (rc, right_side); } - Expression DoResolveInvocation (ResolveContext ec) + Expression DoResolveInvocation (ResolveContext ec, Expression rhs) { Expression member_expr; var atn = expr as ATypeNameExpression; @@ -7260,6 +7276,17 @@ namespace Mono.CSharp IsSpecialMethodInvocation (ec, method, loc); eclass = ExprClass.Value; + + if (type.Kind == MemberKind.ByRef) { + if (rhs == null && arguments?.ContainsEmitWithAwait () == true) { + ec.Report.Error (8178, loc, "`await' cannot be used in an expression containing a call to `{0}' because it returns by reference", + GetSignatureForError ()); + } + + if (rhs != EmptyExpression.OutAccess) + return ByRefDereference.Create (this).Resolve (ec); + } + return this; } @@ -11591,6 +11618,39 @@ namespace Mono.CSharp } } + class ReferenceTypeExpr : TypeExpr + { + FullNamedExpression element; + + public ReferenceTypeExpr (FullNamedExpression element, Location loc) + { + this.element = element; + this.loc = loc; + } + + public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false) + { + type = element.ResolveAsType (mc); + if (type == null) + return null; + + eclass = ExprClass.Type; + type = ReferenceContainer.MakeType (mc.Module, type); + + return type; + } + + public override string GetSignatureForError () + { + return "ref " + element.GetSignatureForError (); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } + class FixedBufferPtr : Expression { readonly Expression array; @@ -12918,4 +12978,146 @@ namespace Mono.CSharp return Reachability.CreateUnreachable (); } } + + class ReferenceExpression : CompositeExpression + { + public ReferenceExpression (Expression expr, Location loc) + : base (expr) + { + this.loc = loc; + } + + static bool CanBeByRef (Expression expr) + { + if (expr is IAssignMethod) + return true; + + var invocation = expr as Invocation; + if (invocation?.Type.Kind == MemberKind.ByRef) + return true; + + return false; + } + + public override Expression CreateExpressionTree (ResolveContext rc) + { + throw new NotSupportedException ("ET"); + } + + protected override Expression DoResolve (ResolveContext rc) + { + var res = expr.DoResolveLValue (rc, EmptyExpression.OutAccess); + if (res == null || !CanBeByRef (res)) { + if (res?.Type != InternalType.ErrorType) + rc.Report.Error (8156, expr.Location, "An expression cannot be used in this context because it may not be returned by reference"); + return ErrorExpression.Instance; + } + + type = res.Type; + var type_container = type as ReferenceContainer; + if (type_container != null) + type = type_container.Element; + + expr = res; + eclass = ExprClass.Value; + return this; + } + + public override void Emit (EmitContext ec) + { + var ml = expr as IMemoryLocation; + if (ml != null) + ml.AddressOf (ec, AddressOp.LoadStore); + else + expr.Emit (ec); + } + + public override void Error_ValueCannotBeConverted (ResolveContext rc, TypeSpec target, bool expl) + { + rc.Report.Error (8173, loc, "The expression must be of type `{0}' because it is being assigned by reference", target.GetSignatureForError ()); + } + } + + class ByRefDereference : CompositeExpression, IMemoryLocation, IAssignMethod + { + bool prepared; + LocalTemporary temporary; + + private ByRefDereference (Expression expr) + : base (expr) + { + } + + public static Expression Create (Expression expr) + { + var rc = expr.Type as ReferenceContainer; + if (rc == null) + return expr; + + return new ByRefDereference (expr) { + type = rc.Element + }; + } + + public void AddressOf (EmitContext ec, AddressOp mode) + { + expr.Emit (ec); + } + + public void Emit (EmitContext ec, bool leave_copy) + { + Emit (ec); + if (leave_copy) { + ec.Emit (OpCodes.Dup); + temporary = new LocalTemporary (type); + temporary.Store (ec); + } + } + + public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound) + { + prepared = isCompound; + + expr.Emit (ec); + + if (isCompound) + ec.Emit (OpCodes.Dup); + + source.Emit (ec); + if (leave_copy) { + throw new NotImplementedException ("leave_copy"); + } + + ec.EmitStoreFromPtr (type); + + if (temporary != null) { + temporary.Emit (ec); + temporary.Release (ec); + } + } + + protected override Expression DoResolve (ResolveContext rc) + { + eclass = ExprClass.Variable; + return this; + } + + public override Expression DoResolveLValue (ResolveContext rc, Expression right_side) + { + return DoResolve (rc); + } + + public override void Emit (EmitContext ec) + { + if (!prepared) + base.Emit(ec); + + ec.EmitLoadFromPtr (type); + } + + public override object Accept (StructuralVisitor visitor) + { + return visitor.Visit (this); + } + } } diff --git a/mcs/mcs/generic.cs b/mcs/mcs/generic.cs index 1efba64ba57..625cd0c773f 100644 --- a/mcs/mcs/generic.cs +++ b/mcs/mcs/generic.cs @@ -1543,6 +1543,9 @@ namespace Mono.CSharp { if (ec is PointerContainer) return PointerContainer.MakeType (context.Module, et); + if (ec is ReferenceContainer) + return ReferenceContainer.MakeType (context.Module, et); + throw new NotImplementedException (); } diff --git a/mcs/mcs/iterators.cs b/mcs/mcs/iterators.cs index c0ccf129e74..feb10844b8c 100644 --- a/mcs/mcs/iterators.cs +++ b/mcs/mcs/iterators.cs @@ -1187,11 +1187,15 @@ namespace Mono.CSharp return; if (!CheckType (ret, parent, out iterator_type, out is_enumerable)) { - parent.Compiler.Report.Error (1624, method.Location, - "The body of `{0}' cannot be an iterator block " + - "because `{1}' is not an iterator interface type", - method.GetSignatureForError (), - ret.GetSignatureForError ()); + if (ret.Kind == MemberKind.ByRef) { + parent.Compiler.Report.Error (8154, method.Location, + "The body of `{0}' cannot be an iterator block because the method returns by reference", + method.GetSignatureForError ()); + } else { + parent.Compiler.Report.Error (1624, method.Location, + "The body of `{0}' cannot be an iterator block because `{1}' is not an iterator interface type", + method.GetSignatureForError (), ret.GetSignatureForError ()); + } return; } diff --git a/mcs/mcs/lambda.cs b/mcs/mcs/lambda.cs index 9c804ceca57..360c5c94630 100644 --- a/mcs/mcs/lambda.cs +++ b/mcs/mcs/lambda.cs @@ -188,6 +188,11 @@ namespace Mono.CSharp { public override Expression CreateExpressionTree (ResolveContext ec) { + if (Expr is ReferenceExpression) { + ec.Report.Error (8155, Expr.Location, "Lambda expressions that return by reference cannot be converted to expression trees"); + return null; + } + return Expr.CreateExpressionTree (ec); } @@ -216,6 +221,12 @@ namespace Mono.CSharp { if (Expr == null) return false; + if (Expr is ReferenceExpression) { + // CSC: should be different error code + ec.Report.Error (8149, loc, "By-reference returns can only be used in lambda expressions that return by reference"); + return false; + } + statement = Expr as ExpressionStatement; if (statement == null) { var reduced = Expr as IReducedExpressionStatement; diff --git a/mcs/mcs/membercache.cs b/mcs/mcs/membercache.cs index d132c872baf..eebf71b844b 100644 --- a/mcs/mcs/membercache.cs +++ b/mcs/mcs/membercache.cs @@ -35,6 +35,7 @@ namespace Mono.CSharp { Enum = 1 << 14, Interface = 1 << 15, TypeParameter = 1 << 16, + ByRef = 1 << 17, ArrayType = 1 << 19, PointerType = 1 << 20, diff --git a/mcs/mcs/pending.cs b/mcs/mcs/pending.cs index 1de765fb4ac..d95f8f13956 100644 --- a/mcs/mcs/pending.cs +++ b/mcs/mcs/pending.cs @@ -748,6 +748,11 @@ namespace Mono.CSharp { Report.Error (737, container.Location, "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public", container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ()); + } else if (mi.ReturnType.Kind == MemberKind.ByRef) { + Report.Error (8152, container.Location, + "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not return by reference", + container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (), + candidate.ReturnType.GetSignatureForError ()); } else { Report.Error (738, container.Location, "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'", diff --git a/mcs/mcs/property.cs b/mcs/mcs/property.cs index 1dd8a86468e..3478711470a 100644 --- a/mcs/mcs/property.cs +++ b/mcs/mcs/property.cs @@ -879,6 +879,11 @@ namespace Mono.CSharp return false; } + if (MemberType.Kind == MemberKind.ByRef) { + Report.Error (8145, Location, "Auto-implemented properties cannot return by reference"); + return false; + } + if (Compiler.Settings.Version < LanguageVersion.V_3 && Initializer == null) Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented properties"); diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index 7a379fc172a..0c02bf0562a 100644 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -1272,16 +1272,38 @@ namespace Mono.CSharp { if (expr == null) return false; + if (expr is ReferenceExpression && block_return_type.Kind != MemberKind.ByRef) { + ec.Report.Error (8149, loc, "By-reference returns can only be used in methods that return by reference"); + return false; + } + if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) { - expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc); + if (block_return_type.Kind == MemberKind.ByRef) { + var ref_expr = Expr as ReferenceExpression; + if (ref_expr == null) { + ec.Report.Error (8150, loc, "By-reference return is required when method returns by reference"); + return false; + } - if (expr == null) { - if (am != null && block_return_type == ec.ReturnType) { - ec.Report.Error (1662, loc, - "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type", - am.ContainerType, am.GetSignatureForError ()); + var byref_return = (ReferenceContainer)block_return_type; + + if (expr.Type != byref_return.Element) { + ec.Report.Error (8151, loc, "The return by reference expression must be of type `{0}' because this method returns by reference", + byref_return.GetSignatureForError ()); + return false; + } + } else { + + expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc); + + if (expr == null) { + if (am != null && block_return_type == ec.ReturnType) { + ec.Report.Error (1662, loc, + "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type", + am.ContainerType, am.GetSignatureForError ()); + } + return false; } - return false; } } @@ -2185,7 +2207,7 @@ namespace Mono.CSharp { } if (type == null) { - type = type_expr.ResolveAsType (bc); + type = ResolveTypeExpression (bc); if (type == null) return false; @@ -2208,6 +2230,25 @@ namespace Mono.CSharp { } if (initializer != null) { + if (li.IsByRef) { + if (!(initializer is ReferenceExpression)) { + bc.Report.Error (8172, loc, "Cannot initialize a by-reference variable `{0}' with a value", li.Name); + return false; + } + + if (bc.CurrentAnonymousMethod is AsyncInitializer) { + bc.Report.Error (8177, loc, "Async methods cannot use by-reference variables"); + } else if (bc.CurrentIterator != null) { + bc.Report.Error (8176, loc, "Iterators cannot use by-reference variables"); + } + + } else { + if (initializer is ReferenceExpression) { + bc.Report.Error (8171, loc, "Cannot initialize a by-value variable `{0}' with a reference expression", li.Name); + return false; + } + } + initializer = ResolveInitializer (bc, li, initializer); // li.Variable.DefinitelyAssigned } @@ -2237,6 +2278,11 @@ namespace Mono.CSharp { return a.ResolveStatement (bc); } + protected virtual TypeSpec ResolveTypeExpression (BlockContext bc) + { + return type_expr.ResolveAsType (bc); + } + protected override void DoEmit (EmitContext ec) { li.CreateBuilder (ec); @@ -2364,6 +2410,7 @@ namespace Mono.CSharp { UsingVariable = 1 << 7, IsLocked = 1 << 8, SymbolFileHidden = 1 << 9, + ByRef = 1 << 10, ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable } @@ -2448,6 +2495,8 @@ namespace Mono.CSharp { } } + public bool IsByRef => (flags & Flags.ByRef) != 0; + public bool IsCompilerGenerated { get { return (flags & Flags.CompilerGenerated) != 0; @@ -2550,10 +2599,15 @@ namespace Mono.CSharp { throw new InternalErrorException ("Already created variable `{0}'", name); } - // - // All fixed variabled are pinned, a slot has to be alocated - // - builder = ec.DeclareLocal (Type, IsFixed); + if (IsByRef) { + builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, Type), IsFixed); + } else { + // + // All fixed variabled are pinned, a slot has to be alocated + // + builder = ec.DeclareLocal(Type, IsFixed); + } + if ((flags & Flags.SymbolFileHidden) == 0) ec.DefineLocalVariable (name, builder); } @@ -2602,7 +2656,10 @@ namespace Mono.CSharp { if ((flags & Flags.CompilerGenerated) != 0) CreateBuilder (ec); - ec.Emit (OpCodes.Ldloca, builder); + if (IsByRef) + ec.Emit (OpCodes.Ldloc, builder); + else + ec.Emit (OpCodes.Ldloca, builder); } public static string GetCompilerGeneratedName (Block block) diff --git a/mcs/mcs/typespec.cs b/mcs/mcs/typespec.cs index e6ee60c585a..0585dfd667a 100644 --- a/mcs/mcs/typespec.cs +++ b/mcs/mcs/typespec.cs @@ -1991,10 +1991,11 @@ namespace Mono.CSharp } } + [System.Diagnostics.DebuggerDisplay("{DisplayDebugInfo()}")] class ReferenceContainer : ElementTypeSpec { ReferenceContainer (TypeSpec element) - : base (MemberKind.Class, element, null) // TODO: Kind.Class is most likely wrong + : base (MemberKind.ByRef, element, null) { } @@ -2004,6 +2005,11 @@ namespace Mono.CSharp } } + string DisplayDebugInfo() + { + return "ref " + GetSignatureForError(); + } + public override MetaType GetMetaInfo () { if (info == null) { @@ -2013,8 +2019,16 @@ namespace Mono.CSharp return info; } + public override string GetSignatureForError () + { + return Element.GetSignatureForError (); + } + public static ReferenceContainer MakeType (ModuleContainer module, TypeSpec element) { + if (element.Kind == MemberKind.ByRef) + throw new ArgumentException (); + ReferenceContainer pc; if (!module.ReferenceTypesCache.TryGetValue (element, out pc)) { pc = new ReferenceContainer (element); diff --git a/mcs/tests/test-ref-01.cs b/mcs/tests/test-ref-01.cs new file mode 100644 index 00000000000..60126a3593c --- /dev/null +++ b/mcs/tests/test-ref-01.cs @@ -0,0 +1,38 @@ +// Compiler options: -unsafe + +public unsafe class X +{ + int field; + int* ufield; + + public static void Main () + { + int i = 5; + ref int j = ref i; + + var x = new X (); + ref var v = ref x.TestMethod (); + } + + ref int TestMethod () + { + return ref field; + } + + ref int TestProperty { + get { + return ref field; + } + } + + ref int this [long arg] { + get { + return ref field; + } + } + + unsafe ref int* Foo () + { + return ref ufield; + } +} \ No newline at end of file diff --git a/mcs/tests/test-ref-02.cs b/mcs/tests/test-ref-02.cs new file mode 100644 index 00000000000..9adf471576e --- /dev/null +++ b/mcs/tests/test-ref-02.cs @@ -0,0 +1,35 @@ +using System; + +class X +{ + int field; + + static void Main () + { + var x = new X (); + x.Run (); + } + + void Run () + { + Test (ref this[0]); + Test (ref Prop); + } + + static int Test (ref int y) + { + return y; + } + + ref int this [int y] { + get { + return ref field; + } + } + + ref int Prop { + get { + return ref field; + } + } +} \ No newline at end of file diff --git a/mcs/tests/test-ref-03.cs b/mcs/tests/test-ref-03.cs new file mode 100644 index 00000000000..201bd6c73ca --- /dev/null +++ b/mcs/tests/test-ref-03.cs @@ -0,0 +1,33 @@ +class X +{ + int x; + + static void Main () + { + var x = new X (); + Foo (ref x.Wrap (1)); + Foo (ref x.Prop); + Foo (ref x[""]); + } + + ref int Wrap (int arg) + { + return ref x; + } + + ref int Prop { + get { + return ref x; + } + } + + ref int this [string arg] { + get { + return ref x; + } + } + + static void Foo (ref int arg) + { + } +} \ No newline at end of file diff --git a/mcs/tests/test-ref-04.cs b/mcs/tests/test-ref-04.cs new file mode 100644 index 00000000000..af96f235ce7 --- /dev/null +++ b/mcs/tests/test-ref-04.cs @@ -0,0 +1,45 @@ +class X +{ + int field; + + public static int Main () + { + var x = new X (); + + x.field = 5; + if (!x.Test1 ()) + return 1; + + x.Test2 (); + + if (x.Test3 ()++ != 6) + return 2; + + if (x.field != 7) + return 3; + + return 0; + } + + bool Test1 () + { + ref var x = ref field; + int v = x; + ++x; + + return x == 6; + } + + void Test2 () + { + ref int x = ref field; + x.ToString (); + } + + ref int Test3 () + { + ref int l = ref field; + ref int v = ref l; + return ref l; + } +} \ No newline at end of file diff --git a/mcs/tests/test-ref-05.cs b/mcs/tests/test-ref-05.cs new file mode 100644 index 00000000000..4848468cfda --- /dev/null +++ b/mcs/tests/test-ref-05.cs @@ -0,0 +1,46 @@ +class X +{ + static int field; + + public static int Main () + { + Test () = 3; + + if (field != (byte) 3) + return 1; + + G.Test (ref field) = 6; + if (field != 6) + return 2; + + --Test (); + if (field != 5) + return 3; + + Test (ref Test (), ref Test ()); + + return 0; + } + + static ref int Test () + { + return ref field; + } + + static void Test (ref T a, ref int b) + { + } + + static void Test2 (ref T arg) + { + Test (ref arg, ref Test ()); + } +} + +class G +{ + public static ref T Test (ref T arg) + { + return ref arg; + } +} \ No newline at end of file diff --git a/mcs/tests/ver-il-net_4_x.xml b/mcs/tests/ver-il-net_4_x.xml index db5bfbd387c..b111a7e3c5a 100644 --- a/mcs/tests/ver-il-net_4_x.xml +++ b/mcs/tests/ver-il-net_4_x.xml @@ -72668,6 +72668,118 @@ + + + + 20 + + + 15 + + + 15 + + + 15 + + + 15 + + + 7 + + + + + + + 14 + + + 27 + + + 11 + + + 15 + + + 15 + + + 7 + + + + + + + 47 + + + 15 + + + 15 + + + 15 + + + 2 + + + 7 + + + + + + + 93 + + + 30 + + + 22 + + + 19 + + + 7 + + + + + + + 108 + + + 14 + + + 2 + + + 13 + + + 7 + + + + + 10 + + + 7 + + +