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 // Licensed under the terms of the GNU GPL
10 // (C) 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
11 // (C) 2004 Novell, Inc
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 namespace Mono.CSharp {
21 public class NullableType : TypeExpr
23 Expression underlying;
25 public NullableType (Expression underlying, Location l)
27 this.underlying = underlying;
30 eclass = ExprClass.Type;
33 public NullableType (Type type, Location loc)
34 : this (new TypeExpression (type, loc), loc)
37 public override string Name {
38 get { return underlying.ToString () + "?"; }
41 public override string FullName {
42 get { return underlying.ToString () + "?"; }
45 protected override TypeExpr DoResolveAsTypeStep (IResolveContext ec)
47 TypeArguments args = new TypeArguments (loc);
48 args.Add (underlying);
50 if (TypeManager.generic_nullable_type == null) {
51 TypeManager.generic_nullable_type = TypeManager.CoreLookupType (
52 "System", "Nullable`1", Kind.Struct, true);
55 ConstructedType ctype = new ConstructedType (TypeManager.generic_nullable_type, args, loc);
56 return ctype.ResolveAsTypeTerminal (ec, false);
60 public abstract class Nullable
62 public sealed class NullableInfo
64 public readonly Type Type;
65 public readonly Type UnderlyingType;
66 public readonly MethodInfo HasValue;
67 public readonly MethodInfo Value;
68 public readonly MethodInfo GetValueOrDefault;
69 public readonly ConstructorInfo Constructor;
71 public NullableInfo (Type type)
74 UnderlyingType = TypeManager.GetTypeArguments (type) [0];
76 PropertyInfo has_value_pi = TypeManager.GetPredefinedProperty (type, "HasValue", Location.Null);
77 PropertyInfo value_pi = TypeManager.GetPredefinedProperty (type, "Value", Location.Null);
78 GetValueOrDefault = TypeManager.GetPredefinedMethod (type, "GetValueOrDefault", Location.Null, Type.EmptyTypes);
80 HasValue = has_value_pi.GetGetMethod (false);
81 Value = value_pi.GetGetMethod (false);
83 if (UnderlyingType.Module == CodeGen.Module.Builder) {
84 Type o_type = TypeManager.DropGenericTypeArguments (type);
85 Constructor = TypeBuilder.GetConstructor (type,
86 TypeManager.GetPredefinedConstructor (o_type, Location.Null, o_type.GetGenericArguments ()));
90 Constructor = type.GetConstructor (new Type[] { UnderlyingType });
94 public class HasValue : Expression
99 private HasValue (Expression expr)
104 public static Expression Create (Expression expr, EmitContext ec)
106 return new HasValue (expr).Resolve (ec);
109 public override void Emit (EmitContext ec)
111 IMemoryLocation memory_loc = expr as IMemoryLocation;
112 if (memory_loc == null) {
113 LocalTemporary temp = new LocalTemporary (expr.Type);
118 memory_loc.AddressOf (ec, AddressOp.LoadStore);
119 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
122 public override Expression DoResolve (EmitContext ec)
124 this.info = new NullableInfo (expr.Type);
126 type = TypeManager.bool_type;
127 eclass = expr.eclass;
132 public class Unwrap : Expression, IMemoryLocation, IAssignMethod
140 protected Unwrap (Expression expr)
143 this.loc = expr.Location;
146 public static Unwrap Create (Expression expr, EmitContext ec)
148 return new Unwrap (expr).Resolve (ec) as Unwrap;
151 public override Expression CreateExpressionTree (EmitContext ec)
153 return expr.CreateExpressionTree (ec);
156 public override Expression DoResolve (EmitContext ec)
161 temp = new LocalTemporary (expr.Type);
163 info = new NullableInfo (expr.Type);
164 type = info.UnderlyingType;
165 eclass = expr.eclass;
169 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
171 return DoResolve (ec);
174 public override void Emit (EmitContext ec)
176 AddressOf (ec, AddressOp.LoadStore);
177 ec.ig.EmitCall (OpCodes.Call, info.Value, null);
180 public void EmitCheck (EmitContext ec)
182 AddressOf (ec, AddressOp.LoadStore);
183 ec.ig.EmitCall (OpCodes.Call, info.HasValue, null);
186 public void EmitGetValueOrDefault (EmitContext ec)
188 AddressOf (ec, AddressOp.LoadStore);
189 ec.ig.EmitCall (OpCodes.Call, info.GetValueOrDefault, null);
192 public override bool Equals (object obj)
194 Unwrap uw = obj as Unwrap;
195 return uw != null && expr.Equals (uw.expr);
198 public Expression Original {
204 public override int GetHashCode ()
206 return expr.GetHashCode ();
209 public override bool IsNull {
215 public void Store (EmitContext ec)
220 void create_temp (EmitContext ec)
222 if ((temp != null) && !has_temp) {
229 public void LoadTemporary (EmitContext ec)
234 public void AddressOf (EmitContext ec, AddressOp mode)
238 temp.AddressOf (ec, AddressOp.LoadStore);
240 ((IMemoryLocation) expr).AddressOf (ec, AddressOp.LoadStore);
243 public void Emit (EmitContext ec, bool leave_copy)
256 public void EmitAssign (EmitContext ec, Expression source,
257 bool leave_copy, bool prepare_for_load)
259 InternalWrap wrap = new InternalWrap (source, info, loc);
260 ((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
263 protected class InternalWrap : Expression
265 public Expression expr;
266 public NullableInfo info;
268 public InternalWrap (Expression expr, NullableInfo info, Location loc)
275 eclass = ExprClass.Value;
278 public override Expression DoResolve (EmitContext ec)
283 public override void Emit (EmitContext ec)
286 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
291 public class Wrap : EmptyCast
293 readonly NullableInfo info;
295 protected Wrap (Expression expr, Type type)
298 info = new NullableInfo (type);
299 eclass = ExprClass.Value;
302 public static new Expression Create (Expression expr, Type type)
304 return new Wrap (expr, type);
307 public override void Emit (EmitContext ec)
310 ec.ig.Emit (OpCodes.Newobj, info.Constructor);
314 class LiftedWrap : Wrap
316 public LiftedWrap (Expression expr, Type type)
321 public override Expression CreateExpressionTree (EmitContext ec)
323 return child.CreateExpressionTree (ec);
328 // Represents null literal lifted to nullable type
330 public class LiftedNull : EmptyConstantCast, IMemoryLocation
332 private LiftedNull (Type nullable_type, Location loc)
333 : base (new NullLiteral (loc), nullable_type)
335 eclass = ExprClass.Value;
338 public static Constant Create (Type nullable, Location loc)
340 return new LiftedNull (nullable, loc);
343 public override Expression CreateExpressionTree (EmitContext ec)
345 ArrayList args = new ArrayList (2);
346 args.Add (new Argument (this));
347 args.Add (new Argument (new TypeOf (new TypeExpression (type, loc), loc)));
349 return CreateExpressionFactoryCall ("Constant", args);
352 public override void Emit (EmitContext ec)
354 // TODO: generate less temporary variables
355 LocalTemporary value_target = new LocalTemporary (type);
357 value_target.AddressOf (ec, AddressOp.Store);
358 ec.ig.Emit (OpCodes.Initobj, type);
359 value_target.Emit (ec);
362 public void AddressOf (EmitContext ec, AddressOp Mode)
364 LocalTemporary value_target = new LocalTemporary (type);
366 value_target.AddressOf (ec, AddressOp.Store);
367 ec.ig.Emit (OpCodes.Initobj, type);
368 ((IMemoryLocation) value_target).AddressOf (ec, Mode);
372 public abstract class Lifted : Expression, IMemoryLocation
374 Expression expr, underlying, wrap, null_value;
377 protected Lifted (Expression expr, Location loc)
383 public override Expression CreateExpressionTree (EmitContext ec)
385 return underlying.CreateExpressionTree (ec);
388 public override Expression DoResolve (EmitContext ec)
390 expr = expr.Resolve (ec);
394 unwrap = Unwrap.Create (expr, ec);
398 underlying = ResolveUnderlying (unwrap, ec);
399 if (underlying == null)
402 TypeExpr target_type = new NullableType (underlying.Type, loc);
403 target_type = target_type.ResolveAsTypeTerminal (ec, false);
404 if (target_type == null)
407 wrap = Wrap.Create (underlying, target_type.Type);
411 null_value = LiftedNull.Create (wrap.Type, loc);
414 eclass = ExprClass.Value;
418 protected abstract Expression ResolveUnderlying (Expression unwrap, EmitContext ec);
420 public override void Emit (EmitContext ec)
422 ILGenerator ig = ec.ig;
423 Label is_null_label = ig.DefineLabel ();
424 Label end_label = ig.DefineLabel ();
426 unwrap.EmitCheck (ec);
427 ig.Emit (OpCodes.Brfalse, is_null_label);
430 ig.Emit (OpCodes.Br, end_label);
432 ig.MarkLabel (is_null_label);
433 null_value.Emit (ec);
435 ig.MarkLabel (end_label);
438 public void AddressOf (EmitContext ec, AddressOp mode)
440 unwrap.AddressOf (ec, mode);
444 public class LiftedConversion : Lifted
446 public readonly bool IsUser;
447 public readonly bool IsExplicit;
448 public readonly Type TargetType;
450 public LiftedConversion (Expression expr, Type target_type, bool is_user,
451 bool is_explicit, Location loc)
454 this.IsUser = is_user;
455 this.IsExplicit = is_explicit;
456 this.TargetType = target_type;
459 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
461 Type type = TypeManager.GetTypeArguments (TargetType) [0];
464 return Convert.UserDefinedConversion (ec, unwrap, type, loc, IsExplicit);
467 return Convert.ExplicitConversion (ec, unwrap, type, loc);
469 return Convert.ImplicitConversion (ec, unwrap, type, loc);
474 public class LiftedUnaryOperator : Lifted
476 public readonly Unary.Operator Oper;
478 public LiftedUnaryOperator (Unary.Operator op, Expression expr, Location loc)
484 protected override Expression ResolveUnderlying (Expression unwrap, EmitContext ec)
486 return new Unary (Oper, unwrap, loc).Resolve (ec);
490 public class LiftedBinaryOperator : Binary
492 Unwrap left_unwrap, right_unwrap;
493 bool left_null_lifted, right_null_lifted;
494 Expression left_orig, right_orig;
496 public LiftedBinaryOperator (Binary.Operator op, Expression left, Expression right,
498 : base (op, left, right)
504 // CSC 2 has this behavior, it allows structs to be compared
505 // with the null literal *outside* of a generics context and
506 // inlines that as true or false.
508 Expression CreateNullConstant (Expression expr)
510 // FIXME: Handle side effect constants
511 Constant c = new BoolConstant (Oper == Operator.Inequality, loc);
513 if ((Oper & Operator.EqualityMask) != 0) {
514 Report.Warning (472, 2, loc, "The result of comparing `{0}' against null is always `{1}'. " +
515 "This operation is undocumented and it is temporary supported for compatibility reasons only",
516 expr.GetSignatureForError (), c.AsString ());
518 Report.Warning (464, 2, loc, "The result of comparing type `{0}' against null is always `{1}'",
519 expr.GetSignatureForError (), c.AsString ());
522 return ReducedExpression.Create (c, this);
525 public override Expression DoResolve (EmitContext ec)
527 if (eclass != ExprClass.Invalid)
530 // TODO: How does it work with use-operators?
531 if ((Oper == Binary.Operator.LogicalAnd) ||
532 (Oper == Binary.Operator.LogicalOr)) {
533 Error_OperatorCannotBeApplied (left, right);
538 if (TypeManager.IsNullableType (left.Type)) {
539 left = left_unwrap = Unwrap.Create (left, ec);
545 if (TypeManager.IsNullableType (right.Type)) {
546 right = right_unwrap = Unwrap.Create (right, ec);
551 if (left is NullLiteral) {
553 left_null_lifted = true;
556 if (right is NullLiteral) {
558 right_null_lifted = true;
561 eclass = ExprClass.Value;
562 Expression expr = DoResolveCore (ec, left_orig, right_orig);
563 if (expr != this || (Oper & Operator.ComparisonMask) != 0)
566 TypeExpr target_type = new NullableType (type, loc);
567 target_type = target_type.ResolveAsTypeTerminal (ec, false);
568 if (target_type == null)
571 type = target_type.Type;
575 void EmitBitwiseBoolean (EmitContext ec)
577 ILGenerator ig = ec.ig;
579 Label load_left = ig.DefineLabel ();
580 Label load_right = ig.DefineLabel ();
581 Label end_label = ig.DefineLabel ();
583 left_unwrap.EmitGetValueOrDefault (ec);
584 ig.Emit (OpCodes.Brtrue_S, load_right);
586 right_unwrap.EmitGetValueOrDefault (ec);
587 ig.Emit (OpCodes.Brtrue_S, load_left);
589 left_unwrap.EmitCheck (ec);
590 ig.Emit (OpCodes.Brfalse_S, load_right);
593 ig.MarkLabel (load_left);
595 if (Oper == Operator.BitwiseAnd) {
596 left_unwrap.LoadTemporary (ec);
598 right_unwrap.LoadTemporary (ec);
599 right_unwrap = left_unwrap;
601 ig.Emit (OpCodes.Br_S, end_label);
604 ig.MarkLabel (load_right);
605 right_unwrap.LoadTemporary (ec);
607 ig.MarkLabel (end_label);
610 void EmitEquality (EmitContext ec)
612 ILGenerator ig = ec.ig;
614 Label both_have_value_label = ig.DefineLabel ();
615 Label end_label = ig.DefineLabel ();
618 // Both are nullable types
620 if (left_unwrap != null && right_unwrap != null && !right.IsNull && !left.IsNull) {
621 Label dissimilar_label = ig.DefineLabel ();
623 left_unwrap.EmitGetValueOrDefault (ec);
624 right_unwrap.EmitGetValueOrDefault (ec);
625 ig.Emit (OpCodes.Bne_Un_S, dissimilar_label);
627 left_unwrap.EmitCheck (ec);
628 right_unwrap.EmitCheck (ec);
629 if (Oper == Operator.Inequality)
630 ig.Emit (OpCodes.Xor);
632 ig.Emit (OpCodes.Ceq);
634 ig.Emit (OpCodes.Br_S, end_label);
636 ig.MarkLabel (dissimilar_label);
637 if (Oper == Operator.Inequality)
638 ig.Emit (OpCodes.Ldc_I4_1);
640 ig.Emit (OpCodes.Ldc_I4_0);
642 ig.MarkLabel (end_label);
647 // Either left or right is nullable
649 if (left_unwrap != null) {
650 left_unwrap.EmitCheck (ec);
652 if (Oper == Binary.Operator.Equality) {
653 ig.Emit (OpCodes.Ldc_I4_0);
654 ig.Emit (OpCodes.Ceq);
658 ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
660 right_unwrap.EmitCheck (ec);
662 if (Oper == Binary.Operator.Equality) {
663 ig.Emit (OpCodes.Ldc_I4_0);
664 ig.Emit (OpCodes.Ceq);
668 ig.Emit (OpCodes.Brtrue_S, both_have_value_label);
671 if (Oper == Binary.Operator.Equality)
672 ig.Emit (OpCodes.Ldc_I4_0);
674 ig.Emit (OpCodes.Ldc_I4_1);
675 ig.Emit (OpCodes.Br, end_label);
677 ig.MarkLabel (both_have_value_label);
680 ig.MarkLabel (end_label);
683 void EmitComparision (EmitContext ec)
685 ILGenerator ig = ec.ig;
687 Label is_null_label = ig.DefineLabel ();
688 Label end_label = ig.DefineLabel ();
690 if (left_unwrap != null) {
691 left_unwrap.EmitCheck (ec);
692 ig.Emit (OpCodes.Brfalse, is_null_label);
695 if (right_unwrap != null) {
696 right_unwrap.EmitCheck (ec);
697 ig.Emit (OpCodes.Brfalse, is_null_label);
701 ig.Emit (OpCodes.Br_S, end_label);
703 ig.MarkLabel (is_null_label);
704 ig.Emit (OpCodes.Ldc_I4_0);
706 ig.MarkLabel (end_label);
709 public override void EmitBranchable (EmitContext ec, Label target, bool onTrue)
712 ec.ig.Emit (onTrue ? OpCodes.Brtrue : OpCodes.Brfalse, target);
715 public override void Emit (EmitContext ec)
717 if (left_unwrap != null)
718 left_unwrap.Store (ec);
719 if (right_unwrap != null)
720 right_unwrap.Store (ec);
722 if (IsBitwiseBoolean) {
723 EmitBitwiseBoolean (ec);
727 if ((Oper & Operator.EqualityMask) != 0) {
732 if ((Oper & Operator.ComparisonMask) != 0) {
733 EmitComparision (ec);
737 ILGenerator ig = ec.ig;
739 Label is_null_label = ig.DefineLabel ();
740 Label end_label = ig.DefineLabel ();
742 if (left_unwrap != null) {
743 left_unwrap.EmitCheck (ec);
744 ig.Emit (OpCodes.Brfalse, is_null_label);
747 if (right_unwrap != null) {
748 right_unwrap.EmitCheck (ec);
749 ig.Emit (OpCodes.Brfalse, is_null_label);
752 base.EmitOperator (ec);
754 ig.Emit (OpCodes.Br_S, end_label);
755 ig.MarkLabel (is_null_label);
756 LiftedNull.Create (type, loc).Emit (ec);
758 ig.MarkLabel (end_label);
761 bool IsBitwiseBoolean {
763 return (Oper & Operator.BitwiseMask) != 0 && left_unwrap != null && right_unwrap != null &&
764 left_unwrap.Type == TypeManager.bool_type && right_unwrap.Type == TypeManager.bool_type;
768 Expression LiftResult (EmitContext ec, Expression res_expr)
770 TypeExpr lifted_type;
773 // Avoid double conversion
775 if (left_unwrap == null || left_null_lifted || !TypeManager.IsEqual (left_unwrap.Type, left.Type)) {
776 lifted_type = new NullableType (left.Type, loc);
777 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
778 if (lifted_type == null)
781 left = EmptyCast.Create (left, lifted_type.Type);
784 if (right_unwrap == null || right_null_lifted || !TypeManager.IsEqual (right_unwrap.Type, right.Type)) {
785 lifted_type = new NullableType (right.Type, loc);
786 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
787 if (lifted_type == null)
790 right = EmptyCast.Create (right, lifted_type.Type);
793 if (left_null_lifted) {
794 left = LiftedNull.Create (right.Type, left.Location);
797 // Value types and null comparison
799 if (right_unwrap == null || (Oper & Operator.RelationalMask) != 0)
800 return CreateNullConstant (right_orig).Resolve (ec);
803 if (right_null_lifted) {
804 right = LiftedNull.Create (left.Type, right.Location);
807 // Value types and null comparison
809 if (left_unwrap == null || (Oper & Operator.RelationalMask) != 0)
810 return CreateNullConstant (left_orig).Resolve (ec);
813 // TODO: Handle bitwise bool
814 if ((Oper & Operator.ComparisonMask) != 0 || IsBitwiseBoolean)
817 lifted_type = new NullableType (res_expr.Type, loc);
818 lifted_type = lifted_type.ResolveAsTypeTerminal (ec, false);
819 if (lifted_type == null)
822 return new LiftedWrap (res_expr, lifted_type.Type).Resolve (ec);
825 protected override Expression ResolveOperatorPredefined (EmitContext ec, Binary.PredefinedOperator [] operators, bool primitives_only)
827 Expression e = base.ResolveOperatorPredefined (ec, operators, primitives_only);
830 return LiftResult (ec, e);
833 // 7.9.9 Equality operators and null
835 // The == and != operators permit one operand to be a value of a nullable type and
836 // the other to be the null literal, even if no predefined or user-defined operator
837 // (in unlifted or lifted form) exists for the operation.
839 if (e == null && (Oper & Operator.EqualityMask) != 0) {
840 if ((left_null_lifted && right_unwrap != null) || (right_null_lifted && left_unwrap != null))
841 return LiftResult (ec, this);
847 protected override Expression ResolveUserOperator (EmitContext ec, Type l, Type r)
849 Expression expr = base.ResolveUserOperator (ec, l, r);
853 return LiftResult (ec, expr);
857 public class NullCoalescingOperator : Expression
859 Expression left, right;
862 public NullCoalescingOperator (Expression left, Expression right, Location loc)
869 public override Expression CreateExpressionTree (EmitContext ec)
871 UserCast uc = left as UserCast;
872 Expression conversion = null;
876 ArrayList c_args = new ArrayList (2);
877 c_args.Add (new Argument (uc.CreateExpressionTree (ec)));
878 c_args.Add (new Argument (left.CreateExpressionTree (ec)));
879 conversion = CreateExpressionFactoryCall ("Lambda", c_args);
882 ArrayList args = new ArrayList (3);
883 args.Add (new Argument (left.CreateExpressionTree (ec)));
884 args.Add (new Argument (right.CreateExpressionTree (ec)));
885 if (conversion != null)
886 args.Add (new Argument (conversion));
888 return CreateExpressionFactoryCall ("Coalesce", args);
891 public override Expression DoResolve (EmitContext ec)
896 left = left.Resolve (ec);
897 right = right.Resolve (ec);
899 if (left == null || right == null)
902 eclass = ExprClass.Value;
903 Type ltype = left.Type, rtype = right.Type;
906 if (TypeManager.IsNullableType (ltype)) {
907 NullableInfo info = new NullableInfo (ltype);
909 unwrap = Unwrap.Create (left, ec);
913 expr = Convert.ImplicitConversion (ec, right, info.UnderlyingType, loc);
920 } else if (!TypeManager.IsReferenceType (ltype)) {
921 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
925 expr = Convert.ImplicitConversion (ec, right, ltype, loc);
932 Expression left_null = unwrap != null ? unwrap : left;
933 expr = Convert.ImplicitConversion (ec, left_null, rtype, loc);
940 Binary.Error_OperatorCannotBeApplied (loc, "??", ltype, rtype);
944 public override void Emit (EmitContext ec)
946 ILGenerator ig = ec.ig;
948 Label is_null_label = ig.DefineLabel ();
949 Label end_label = ig.DefineLabel ();
951 if (unwrap != null) {
952 unwrap.EmitCheck (ec);
953 ig.Emit (OpCodes.Brfalse, is_null_label);
956 ig.Emit (OpCodes.Br, end_label);
958 ig.MarkLabel (is_null_label);
961 ig.MarkLabel (end_label);
964 ig.Emit (OpCodes.Dup);
965 ig.Emit (OpCodes.Brtrue, end_label);
967 ig.MarkLabel (is_null_label);
969 ig.Emit (OpCodes.Pop);
972 ig.MarkLabel (end_label);
975 protected override void CloneTo (CloneContext clonectx, Expression t)
977 NullCoalescingOperator target = (NullCoalescingOperator) t;
979 target.left = left.Clone (clonectx);
980 target.right = right.Clone (clonectx);
984 public class LiftedUnaryMutator : ExpressionStatement
986 public readonly UnaryMutator.Mode Mode;
987 Expression expr, null_value;
988 UnaryMutator underlying;
991 public LiftedUnaryMutator (UnaryMutator.Mode mode, Expression expr, Location loc)
997 eclass = ExprClass.Value;
1000 public override Expression DoResolve (EmitContext ec)
1002 expr = expr.Resolve (ec);
1006 unwrap = Unwrap.Create (expr, ec);
1010 underlying = (UnaryMutator) new UnaryMutator (Mode, unwrap, loc).Resolve (ec);
1011 if (underlying == null)
1015 null_value = LiftedNull.Create (type, loc);
1019 void DoEmit (EmitContext ec, bool is_expr)
1021 ILGenerator ig = ec.ig;
1022 Label is_null_label = ig.DefineLabel ();
1023 Label end_label = ig.DefineLabel ();
1025 unwrap.EmitCheck (ec);
1026 ig.Emit (OpCodes.Brfalse, is_null_label);
1029 underlying.Emit (ec);
1031 underlying.EmitStatement (ec);
1032 ig.Emit (OpCodes.Br, end_label);
1034 ig.MarkLabel (is_null_label);
1036 null_value.Emit (ec);
1038 ig.MarkLabel (end_label);
1041 public override void Emit (EmitContext ec)
1046 public override void EmitStatement (EmitContext ec)