2
// nullable.cs: Nullable types support
4
// Authors: Martin Baulig (martin@ximian.com)
5
// Miguel de Icaza (miguel@ximian.com)
6
// Marek Safar (marek.safar@gmail.com)
8
// Dual licensed under the terms of the MIT X11 or GNU GPL
10
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11
// Copyright 2004-2008 Novell, Inc
12
// Copyright 2011 Xamarin Inc
18
using IKVM.Reflection.Emit;
20
using System.Reflection.Emit;
23
namespace Mono.CSharp.Nullable
25
public class NullableType : TypeExpr
27
readonly TypeSpec underlying;
29
public NullableType (TypeSpec type, Location loc)
31
this.underlying = type;
35
public override TypeSpec ResolveAsType (IMemberContext ec)
37
eclass = ExprClass.Type;
39
var otype = ec.Module.PredefinedTypes.Nullable.Resolve ();
43
TypeArguments args = new TypeArguments (new TypeExpression (underlying, loc));
44
GenericTypeExpr ctype = new GenericTypeExpr (otype, args, loc);
46
type = ctype.ResolveAsType (ec);
51
static class NullableInfo
53
public static MethodSpec GetConstructor (TypeSpec nullableType)
55
return (MethodSpec) MemberCache.FindMember (nullableType,
56
MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly);
59
public static MethodSpec GetHasValue (TypeSpec nullableType)
61
return (MethodSpec) MemberCache.FindMember (nullableType,
62
MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
65
public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType)
67
return (MethodSpec) MemberCache.FindMember (nullableType,
68
MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
72
// Don't use unless really required for correctness, see Unwrap::Emit
74
public static MethodSpec GetValue (TypeSpec nullableType)
76
return (MethodSpec) MemberCache.FindMember (nullableType,
77
MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
80
public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
82
return ((InflatedTypeSpec) nullableType).TypeArguments[0];
86
public class Unwrap : Expression, IMemoryLocation
91
readonly bool useDefaultValue;
93
Unwrap (Expression expr, bool useDefaultValue)
96
this.loc = expr.Location;
97
this.useDefaultValue = useDefaultValue;
99
type = NullableInfo.GetUnderlyingType (expr.Type);
100
eclass = expr.eclass;
103
public override bool ContainsEmitWithAwait ()
105
return expr.ContainsEmitWithAwait ();
108
public static Expression Create (Expression expr)
111
// Avoid unwraping and wraping of same type
113
Wrap wrap = expr as Wrap;
117
return Create (expr, false);
120
public static Unwrap Create (Expression expr, bool useDefaultValue)
122
return new Unwrap (expr, useDefaultValue);
125
public override Expression CreateExpressionTree (ResolveContext ec)
127
return expr.CreateExpressionTree (ec);
130
protected override Expression DoResolve (ResolveContext ec)
135
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
137
expr = expr.DoResolveLValue (ec, right_side);
141
public override void Emit (EmitContext ec)
145
var call = new CallEmitter ();
146
call.InstanceExpression = this;
149
// Using GetGetValueOrDefault is prefered because JIT can possibly
150
// inline it whereas Value property contains a throw which is very
151
// unlikely to be inlined
154
call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
156
call.EmitPredefined (ec, NullableInfo.GetValue (expr.Type), null);
159
public void EmitCheck (EmitContext ec)
163
var call = new CallEmitter ();
164
call.InstanceExpression = this;
166
call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
169
public override bool Equals (object obj)
171
Unwrap uw = obj as Unwrap;
172
return uw != null && expr.Equals (uw.expr);
175
public Expression Original {
181
public override int GetHashCode ()
183
return expr.GetHashCode ();
186
public override bool IsNull {
192
void Store (EmitContext ec)
197
if (expr is VariableReference)
201
LocalVariable.Store (ec);
204
public void Load (EmitContext ec)
206
if (expr is VariableReference)
209
LocalVariable.Emit (ec);
212
public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
214
return expr.MakeExpression (ctx);
217
public void AddressOf (EmitContext ec, AddressOp mode)
219
IMemoryLocation ml = expr as VariableReference;
221
ml.AddressOf (ec, mode);
223
LocalVariable.AddressOf (ec, mode);
227
// Keeps result of non-variable expression
229
LocalTemporary LocalVariable {
232
temp = new LocalTemporary (expr.Type);
239
// Calls get_Value method on nullable expression
241
public class UnwrapCall : CompositeExpression
243
public UnwrapCall (Expression expr)
248
protected override Expression DoResolve (ResolveContext rc)
253
type = NullableInfo.GetUnderlyingType (type);
258
public override void Emit (EmitContext ec)
260
var call = new CallEmitter ();
261
call.InstanceExpression = Child;
262
call.EmitPredefined (ec, NullableInfo.GetValue (Child.Type), null);
266
public class Wrap : TypeCast
268
private Wrap (Expression expr, TypeSpec type)
271
eclass = ExprClass.Value;
274
public override Expression CreateExpressionTree (ResolveContext ec)
276
TypeCast child_cast = child as TypeCast;
277
if (child_cast != null) {
279
return child_cast.CreateExpressionTree (ec);
282
return base.CreateExpressionTree (ec);
285
public static Expression Create (Expression expr, TypeSpec type)
288
// Avoid unwraping and wraping of the same type
290
Unwrap unwrap = expr as Unwrap;
291
if (unwrap != null && expr.Type == NullableInfo.GetUnderlyingType (type))
292
return unwrap.Original;
294
return new Wrap (expr, type);
297
public override void Emit (EmitContext ec)
300
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
305
// Represents null literal lifted to nullable type
307
public class LiftedNull : NullConstant, IMemoryLocation
309
private LiftedNull (TypeSpec nullable_type, Location loc)
310
: base (nullable_type, loc)
312
eclass = ExprClass.Value;
315
public static Constant Create (TypeSpec nullable, Location loc)
317
return new LiftedNull (nullable, loc);
320
public static Constant CreateFromExpression (ResolveContext ec, Expression e)
322
ec.Report.Warning (458, 2, e.Location, "The result of the expression is always `null' of type `{0}'",
323
TypeManager.CSharpName (e.Type));
325
return ReducedExpression.Create (Create (e.Type, e.Location), e);
328
public override void Emit (EmitContext ec)
330
// TODO: generate less temporary variables
331
LocalTemporary value_target = new LocalTemporary (type);
333
value_target.AddressOf (ec, AddressOp.Store);
334
ec.Emit (OpCodes.Initobj, type);
335
value_target.Emit (ec);
336
value_target.Release (ec);
339
public void AddressOf (EmitContext ec, AddressOp Mode)
341
LocalTemporary value_target = new LocalTemporary (type);
343
value_target.AddressOf (ec, AddressOp.Store);
344
ec.Emit (OpCodes.Initobj, type);
345
((IMemoryLocation) value_target).AddressOf (ec, Mode);
350
// Generic lifting expression, supports all S/S? -> T/T? cases
352
public class Lifted : Expression, IMemoryLocation
354
Expression expr, null_value;
357
public Lifted (Expression expr, Unwrap unwrap, TypeSpec type)
360
this.unwrap = unwrap;
361
this.loc = expr.Location;
365
public Lifted (Expression expr, Expression unwrap, TypeSpec type)
366
: this (expr, unwrap as Unwrap, type)
370
public override bool ContainsEmitWithAwait ()
372
return unwrap.ContainsEmitWithAwait ();
375
public override Expression CreateExpressionTree (ResolveContext ec)
377
return expr.CreateExpressionTree (ec);
380
protected override Expression DoResolve (ResolveContext ec)
383
// It's null when lifting non-nullable type
385
if (unwrap == null) {
386
// S -> T? is wrap only
387
if (type.IsNullableType)
388
return Wrap.Create (expr, type);
390
// S -> T can be simplified
394
// Wrap target for T?
395
if (type.IsNullableType) {
396
expr = Wrap.Create (expr, type);
400
null_value = LiftedNull.Create (type, loc);
401
} else if (TypeSpec.IsValueType (type)) {
402
null_value = LiftedNull.Create (type, loc);
404
null_value = new NullConstant (type, loc);
407
eclass = ExprClass.Value;
411
public override void Emit (EmitContext ec)
413
Label is_null_label = ec.DefineLabel ();
414
Label end_label = ec.DefineLabel ();
416
unwrap.EmitCheck (ec);
417
ec.Emit (OpCodes.Brfalse, is_null_label);
421
ec.Emit (OpCodes.Br, end_label);
422
ec.MarkLabel (is_null_label);
424
null_value.Emit (ec);
425
ec.MarkLabel (end_label);
428
public void AddressOf (EmitContext ec, AddressOp mode)
430
unwrap.AddressOf (ec, mode);
434
public class LiftedUnaryOperator : Unary, IMemoryLocation
437
Expression user_operator;
439
public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
440
: base (op, expr, loc)
444
public void AddressOf (EmitContext ec, AddressOp mode)
446
unwrap.AddressOf (ec, mode);
449
public override Expression CreateExpressionTree (ResolveContext ec)
451
if (user_operator != null)
452
return user_operator.CreateExpressionTree (ec);
454
if (Oper == Operator.UnaryPlus)
455
return Expr.CreateExpressionTree (ec);
457
return base.CreateExpressionTree (ec);
460
protected override Expression DoResolve (ResolveContext ec)
462
unwrap = Unwrap.Create (Expr, false);
466
Expression res = base.ResolveOperator (ec, unwrap);
468
if (user_operator == null)
471
res = Expr = LiftExpression (ec, Expr);
477
eclass = ExprClass.Value;
482
public override void Emit (EmitContext ec)
484
Label is_null_label = ec.DefineLabel ();
485
Label end_label = ec.DefineLabel ();
487
unwrap.EmitCheck (ec);
488
ec.Emit (OpCodes.Brfalse, is_null_label);
490
if (user_operator != null) {
491
user_operator.Emit (ec);
493
EmitOperator (ec, NullableInfo.GetUnderlyingType (type));
496
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
497
ec.Emit (OpCodes.Br_S, end_label);
499
ec.MarkLabel (is_null_label);
500
LiftedNull.Create (type, loc).Emit (ec);
502
ec.MarkLabel (end_label);
505
static Expression LiftExpression (ResolveContext ec, Expression expr)
507
var lifted_type = new NullableType (expr.Type, expr.Location);
508
if (lifted_type.ResolveAsType (ec) == null)
511
expr.Type = lifted_type.Type;
515
protected override Expression ResolveEnumOperator (ResolveContext ec, Expression expr, TypeSpec[] predefined)
517
expr = base.ResolveEnumOperator (ec, expr, predefined);
521
Expr = LiftExpression (ec, Expr);
522
return LiftExpression (ec, expr);
525
protected override Expression ResolveUserOperator (ResolveContext ec, Expression expr)
527
expr = base.ResolveUserOperator (ec, expr);
532
// When a user operator is of non-nullable type
534
if (Expr is Unwrap) {
535
user_operator = LiftExpression (ec, expr);
536
return user_operator;
543
public class LiftedBinaryOperator : Binary
545
Unwrap left_unwrap, right_unwrap;
546
Expression left_orig, right_orig;
547
Expression user_operator;
548
MethodSpec wrap_ctor;
550
public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right)
551
: base (op, left, right)
555
bool IsBitwiseBoolean {
557
return (Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) &&
558
((left_unwrap != null && left_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool) ||
559
(right_unwrap != null && right_unwrap.Type.BuiltinType == BuiltinTypeSpec.Type.Bool));
563
bool IsLeftNullLifted {
565
return (state & State.LeftNullLifted) != 0;
569
bool IsRightNullLifted {
571
return (state & State.RightNullLifted) != 0;
575
public override Expression CreateExpressionTree (ResolveContext ec)
577
if (user_operator != null)
578
return user_operator.CreateExpressionTree (ec);
580
return base.CreateExpressionTree (ec);
584
// CSC 2 has this behavior, it allows structs to be compared
585
// with the null literal *outside* of a generics context and
586
// inlines that as true or false.
588
Constant CreateNullConstant (ResolveContext ec, Expression expr)
590
// FIXME: Handle side effect constants
591
Constant c = new BoolConstant (ec.BuiltinTypes, Oper == Operator.Inequality, loc);
593
if ((Oper & Operator.EqualityMask) != 0) {
594
ec.Report.Warning (472, 2, loc, "The result of comparing value type `{0}' with null is always `{1}'",
595
TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ());
597
ec.Report.Warning (464, 2, loc, "The result of comparing type `{0}' with null is always `{1}'",
598
TypeManager.CSharpName (expr.Type), c.GetValueAsLiteral ());
601
return ReducedExpression.Create (c, this);
604
protected override Expression DoResolve (ResolveContext ec)
606
if ((Oper & Operator.LogicalMask) != 0) {
607
Error_OperatorCannotBeApplied (ec, left, right);
611
bool use_default_call = (Oper & (Operator.BitwiseMask | Operator.EqualityMask)) != 0;
613
if (left.Type.IsNullableType) {
614
left = left_unwrap = Unwrap.Create (left, use_default_call);
620
if (right.Type.IsNullableType) {
621
right = right_unwrap = Unwrap.Create (right, use_default_call);
627
// Some details are in 6.4.2, 7.2.7
628
// Arguments can be lifted for equal operators when the return type is bool and both
629
// arguments are of same type
631
if (left_orig is NullLiteral) {
633
state |= State.LeftNullLifted;
634
type = ec.BuiltinTypes.Bool;
637
if (right_orig.IsNull) {
638
if ((Oper & Operator.ShiftMask) != 0)
639
right = new EmptyExpression (ec.BuiltinTypes.Int);
643
state |= State.RightNullLifted;
644
type = ec.BuiltinTypes.Bool;
647
eclass = ExprClass.Value;
648
return DoResolveCore (ec, left_orig, right_orig);
651
void EmitBitwiseBoolean (EmitContext ec)
653
Label load_left = ec.DefineLabel ();
654
Label load_right = ec.DefineLabel ();
655
Label end_label = ec.DefineLabel ();
657
// null & value, null | value
658
if (left_unwrap == null) {
659
left_unwrap = right_unwrap;
664
left_unwrap.Emit (ec);
665
ec.Emit (OpCodes.Brtrue, load_right);
667
// value & null, value | null
668
if (right_unwrap != null) {
669
right_unwrap.Emit (ec);
670
ec.Emit (OpCodes.Brtrue_S, load_left);
673
left_unwrap.EmitCheck (ec);
674
ec.Emit (OpCodes.Brfalse_S, load_right);
677
ec.MarkLabel (load_left);
679
if (Oper == Operator.BitwiseAnd) {
680
left_unwrap.Load (ec);
682
if (right_unwrap == null) {
684
if (right is EmptyConstantCast || right is EmptyCast)
685
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
687
right_unwrap.Load (ec);
688
right_unwrap = left_unwrap;
691
ec.Emit (OpCodes.Br_S, end_label);
694
ec.MarkLabel (load_right);
695
if (right_unwrap == null) {
696
if (Oper == Operator.BitwiseAnd) {
698
if (right is EmptyConstantCast || right is EmptyCast)
699
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
701
left_unwrap.Load (ec);
704
right_unwrap.Load (ec);
707
ec.MarkLabel (end_label);
711
// Emits optimized equality or inequality operator when possible
713
void EmitEquality (EmitContext ec)
716
// Either left or right is null
718
if (left_unwrap != null && (IsRightNullLifted || right.IsNull)) {
719
left_unwrap.EmitCheck (ec);
720
if (Oper == Binary.Operator.Equality) {
722
ec.Emit (OpCodes.Ceq);
727
if (right_unwrap != null && (IsLeftNullLifted || left.IsNull)) {
728
right_unwrap.EmitCheck (ec);
729
if (Oper == Binary.Operator.Equality) {
731
ec.Emit (OpCodes.Ceq);
736
Label dissimilar_label = ec.DefineLabel ();
737
Label end_label = ec.DefineLabel ();
739
if (user_operator != null) {
740
user_operator.Emit (ec);
741
ec.Emit (Oper == Operator.Equality ? OpCodes.Brfalse_S : OpCodes.Brtrue_S, dissimilar_label);
743
if (ec.HasSet (BuilderContext.Options.AsyncBody) && right.ContainsEmitWithAwait ()) {
744
left = left.EmitToField (ec);
745
right = right.EmitToField (ec);
751
ec.Emit (OpCodes.Bne_Un_S, dissimilar_label);
754
if (left_unwrap != null)
755
left_unwrap.EmitCheck (ec);
757
if (right_unwrap != null)
758
right_unwrap.EmitCheck (ec);
760
if (left_unwrap != null && right_unwrap != null) {
761
if (Oper == Operator.Inequality)
762
ec.Emit (OpCodes.Xor);
764
ec.Emit (OpCodes.Ceq);
766
if (Oper == Operator.Inequality) {
768
ec.Emit (OpCodes.Ceq);
772
ec.Emit (OpCodes.Br_S, end_label);
774
ec.MarkLabel (dissimilar_label);
775
if (Oper == Operator.Inequality)
780
ec.MarkLabel (end_label);
783
public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
786
ec.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
789
public override void Emit (EmitContext ec)
792
// Optimize same expression operation
794
if (right_unwrap != null && right.Equals (left))
795
right_unwrap = left_unwrap;
797
if (user_operator == null && IsBitwiseBoolean) {
798
EmitBitwiseBoolean (ec);
802
if ((Oper & Operator.EqualityMask) != 0) {
807
Label is_null_label = ec.DefineLabel ();
808
Label end_label = ec.DefineLabel ();
810
if (left_unwrap != null) {
811
left_unwrap.EmitCheck (ec);
812
ec.Emit (OpCodes.Brfalse, is_null_label);
816
// Don't emit HasValue check when left and right expressions are same
818
if (right_unwrap != null && !left.Equals (right)) {
819
right_unwrap.EmitCheck (ec);
820
ec.Emit (OpCodes.Brfalse, is_null_label);
823
EmitOperator (ec, left.Type);
825
if (wrap_ctor != null)
826
ec.Emit (OpCodes.Newobj, wrap_ctor);
828
ec.Emit (OpCodes.Br_S, end_label);
829
ec.MarkLabel (is_null_label);
831
if ((Oper & Operator.ComparisonMask) != 0) {
834
LiftedNull.Create (type, loc).Emit (ec);
837
ec.MarkLabel (end_label);
840
protected override void EmitOperator (EmitContext ec, TypeSpec l)
842
if (user_operator != null) {
843
user_operator.Emit (ec);
847
if (left.Type.IsNullableType) {
848
l = NullableInfo.GetUnderlyingType (left.Type);
849
left = EmptyCast.Create (left, l);
852
if (right.Type.IsNullableType) {
853
right = EmptyCast.Create (right, NullableInfo.GetUnderlyingType (right.Type));
856
base.EmitOperator (ec, l);
859
Expression LiftResult (ResolveContext ec, Expression res_expr)
861
TypeSpec lifted_type;
864
// Avoid double conversion
866
if (left_unwrap == null || IsLeftNullLifted || left_unwrap.Type != left.Type || (left_unwrap != null && IsRightNullLifted)) {
867
lifted_type = new NullableType (left.Type, loc).ResolveAsType (ec);
868
if (lifted_type == null)
871
if (left is UserCast || left is EmptyCast || left is OpcodeCast)
872
left.Type = lifted_type;
874
left = EmptyCast.Create (left, lifted_type);
877
if (left != right && (right_unwrap == null || IsRightNullLifted || right_unwrap.Type != right.Type || (right_unwrap != null && IsLeftNullLifted))) {
878
lifted_type = new NullableType (right.Type, loc).ResolveAsType (ec);
879
if (lifted_type == null)
883
if (r is ReducedExpression)
884
r = ((ReducedExpression) r).OriginalExpression;
886
if (r is UserCast || r is EmptyCast || r is OpcodeCast)
887
r.Type = lifted_type;
889
right = EmptyCast.Create (right, lifted_type);
892
if ((Oper & Operator.ComparisonMask) == 0) {
893
lifted_type = new NullableType (res_expr.Type, loc).ResolveAsType (ec);
894
if (lifted_type == null)
897
wrap_ctor = NullableInfo.GetConstructor (lifted_type);
898
type = res_expr.Type = lifted_type;
901
if (IsLeftNullLifted) {
902
left = LiftedNull.Create (right.Type, left.Location);
905
// Special case for bool?, the result depends on both null right side and left side value
907
if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) {
911
if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
912
return LiftedNull.CreateFromExpression (ec, res_expr);
915
// Value types and null comparison
917
if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
918
return CreateNullConstant (ec, right_orig);
921
if (IsRightNullLifted) {
922
right = LiftedNull.Create (left.Type, right.Location);
925
// Special case for bool?, the result depends on both null right side and left side value
927
if ((Oper == Operator.BitwiseAnd || Oper == Operator.BitwiseOr) && NullableInfo.GetUnderlyingType (type).BuiltinType == BuiltinTypeSpec.Type.Bool) {
931
if ((Oper & (Operator.ArithmeticMask | Operator.ShiftMask | Operator.BitwiseMask)) != 0)
932
return LiftedNull.CreateFromExpression (ec, res_expr);
935
// Value types and null comparison
937
if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
938
return CreateNullConstant (ec, left_orig);
944
protected override Expression ResolveOperatorPredefined (ResolveContext ec, Binary.PredefinedOperator [] operators, bool primitives_only, TypeSpec enum_type)
946
Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only, enum_type);
948
if (e == this || enum_type != null)
949
return LiftResult (ec, e);
952
// 7.9.9 Equality operators and null
954
// The == and != operators permit one operand to be a value of a nullable type and
955
// the other to be the null literal, even if no predefined or user-defined operator
956
// (in unlifted or lifted form) exists for the operation.
958
if (e == null && (Oper & Operator.EqualityMask) != 0) {
959
if ((IsLeftNullLifted && right_unwrap != null) || (IsRightNullLifted && left_unwrap != null))
960
return LiftResult (ec, this);
966
protected override Expression ResolveUserOperator (ResolveContext ec, Expression left, Expression right)
969
// Try original types first for exact match without unwrapping
971
Expression expr = base.ResolveUserOperator (ec, left_orig, right_orig);
975
State orig_state = state;
978
// One side is a nullable type, try to match underlying types
980
if (left_unwrap != null || right_unwrap != null || (state & (State.RightNullLifted | State.LeftNullLifted)) != 0) {
981
expr = base.ResolveUserOperator (ec, left, right);
988
// Lift the result in the case it can be null and predefined or user operator
989
// result type is of a value type
991
if (!TypeSpec.IsValueType (expr.Type))
994
if (state != orig_state)
997
expr = LiftResult (ec, expr);
998
if (expr is Constant)
1002
user_operator = expr;
1007
public class NullCoalescingOperator : Expression
1009
Expression left, right;
1012
public NullCoalescingOperator (Expression left, Expression right)
1016
this.loc = left.Location;
1019
public Expression LeftExpression {
1025
public Expression RightExpression {
1031
public override Expression CreateExpressionTree (ResolveContext ec)
1033
if (left is NullLiteral)
1034
ec.Report.Error (845, loc, "An expression tree cannot contain a coalescing operator with null left side");
1036
UserCast uc = left as UserCast;
1037
Expression conversion = null;
1041
Arguments c_args = new Arguments (2);
1042
c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
1043
c_args.Add (new Argument (left.CreateExpressionTree (ec)));
1044
conversion = CreateExpressionFactoryCall (ec, "Lambda", c_args);
1047
Arguments args = new Arguments (3);
1048
args.Add (new Argument (left.CreateExpressionTree (ec)));
1049
args.Add (new Argument (right.CreateExpressionTree (ec)));
1050
if (conversion != null)
1051
args.Add (new Argument (conversion));
1053
return CreateExpressionFactoryCall (ec, "Coalesce", args);
1056
Expression ConvertExpression (ResolveContext ec)
1058
// TODO: ImplicitConversionExists should take care of this
1059
if (left.eclass == ExprClass.MethodGroup)
1062
TypeSpec ltype = left.Type;
1065
// If left is a nullable type and an implicit conversion exists from right to underlying type of left,
1066
// the result is underlying type of left
1068
if (ltype.IsNullableType) {
1069
unwrap = Unwrap.Create (left, false);
1074
// Reduce (left ?? null) to left
1077
return ReducedExpression.Create (left, this);
1079
if (Convert.ImplicitConversionExists (ec, right, unwrap.Type)) {
1084
// If right is a dynamic expression, the result type is dynamic
1086
if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1089
// Need to box underlying value type
1090
left = Convert.ImplicitBoxingConversion (left, ltype, type);
1094
right = Convert.ImplicitConversion (ec, right, ltype, loc);
1098
} else if (TypeSpec.IsReferenceType (ltype)) {
1099
if (Convert.ImplicitConversionExists (ec, right, ltype)) {
1101
// If right is a dynamic expression, the result type is dynamic
1103
if (right.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
1109
// Reduce ("foo" ?? expr) to expression
1111
Constant lc = left as Constant;
1112
if (lc != null && !lc.IsDefaultValue)
1113
return ReducedExpression.Create (lc, this);
1116
// Reduce (left ?? null) to left OR (null-constant ?? right) to right
1118
if (right.IsNull || lc != null)
1119
return ReducedExpression.Create (lc != null ? right : left, this);
1121
right = Convert.ImplicitConversion (ec, right, ltype, loc);
1127
// Special case null ?? null
1129
if (ltype == right.Type) {
1137
TypeSpec rtype = right.Type;
1138
if (!Convert.ImplicitConversionExists (ec, unwrap != null ? unwrap : left, rtype) || right.eclass == ExprClass.MethodGroup)
1142
// Reduce (null ?? right) to right
1145
return ReducedExpression.Create (right, this).Resolve (ec);
1147
left = Convert.ImplicitConversion (ec, unwrap != null ? unwrap : left, rtype, loc);
1152
public override bool ContainsEmitWithAwait ()
1155
return unwrap.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1157
return left.ContainsEmitWithAwait () || right.ContainsEmitWithAwait ();
1160
protected override Expression DoResolve (ResolveContext ec)
1162
left = left.Resolve (ec);
1163
right = right.Resolve (ec);
1165
if (left == null || right == null)
1168
eclass = ExprClass.Value;
1170
Expression e = ConvertExpression (ec);
1172
Binary.Error_OperatorCannotBeApplied (ec, left, right, "??", loc);
1179
public override void Emit (EmitContext ec)
1181
Label end_label = ec.DefineLabel ();
1183
if (unwrap != null) {
1184
Label is_null_label = ec.DefineLabel ();
1186
unwrap.EmitCheck (ec);
1187
ec.Emit (OpCodes.Brfalse, is_null_label);
1190
ec.Emit (OpCodes.Br, end_label);
1192
ec.MarkLabel (is_null_label);
1195
ec.MarkLabel (end_label);
1200
ec.Emit (OpCodes.Dup);
1202
// Only to make verifier happy
1203
if (left.Type.IsGenericParameter)
1204
ec.Emit (OpCodes.Box, left.Type);
1206
ec.Emit (OpCodes.Brtrue, end_label);
1208
ec.Emit (OpCodes.Pop);
1211
ec.MarkLabel (end_label);
1214
protected override void CloneTo (CloneContext clonectx, Expression t)
1216
NullCoalescingOperator target = (NullCoalescingOperator) t;
1218
target.left = left.Clone (clonectx);
1219
target.right = right.Clone (clonectx);
1222
public override object Accept (StructuralVisitor visitor)
1224
return visitor.Visit (this);
1228
class LiftedUnaryMutator : UnaryMutator
1230
public LiftedUnaryMutator (Mode mode, Expression expr, Location loc)
1231
: base (mode, expr, loc)
1235
protected override Expression DoResolve (ResolveContext ec)
1237
var orig_expr = expr;
1239
expr = Unwrap.Create (expr);
1241
var res = base.DoResolveOperation (ec);
1249
protected override void EmitOperation (EmitContext ec)
1251
Label is_null_label = ec.DefineLabel ();
1252
Label end_label = ec.DefineLabel ();
1254
LocalTemporary lt = new LocalTemporary (type);
1256
// Value is on the stack
1259
var call = new CallEmitter ();
1260
call.InstanceExpression = lt;
1261
call.EmitPredefined (ec, NullableInfo.GetHasValue (expr.Type), null);
1263
ec.Emit (OpCodes.Brfalse, is_null_label);
1265
call = new CallEmitter ();
1266
call.InstanceExpression = lt;
1267
call.EmitPredefined (ec, NullableInfo.GetGetValueOrDefault (expr.Type), null);
1271
base.EmitOperation (ec);
1273
ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
1274
ec.Emit (OpCodes.Br_S, end_label);
1276
ec.MarkLabel (is_null_label);
1277
LiftedNull.Create (type, loc).Emit (ec);
1279
ec.MarkLabel (end_label);