context: //
  // nullable.cs: Nullable types support
  //
  // Authors: Martin Baulig (martin@ximian.com)
  //          Miguel de Icaza (miguel@ximian.com)
  //          Marek Safar (marek.safar@gmail.com)
  //
  // Dual licensed under the terms of the MIT X11 or GNU GPL
  //
  // Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
  // Copyright 2004-2008 Novell, Inc
  //
  
  using System;
  
  #if STATIC
  using IKVM.Reflection.Emit;
  #else
  using System.Reflection.Emit;
  #endif
  	
  namespace Mono.CSharp.Nullable
  {
  	public class NullableType : TypeExpr
  	{
  		TypeExpr underlying;
  
  		public NullableType (TypeExpr underlying, Location l)
  		{
  			this.underlying = underlying;
  			loc = l;
  
  			eclass = ExprClass.Type;
  		}
  
  		public NullableType (TypeSpec type, Location loc)
  			: this (new TypeExpression (type, loc), loc)
  		{ }
  
  		public override TypeExpr ResolveAsType (IMemberContext ec)
  		{
  			var type = ec.Module.PredefinedTypes.Nullable.Resolve ();
  			if (type == null)
  				return null;
  
  			TypeArguments args = new TypeArguments (underlying);
  			GenericTypeExpr ctype = new GenericTypeExpr (type, args, loc);
  			return ctype.ResolveAsType (ec);
  		}
  	}
  
  	static class NullableInfo
  	{
  		public static MethodSpec GetConstructor (TypeSpec nullableType)
  		{
  			return (MethodSpec) MemberCache.FindMember (nullableType,
  				MemberFilter.Constructor (ParametersCompiled.CreateFullyResolved (GetUnderlyingType (nullableType))), BindingRestriction.DeclaredOnly);
  		}
  
  		public static MethodSpec GetHasValue (TypeSpec nullableType)
  		{
  			return (MethodSpec) MemberCache.FindMember (nullableType,
  				MemberFilter.Method ("get_HasValue", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
  		}
  
  		public static MethodSpec GetGetValueOrDefault (TypeSpec nullableType)
  		{
  			return (MethodSpec) MemberCache.FindMember (nullableType,
  				MemberFilter.Method ("GetValueOrDefault", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
  		}
  
  		public static MethodSpec GetValue (TypeSpec nullableType)
  		{
  			return (MethodSpec) MemberCache.FindMember (nullableType,
  				MemberFilter.Method ("get_Value", 0, ParametersCompiled.EmptyReadOnlyParameters, null), BindingRestriction.None);
  		}
  
  		public static TypeSpec GetUnderlyingType (TypeSpec nullableType)
  		{
  			return ((InflatedTypeSpec) nullableType).TypeArguments[0];
  		}
  	}
  
  	public class Unwrap : Expression, IMemoryLocation, IAssignMethod
  	{
  		Expression expr;
  
  		LocalTemporary temp;
  		readonly bool useDefaultValue;
  
  		Unwrap (Expression expr, bool useDefaultValue)
  		{
  			this.expr = expr;
  			this.loc = expr.Location;
  			this.useDefaultValue = useDefaultValue;
  
  			type = NullableInfo.GetUnderlyingType (expr.Type);
  			eclass = expr.eclass;
  		}
  
  		public static Expression Create (Expression expr)
  		{
  			//
  			// Avoid unwraping and wraping of same type
  			//
  			Wrap wrap = expr as Wrap;
  			if (wrap != null)
  				return wrap.Child;
  
  			return Create (expr, false);
  		}
  
  		public static Unwrap Create (Expression expr, bool useDefaultValue)
  		{
  			return new Unwrap (expr, useDefaultValue);
  		}
  		
  		public override Expression CreateExpressionTree (ResolveContext ec)
  		{
  			return expr.CreateExpressionTree (ec);
  		}
  
  		protected override Expression DoResolve (ResolveContext ec)
  		{
  			return this;
  		}
  
  		public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
  		{
  			expr = expr.DoResolveLValue (ec, right_side);
  			return this;
  		}
  
  		public override void Emit (EmitContext ec)
  		{
  			Store (ec);
  			if (useDefaultValue)
  				Invocation.EmitCall (ec, this, NullableInfo.GetGetValueOrDefault (expr.Type), null, loc);
  			else
  				Invocation.EmitCall (ec, this, NullableInfo.GetValue (expr.Type), null, loc);
  		}
  
  		public void EmitCheck (EmitContext ec)
  		{
  			Store (ec);
  			Invocation.EmitCall (ec, this, NullableInfo.GetHasValue (expr.Type), null, loc);
  		}
  
  		public override bool Equals (object obj)
  		{
  			Unwrap uw = obj as Unwrap;
  			return uw != null && expr.Equals (uw.expr);
  		}
  
  		public Expression Original {
  			get {
  				return expr;
  			}
  		}
  		
  		public override int GetHashCode ()
  		{
  			return expr.GetHashCode ();
  		}
  
  		public override bool IsNull {
  			get {
  				return expr.IsNull;
  			}
  		}
  
  		void Store (EmitContext ec)
  		{
  			if (expr is VariableReference)
  				return;
  
  			if (temp != null)
  				return;
  
  			expr.Emit (ec);
  			LocalVariable.Store (ec);
  		}
  
  		public void Load (EmitContext ec)
  		{
  			if (expr is VariableReference)
  				expr.Emit (ec);
  			else
  				LocalVariable.Emit (ec);
  		}
  
  		public override System.Linq.Expressions.Expression MakeExpression (BuilderContext ctx)
  		{
  			return expr.MakeExpression (ctx);
  		}
  
  		public void AddressOf (EmitContext ec, AddressOp mode)
  		{
  			IMemoryLocation ml = expr as VariableReference;
  			if (ml != null)
  				ml.AddressOf (ec, mode);
  			else
  				LocalVariable.AddressOf (ec, mode);
  		}
  
  		//
  		// Keeps result of non-variable expression
  		//
  		LocalTemporary LocalVariable {
  			get {
  				if (temp == null)
  					temp = new LocalTemporary (expr.Type);
  				return temp;
  			}
  		}
  
  		public void Emit (EmitContext ec, bool leave_copy)
  		{
  			if (leave_copy)
  				Load (ec);
  
  			Emit (ec);
  		}
  
  		public void EmitAssign (EmitContext ec, Expression source,
  					bool leave_copy, bool prepare_for_load)
  		{
  			InternalWrap wrap = new InternalWrap (source, expr.Type, loc);
  			((IAssignMethod) expr).EmitAssign (ec, wrap, leave_copy, false);
  		}
  
  		class InternalWrap : Expression
  		{
  			public Expression expr;
  
  			public InternalWrap (Expression expr, TypeSpec type, Location loc)
  			{
  				this.expr = expr;
  				this.loc = loc;
  				this.type = type;
  
  				eclass = ExprClass.Value;
  			}
  
  			public override Expression CreateExpressionTree (ResolveContext ec)
  			{
  				throw new NotSupportedException ("ET");
  			}
  
  			protected override Expression DoResolve (ResolveContext ec)
  			{
  				return this;
  			}
  
  			public override void Emit (EmitContext ec)
  			{
  				expr.Emit (ec);
  				ec.Emit (OpCodes.Newobj, NullableInfo.GetConstructor (type));
  			}
  		}
  	}
  
  	//
  	// Calls get_Value method on nullable expression
  	//
  	public class UnwrapCall : CompositeExpression
  	{
  		public UnwrapCall (Expression expr)
  			: base (expr)
  		{
  		}
  
  		protected override Expression DoResolve (ResolveContext rc)
  		{
  			base.DoResolve (rc);
  
  			if (type != null)
  
gt: 				type = NullableInfo.GetUnderlyingType (type);