--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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
--- /dev/null
+// 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<Action> e = () => c?.TestMethod ();
+ }
+}
--- /dev/null
+// CS8072: An expression tree cannot contain a null propagating operator
+// Line: 11
+
+using System;
+using System.Linq.Expressions;
+
+class C
+{
+ static int Main ()
+ {
+ Expression<Func<string, char?>> e = l => l?[1];
+ return 0;
+ }
+}
\ No newline at end of file
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
{
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;
{
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;
+ }
/// <summary>
/// Resolves an expression and performs semantic analysis on it.
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)
{
}
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))
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);
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (NullShortCircuit) {
+ Error_NullShortCircuitInsideExpressionTree (ec);
+ }
+
return CreateExpressionTree (ec, true);
}
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);
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (NullShortCircuit) {
+ Error_NullShortCircuitInsideExpressionTree (ec);
+ }
+
Arguments args;
if (IsSingleDimensionalArrayLength ()) {
args = new Arguments (1);
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;
if (!ResolveSetter (ec))
return null;
-
+/*
if (NullShortCircuit && ec.HasSet (ResolveContext.Options.CompoundAssignmentScope)) {
var lifted_type = LiftMemberType (ec, type);
if (type != lifted_type) {
return Nullable.Wrap.Create (this, lifted_type);
}
}
-
+*/
return this;
}
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;
this.Arguments = args;
}
+ public bool NullPropagating { get; set; }
+
public override Location StartLocation {
get {
return Expr.StartLocation;
//
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);
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) {
loc = l;
}
+ public bool NullShortCircuit { get; set; }
+
public void AddressOf (EmitContext ec, AddressOp mode)
{
var ac = (ArrayContainer) ea.Expr.Type;
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (NullShortCircuit)
+ Error_NullShortCircuitInsideExpressionTree (ec);
+
return ea.CreateExpressionTree (ec);
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
{
+ if (NullShortCircuit)
+ throw new NotSupportedException ("null propagating operator assignment");
+
return DoResolve (ec);
}
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);
}
//
// 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 {
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) {
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (NullShortCircuit) {
+ Error_NullShortCircuitInsideExpressionTree (ec);
+ }
+
Arguments args = Arguments.CreateForExpressionTree (ec, arguments,
InstanceExpression.CreateExpressionTree (ec),
new TypeOfMethod (Getter, loc));
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;
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;
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;
res = TestEvent ();
if (res != 0)
return 30 + res;
-*/
Console.WriteLine ("ok");
return 0;
--- /dev/null
+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
<method name="Void remove_ev1(System.Action)" attrs="2182">\r
<size>42</size>\r
</method>\r
- <method name="Int32 get_Item(Boolean)" attrs="2182">\r
- <size>10</size>\r
- </method>\r
- <method name="System.Nullable`1[System.Int32] get_Item(System.Nullable`1[System.Int32])" attrs="2182">\r
- <size>10</size>\r
- </method>\r
- <method name="System.String get_Item(System.String)" attrs="2182">\r
- <size>10</size>\r
- </method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
</type>\r
<type name="C">\r
<method name="Int32 Main()" attrs="145">\r
- <size>20</size>\r
+ <size>86</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
+ <method name="Int32 TestProperty()" attrs="145">\r
+ <size>168</size>\r
+ </method>\r
+ <method name="Int32 TestField()" attrs="145">\r
+ <size>168</size>\r
+ </method>\r
+ <method name="Int32 TestEvent()" attrs="145">\r
+ <size>50</size>\r
+ </method>\r
</type>\r
</test>\r
<test name="test-null-operator-03.cs">\r
</method>\r
</type>\r
</test>\r
+ <test name="test-null-operator-05.cs">\r
+ <type name="CI">\r
+ <method name="Void set_Item(System.String, System.String)" attrs="2182">\r
+ <size>2</size>\r
+ </method>\r
+ <method name="System.String get_Item(System.String)" attrs="2182">\r
+ <size>14</size>\r
+ </method>\r
+ <method name="Void set_Item(Int32, System.Nullable`1[System.Int32])" attrs="2182">\r
+ <size>2</size>\r
+ </method>\r
+ <method name="System.Nullable`1[System.Int32] get_Item(Int32)" attrs="2182">\r
+ <size>15</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ <type name="C">\r
+ <method name="Int32 TestArrayAccess()" attrs="145">\r
+ <size>114</size>\r
+ </method>\r
+ <method name="Int32 TestIndexerAccess()" attrs="145">\r
+ <size>93</size>\r
+ </method>\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>64</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-partial-01.cs">\r
<type name="Foo.Hello">\r
<method name="Void .ctor()" attrs="6278">\r