From: Marek Safar Date: Mon, 4 Aug 2014 15:32:15 +0000 (+0200) Subject: [mcs] null propagating operator on index expressions X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=3bd87b33c522187af6aa8e31bbd82043905d357e;p=mono.git [mcs] null propagating operator on index expressions --- diff --git a/mcs/errors/cs0023-22.cs b/mcs/errors/cs0023-22.cs new file mode 100644 index 00000000000..ce5c0b29fdd --- /dev/null +++ b/mcs/errors/cs0023-22.cs @@ -0,0 +1,10 @@ +// CS0023: The `?' operator cannot be applied to operand of type `null' +// Line: 8 + +class C +{ + static void Main () + { + var res = null?[0]; + } +} \ No newline at end of file diff --git a/mcs/errors/cs0023-23.cs b/mcs/errors/cs0023-23.cs new file mode 100644 index 00000000000..ae7db34845d --- /dev/null +++ b/mcs/errors/cs0023-23.cs @@ -0,0 +1,12 @@ +// CS0023: The `?' operator cannot be applied to operand of type `void' +// Line: 10 + +using System; + +class C +{ + static void Main () + { + var v = Console.WriteLine ()?[0]; + } +} \ No newline at end of file diff --git a/mcs/errors/cs1644-42.cs b/mcs/errors/cs1644-42.cs new file mode 100644 index 00000000000..aea12a8aef0 --- /dev/null +++ b/mcs/errors/cs1644-42.cs @@ -0,0 +1,12 @@ +// CS1644: Feature `null propagating operator' cannot be used because it is not part of the C# 5.0 language specification +// Line: 10 +// Compiler options: -langversion:5 + +class C +{ + static void Main () + { + string[] a = null; + var s = a?[0]; + } +} \ No newline at end of file diff --git a/mcs/errors/cs8072-2.cs b/mcs/errors/cs8072-2.cs new file mode 100644 index 00000000000..f9f2a1bf1ce --- /dev/null +++ b/mcs/errors/cs8072-2.cs @@ -0,0 +1,16 @@ +// CS8072: An expression tree cannot contain a null propagating operator +// Line: 14 + +using System; +using System.Linq.Expressions; + +public class C +{ + public void TestMethod () { } + + static void Main () + { + C c = null; + Expression e = () => c?.TestMethod (); + } +} diff --git a/mcs/errors/cs8072.cs b/mcs/errors/cs8072.cs new file mode 100644 index 00000000000..e53e9069ac9 --- /dev/null +++ b/mcs/errors/cs8072.cs @@ -0,0 +1,14 @@ +// CS8072: An expression tree cannot contain a null propagating operator +// Line: 11 + +using System; +using System.Linq.Expressions; + +class C +{ + static int Main () + { + Expression> e = l => l?[1]; + return 0; + } +} \ No newline at end of file diff --git a/mcs/mcs/cs-parser.jay b/mcs/mcs/cs-parser.jay index 0d912c99818..693f95ce599 100644 --- a/mcs/mcs/cs-parser.jay +++ b/mcs/mcs/cs-parser.jay @@ -3566,9 +3566,11 @@ element_access if (lang_version < LanguageVersion.V_6) FeatureIsNotAvailable (GetLocation ($2), "null propagating operator"); - throw new NotImplementedException (); -// $$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3)); -// lbag.AddLocation ($$, GetLocation ($4)); + $$ = new ElementAccess ((Expression) $1, (Arguments) $4, GetLocation ($3)) { + NullPropagating = true + }; + + lbag.AddLocation ($$, GetLocation ($2), GetLocation ($5)); } | primary_expression OPEN_BRACKET_EXPR expression_list_arguments error { diff --git a/mcs/mcs/cs-tokenizer.cs b/mcs/mcs/cs-tokenizer.cs index 908bdaeab8c..75450430048 100644 --- a/mcs/mcs/cs-tokenizer.cs +++ b/mcs/mcs/cs-tokenizer.cs @@ -1264,15 +1264,6 @@ namespace Mono.CSharp return Token.INTERR_OPERATOR; } - switch (current_token) { - case Token.CLOSE_PARENS: - case Token.TRUE: - case Token.FALSE: - case Token.NULL: - case Token.LITERAL: - return Token.INTERR; - } - if (d != ' ') { if (d == ',' || d == ';' || d == '>') return Token.INTERR_NULLABLE; diff --git a/mcs/mcs/ecore.cs b/mcs/mcs/ecore.cs index ee5479c94fa..97fe44d239d 100644 --- a/mcs/mcs/ecore.cs +++ b/mcs/mcs/ecore.cs @@ -429,6 +429,18 @@ namespace Mono.CSharp { { return type.GetDefinition ().GetSignatureForError (); } + + protected static bool IsNullPropagatingValid (TypeSpec type) + { + return (TypeSpec.IsReferenceType (type) && type != InternalType.NullLiteral) || type.IsNullableType; + } + + protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type) + { + return TypeSpec.IsValueType (type) && !type.IsNullableType ? + Nullable.NullableInfo.MakeType (rc.Module, type) : + type; + } /// /// Resolves an expression and performs semantic analysis on it. @@ -936,6 +948,11 @@ namespace Mono.CSharp { ec.Report.Error (1944, loc, "An expression tree cannot contain an unsafe pointer operation"); } + protected void Error_NullShortCircuitInsideExpressionTree (ResolveContext rc) + { + rc.Report.Error (8072, loc, "An expression tree cannot contain a null propagating operator"); + } + public virtual void FlowAnalysis (FlowAnalysisContext fc) { } @@ -3350,13 +3367,6 @@ namespace Mono.CSharp { InstanceExpression.FlowAnalysis (fc); } - protected static TypeSpec LiftMemberType (ResolveContext rc, TypeSpec type) - { - return TypeSpec.IsValueType (type) && !type.IsNullableType ? - Nullable.NullableInfo.MakeType (rc.Module, type) : - type; - } - public bool ResolveInstanceExpression (ResolveContext rc, Expression rhs) { if (!ResolveInstanceExpressionCore (rc, rhs)) @@ -3818,7 +3828,10 @@ namespace Mono.CSharp { if (IsConditionallyExcluded) ec.Report.Error (765, loc, "Partial methods with only a defining declaration or removed conditional methods cannot be used in an expression tree"); - + + if (NullShortCircuit) + Error_NullShortCircuitInsideExpressionTree (ec); + return new TypeOfMethod (best_candidate, loc); } @@ -5895,6 +5908,10 @@ namespace Mono.CSharp { public override Expression CreateExpressionTree (ResolveContext ec) { + if (NullShortCircuit) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + return CreateExpressionTree (ec, true); } @@ -6089,8 +6106,11 @@ namespace Mono.CSharp { return null; } - override public Expression DoResolveLValue (ResolveContext ec, Expression right_side) + public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) { + if (NullShortCircuit) + throw new NotSupportedException ("null propagating operator assignment"); + if (spec is FixedFieldSpec) { // It could be much better error message but we want to be error compatible Error_ValueAssignment (ec, right_side); @@ -6470,6 +6490,10 @@ namespace Mono.CSharp { public override Expression CreateExpressionTree (ResolveContext ec) { + if (NullShortCircuit) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + Arguments args; if (IsSingleDimensionalArrayLength ()) { args = new Arguments (1); @@ -6727,6 +6751,9 @@ namespace Mono.CSharp { public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) { + if (NullShortCircuit) + throw new NotSupportedException ("null propagating operator assignment"); + if (right_side == EmptyExpression.OutAccess) { // TODO: best_candidate can be null at this point INamedBlockVariable variable = null; @@ -6752,7 +6779,7 @@ namespace Mono.CSharp { if (!ResolveSetter (ec)) return null; - +/* if (NullShortCircuit && ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) { var lifted_type = LiftMemberType (ec, type); if (type != lifted_type) { @@ -6760,7 +6787,7 @@ namespace Mono.CSharp { return Nullable.Wrap.Create (this, lifted_type); } } - +*/ return this; } diff --git a/mcs/mcs/expression.cs b/mcs/mcs/expression.cs index d2fced11c68..69ab3e79db6 100644 --- a/mcs/mcs/expression.cs +++ b/mcs/mcs/expression.cs @@ -8849,11 +8849,6 @@ namespace Mono.CSharp return (type.Kind & dot_kinds) != 0 || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic; } - static bool IsNullPropagatingValid (TypeSpec type) - { - return TypeSpec.IsReferenceType (type) || type.IsNullableType; - } - public override Expression LookupNameExpression (ResolveContext rc, MemberLookupRestrictions restrictions) { var sn = expr as SimpleName; @@ -9355,6 +9350,8 @@ namespace Mono.CSharp this.Arguments = args; } + public bool NullPropagating { get; set; } + public override Location StartLocation { get { return Expr.StartLocation; @@ -9372,8 +9369,15 @@ namespace Mono.CSharp // Expression CreateAccessExpression (ResolveContext ec) { + if (NullPropagating && !IsNullPropagatingValid (type)) { + Error_OperatorCannotBeApplied (ec, loc, "?", type); + return null; + } + if (type.IsArray) - return (new ArrayAccess (this, loc)); + return new ArrayAccess (this, loc) { + NullShortCircuit = NullPropagating + }; if (type.IsPointer) return MakePointerAccess (ec, type); @@ -9388,7 +9392,9 @@ namespace Mono.CSharp var indexers = MemberCache.FindMembers (type, MemberCache.IndexerNameAlias, false); if (indexers != null || type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) { - return new IndexerExpr (indexers, type, this); + return new IndexerExpr (indexers, type, this) { + NullShortCircuit = NullPropagating + }; } if (type != InternalType.ErrorType) { @@ -9514,6 +9520,8 @@ namespace Mono.CSharp loc = l; } + public bool NullShortCircuit { get; set; } + public void AddressOf (EmitContext ec, AddressOp mode) { var ac = (ArrayContainer) ea.Expr.Type; @@ -9532,6 +9540,9 @@ namespace Mono.CSharp public override Expression CreateExpressionTree (ResolveContext ec) { + if (NullShortCircuit) + Error_NullShortCircuitInsideExpressionTree (ec); + return ea.CreateExpressionTree (ec); } @@ -9542,6 +9553,9 @@ namespace Mono.CSharp public override Expression DoResolveLValue (ResolveContext ec, Expression right_side) { + if (NullShortCircuit) + throw new NotSupportedException ("null propagating operator assignment"); + return DoResolve (ec); } @@ -9564,9 +9578,13 @@ namespace Mono.CSharp UnsafeError (ec, ea.Location); } + if (NullShortCircuit) + type = LiftMemberType (ec, type); + foreach (Argument a in ea.Arguments) { - if (a is NamedArgument) - ElementAccess.Error_NamedArgument ((NamedArgument) a, ec.Report); + var na = a as NamedArgument; + if (na != null) + ElementAccess.Error_NamedArgument (na, ec.Report); a.Expr = ConvertExpressionToArrayIndex (ec, a.Expr); } @@ -9589,30 +9607,35 @@ namespace Mono.CSharp // // Load the array arguments into the stack. // - void LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait) + InstanceEmitter LoadInstanceAndArguments (EmitContext ec, bool duplicateArguments, bool prepareAwait) { + InstanceEmitter ie; if (prepareAwait) { + ie = new InstanceEmitter (); ea.Expr = ea.Expr.EmitToField (ec); - } else if (duplicateArguments) { - ea.Expr.Emit (ec); - ec.Emit (OpCodes.Dup); - - var copy = new LocalTemporary (ea.Expr.Type); - copy.Store (ec); - ea.Expr = copy; } else { - ea.Expr.Emit (ec); + ie = new InstanceEmitter (ea.Expr, false); + ie.NullShortCircuit = NullShortCircuit; + ie.Emit (ec); + + if (duplicateArguments) { + ec.Emit (OpCodes.Dup); + + var copy = new LocalTemporary (ea.Expr.Type); + copy.Store (ec); + ea.Expr = copy; + } } var dup_args = ea.Arguments.Emit (ec, duplicateArguments, prepareAwait); if (dup_args != null) ea.Arguments = dup_args; + + return ie; } public void Emit (EmitContext ec, bool leave_copy) { - var ac = ea.Expr.Type as ArrayContainer; - if (prepared) { ec.EmitLoadFromPtr (type); } else { @@ -9620,8 +9643,12 @@ namespace Mono.CSharp LoadInstanceAndArguments (ec, false, true); } - LoadInstanceAndArguments (ec, false, false); + var ac = (ArrayContainer) ea.Expr.Type; + var inst = LoadInstanceAndArguments (ec, false, false); ec.EmitArrayLoad (ac); + + if (NullShortCircuit) + inst.EmitResultLift (ec, ((ArrayContainer) ea.Expr.Type).Element, false); } if (leave_copy) { @@ -9810,6 +9837,10 @@ namespace Mono.CSharp public override Expression CreateExpressionTree (ResolveContext ec) { + if (NullShortCircuit) { + Error_NullShortCircuitInsideExpressionTree (ec); + } + Arguments args = Arguments.CreateForExpressionTree (ec, arguments, InstanceExpression.CreateExpressionTree (ec), new TypeOfMethod (Getter, loc)); diff --git a/mcs/tests/test-null-operator-02.cs b/mcs/tests/test-null-operator-02.cs index de4158a3d2b..83cec417b91 100644 --- a/mcs/tests/test-null-operator-02.cs +++ b/mcs/tests/test-null-operator-02.cs @@ -11,22 +11,10 @@ class CI public string PropReference { get; set; } public event Action ev1; - - public int this [bool arg] { get { return 4; } } - public int? this [int? arg] { get { return arg; } } - public string this [string arg] { get { return arg; } } - -/* - void Foo () - { - var l = this?.Field; - } -*/ } class C { - /* static int TestProperty () { CI ci = null; @@ -34,16 +22,16 @@ class C var m2 = ci?.PropNullable; var m3 = ci?.PropReference; - ci?.Prop = 6; +// ci?.Prop = 6; ci = new CI (); m1 = ci?.Prop; m2 = ci?.PropNullable; m3 = ci?.PropReference; - ci?.Prop = 5; - if (ci.Prop != 5) - return 1; +// ci?.Prop = 5; +// if (ci.Prop != 5) +// return 1; // TODO: It's not allowed for now // ci?.Prop += 4; @@ -89,20 +77,11 @@ class C return 0; } -*/ -/* - static int TestIndexer () - { - CI ci = null; - var m1 = ci?[0]; - var m2 = ci?[(int?) 1]; - var m3 = ci?[""]; - } -*/ + static int Main () { int res; -/* + res = TestProperty (); if (res != 0) return 10 + res; @@ -114,7 +93,6 @@ class C res = TestEvent (); if (res != 0) return 30 + res; -*/ Console.WriteLine ("ok"); return 0; diff --git a/mcs/tests/test-null-operator-05.cs b/mcs/tests/test-null-operator-05.cs new file mode 100644 index 00000000000..5fa5c6ff9b4 --- /dev/null +++ b/mcs/tests/test-null-operator-05.cs @@ -0,0 +1,58 @@ +using System; + +class CI +{ + public string this [string i] { set { } get { return ""; } } + public int? this [int i] { set { } get { return 1; } } +} + +class C +{ + static int TestArrayAccess () + { + byte[] arr = null; + var v = arr? [0]; + if (v != null) + return 1; + + long?[] ar2 = null; + var v2 = ar2? [-1]; + if (v2 != null) + return 2; + +// TODO: Disabled for now? +// arr? [0] += 2; + return 0; + } + + static int TestIndexerAccess () + { + CI ci = null; + var v = ci? ["x"]; + if (v != null) + return 1; + + var v2 = ci? [0]; + if (v2 != null) + return 2; + +// TODO: Disabled for now? +// ci? [0] += 3; + return 0; + } + + static int Main () + { + int res; + res = TestArrayAccess (); + if (res != 0) + return 10 + res; + + res = TestIndexerAccess (); + if (res != 0) + return 20 + res; + + Console.WriteLine ("ok"); + return 0; + } +} \ No newline at end of file diff --git a/mcs/tests/ver-il-net_4_5.xml b/mcs/tests/ver-il-net_4_5.xml index 098d9472835..5f6c441e5d7 100644 --- a/mcs/tests/ver-il-net_4_5.xml +++ b/mcs/tests/ver-il-net_4_5.xml @@ -67267,26 +67267,26 @@ 42 - - 10 - - - 10 - - - 10 - 7 - 20 + 86 7 + + 168 + + + 168 + + + 50 + @@ -67315,6 +67315,39 @@ + + + + 2 + + + 14 + + + 2 + + + 15 + + + 7 + + + + + 114 + + + 93 + + + 64 + + + 7 + + +