context: //
  // parameter.cs: Parameter definition.
  //
  // Author: Miguel de Icaza (miguel@gnu.org)
  //         Marek Safar (marek.safar@seznam.cz)
  //
  // Dual licensed under the terms of the MIT X11 or GNU GPL
  //
  // Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
  // Copyright 2003-2008 Novell, Inc. 
  // Copyright 2011 Xamarin Inc
  //
  //
  using System;
  using System.Text;
  
  #if STATIC
  using MetaType = IKVM.Reflection.Type;
  using IKVM.Reflection;
  using IKVM.Reflection.Emit;
  #else
  using MetaType = System.Type;
  using System.Reflection;
  using System.Reflection.Emit;
  #endif
  
  namespace Mono.CSharp {
  
  	/// <summary>
  	///   Abstract Base class for parameters of a method.
  	/// </summary>
  	public abstract class ParameterBase : Attributable
  	{
  		protected ParameterBuilder builder;
  
  		public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
  		{
  #if false
  			if (a.Type == pa.MarshalAs) {
  				UnmanagedMarshal marshal = a.GetMarshal (this);
  				if (marshal != null) {
  					builder.SetMarshal (marshal);
  				}
  				return;
  			}
  #endif
  			if (a.HasSecurityAttribute) {
  				a.Error_InvalidSecurityParent ();
  				return;
  			}
  
  			if (a.Type == pa.Dynamic) {
  				a.Error_MisusedDynamicAttribute ();
  				return;
  			}
  
  			builder.SetCustomAttribute ((ConstructorInfo) ctor.GetMetaInfo (), cdata);
  		}
  
  		public ParameterBuilder Builder {
  			get {
  				return builder;
  			}
  		}
  
  		public override bool IsClsComplianceRequired()
  		{
  			return false;
  		}
  	}
  
  	/// <summary>
  	/// Class for applying custom attributes on the return type
  	/// </summary>
  	public class ReturnParameter : ParameterBase
  	{
  		MemberCore method;
  
  		// TODO: merge method and mb
  		public ReturnParameter (MemberCore method, MethodBuilder mb, Location location)
  		{
  			this.method = method;
  			try {
  				builder = mb.DefineParameter (0, ParameterAttributes.None, "");			
  			}
  			catch (ArgumentOutOfRangeException) {
  				method.Compiler.Report.RuntimeMissingSupport (location, "custom attributes on the return type");
  			}
  		}
  
  		public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
  		{
  			if (a.Type == pa.CLSCompliant) {
  				method.Compiler.Report.Warning (3023, 1, a.Location,
  					"CLSCompliant attribute has no meaning when applied to return types. Try putting it on the method instead");
  			}
  
  			// This occurs after Warning -28
  			if (builder == null)
  				return;
  
  			base.ApplyAttributeBuilder (a, ctor, cdata, pa);
  		}
  
  		public override AttributeTargets AttributeTargets {
  			get {
  				return AttributeTargets.ReturnValue;
  			}
  		}
  
  		/// <summary>
  		/// Is never called
  		/// </summary>
  		public override string[] ValidAttributeTargets {
  			get {
  				return null;
  			}
  		}
  	}
  
  	public class ImplicitLambdaParameter : Parameter
  	{
  		public ImplicitLambdaParameter (string name, Location loc)
  			: base (null, name, Modifier.NONE, null, loc)
  		{
  		}
  
  		public override TypeSpec Resolve (IMemberContext ec, int index)
  		{
  			if (parameter_type == null)
  				throw new InternalErrorException ("A type of implicit lambda parameter `{0}' is not set",
  					Name);
  
  			base.idx = index;
  			return parameter_type;
  		}
  
  		public void SetParameterType (TypeSpec type)
  		{
  			parameter_type = type;
  		}
  	}
  
  	public class ParamsParameter : Parameter {
  		public ParamsParameter (FullNamedExpression type, string name, Attributes attrs, Location loc):
  			base (type, name, Parameter.Modifier.PARAMS, attrs, loc)
  		{
  		}
  
  		public override TypeSpec Resolve (IMemberContext ec, int index)
  		{
  			if (base.Resolve (ec, index) == null)
  				return null;
  
  			var ac = parameter_type as ArrayContainer;
  			if (ac == null || ac.Rank != 1) {
  				ec.Module.Compiler.Report.Error (225, Location, "The params parameter must be a single dimensional array");
  				return null;
  			}
  
  			return parameter_type;
  		}
  
  		public override void ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa)
  		{
  			base.ApplyAttributes (mb, cb, index, pa);
  			pa.ParamArray.EmitAttribute (builder);
  		}
  	}
  
  	public class ArglistParameter : Parameter {
  		// Doesn't have proper type because it's never chosen for better conversion
  		public ArglistParameter (Location loc) :
  			base (null, String.Empty, Parameter.Modifier.NONE, null, loc)
  		{
  			parameter_type = InternalType.Arglist;
  		}
  
  		public override void  ApplyAttributes (MethodBuilder mb, ConstructorBuilder cb, int index, PredefinedAttributes pa)
  		{
  			// Nothing to do
  		}
  
  		public override bool CheckAccessibility (InterfaceMemberBase member)
  		{
  			return true;
  		}
  
  		public override TypeSpec Resolve (IMemberContext ec, int index)
  		{
  			return parameter_type;
  		}
  	}
  
  	public interface IParameterData
  	{
  		Expression DefaultValue { get; }
  		bool HasExtensionMethodModifier { get; }
  		bool HasDefaultValue { get; }
  		Parameter.Modifier ModFlags { get; }
  		string Name { get; }
  	}
  
  	//
  	// Parameter information created by parser
  	//
  	public class Parameter : ParameterBase, IParameterData, ILocalVariable // TODO: INamedBlockVariable
  	{
  		[Flags]
  		public enum Modifier : byte {
  			NONE    = 0,
  			PARAMS  = 1 << 0,
  			REF = 1 << 1,
  			OUT = 1 << 2,
  			This = 1 << 3,
  			CallerMemberName = 1 << 4,
  			CallerLineNumber = 1 << 5,
  			CallerFilePath = 1 << 6,
  
  			RefOutMask = REF | OUT,
  			ModifierMask = PARAMS | REF | OUT | This,
  			CallerMask = CallerMemberName | CallerLineNumber | CallerFilePath
  		}
  
  		static readonly string[] attribute_targets = new string[] { "param" };
  		static readonly string[] attribute_targets_primary = new string[] { "param", "field" };
  
  		FullNamedExpression texpr;
  		Modifier modFlags;
  		string name;
  		Expression default_expr;
  		protected TypeSpec parameter_type;
  		readonly Location loc;
  		protected int idx;
  		public bool HasAddressTaken;
  
  		Constructor primary_constructor;
  		TemporaryVariableReference expr_tree_variable;
  
  		HoistedParameter hoisted_variant;
  
  		public Parameter (FullNamedExpression type, string name, Modifier mod, Attributes attrs, Location loc)
  		{
  			this.name = name;
  			modFlags = mod;
  			this.loc = loc;
  			texpr = type;
  
  			// Only assign, attributes will be attached during resolve
  			base.attributes = attrs;
  		}
  
  		#region Properties
  
  		public Expression DefaultExpression {
  			get {
  				return default_expr;
  			}
  		}
  
  		public DefaultParameterValueExpression DefaultValue {
  			get {
  				return default_expr as DefaultParameterValueExpression;
  			}
  			set {
  				default_expr = value;
  			}
  		}
  
  		Expression IParameterData.DefaultValue {
  			get {
  				var expr = default_expr as DefaultParameterValueExpression;
  				return expr == null ? default_expr : expr.Child;
  			}
  		}
  
  		bool HasOptionalExpression {
  			get {
  				return default_expr is DefaultParameterValueExpression;
  			}
  		}
  
  		public Location Location {
  			get {
  				return loc;
  			}
  		}
  
  		public Modifier ParameterModifier {
  			get {
  				return modFlags;
  			}
  		}
  
  		public TypeSpec Type {
  			get {
  				return parameter_type;
  			}
  			set {
  				parameter_type = value;
  			}
  		}
  
  		public FullNamedExpression TypeExpression  {
  			get {
  				return texpr;
  			}
  		}
  
  		public override string[] ValidAttributeTargets {
  			get {
  				return primary_constructor != null ? attribute_targets_primary : attribute_targets;
  			}
  		}
  
  		#endregion
  
  		public override void ApplyAttributeBuilder (Attribute a, MethodSpec ctor, byte[] cdata, PredefinedAttributes pa)
  		{
  			if (a.Target == AttributeTargets.Field) {
  				var field = MemberCache.FindMember (primary_constructor.Spec.DeclaringType, MemberFilter.Field (name, parameter_type), BindingRestriction.DeclaredOnly);
  				((Field)field.MemberDefinition).ApplyAttributeBuilder (a, ctor, cdata, pa);
  				return;
  			}
  
  			if (a.Type == pa.In && ModFlags == Modifier.OUT) {
  				a.Report.Error (36, a.Location, "An out parameter cannot have the `In' attribute");
  				return;
  			}
  
  			if (a.Type == pa.ParamArray) {
  				a.Report.Error (674, a.Location, "Do not use `System.ParamArrayAttribute'. Use the `params' keyword instead");
  				return;
  			}
  
  			if (a.Type == pa.Out && (ModFlags & Modifier.REF) != 0 &&
  			    !OptAttributes.Contains (pa.In)) {
  				a.Report.Error (662, a.Location,
  					"Cannot specify only `Out' attribute on a ref parameter. Use both `In' and `Out' attributes or neither");
  				return;
  			}
  
  			if (a.Type == pa.CLSCompliant) {
  				a.Report.Warning (3022, 1, a.Location, "CLSCompliant attribute has no meaning when applied to parameters. Try putting it on the method instead");
  			} else if (a.Type == pa.DefaultParameterValue || a.Type == pa.OptionalParameter) {
  				if (HasOptionalExpression) {
  					a.Report.Error (1745, a.Location,
  						"Cannot specify `{0}' attribute on optional parameter `{1}'",
  						a.Type.GetSignatureForError ().Replace ("Attribute", ""), Name);
  				}
  
  				if (a.Type == pa.DefaultParameterValue)
  					return;
  			} else if (a.Type == pa.CallerMemberNameAttribute) {
  				if ((modFlags & Modifier.CallerMemberName) == 0) {
  					a.Report.Error (4022, a.Location,
  						"The CallerMemberName attribute can only be applied to parameters with default value");
  				}
  			} else if (a.Type == pa.CallerLineNumberAttribute) {
  				if ((modFlags & Modifier.CallerLineNumber) == 0) {
  					a.Report.Error (4020, a.Location,
  						"The CallerLineNumber attribute can only be applied to parameters with default value");
  				}
  			} else if (a.Type == pa.CallerFilePathAttribute) {
  				if ((modFlags & Modifier.CallerFilePath) == 0) {
  					a.Report.Error (4021, a.Location,
  						"The CallerFilePath attribute can only be applied to parameters with default value");
  				}
  			}
  
  			base.ApplyAttributeBuilder (a, ctor, cdata, pa);
  		}
  		
  		public virtual bool CheckAccessibility (InterfaceMemberBase member)
  		{
  			if (parameter_type == null)
  				return true;
  
  			return member.IsAccessibleAs (parameter_type);
  		}
  
  		bool IsValidCallerContext (MemberCore memberContext)
  		{
  			var m = memberContext as Method;
  			if (m != null)
  				return !m.IsPartialImplementation;
  
  			return true;
  		}
  
  		// <summary>
  		//   Resolve is used in method definitions
  		// </summary>
  		public virtual TypeSpec Resolve (IMemberContext rc, int index)
  		{
  			if (parameter_type != null)
  				return parameter_type;
  
  			if (attributes != null)
  				attributes.AttachTo (this, rc);
  
  			var ctor = rc.CurrentMemberDefinition as Constructor;
  			if (ctor != null && ctor.IsPrimaryConstructor)
  				primary_constructor = ctor;
  
  			parameter_type = texpr.ResolveAsType (rc);
  			if (parameter_type == null)
  				return null;
  
  			this.idx = index;
  
  			if ((modFlags & Parameter.Modifier.RefOutMask) != 0 && parameter_type.IsSpecialRuntimeType) {
  				rc.Module.Compiler.Report.Error (1601, Location, "Method or delegate parameter cannot be of type `{0}'",
  					GetSignatureForError ());
  				return null;
  			}
  
  			VarianceDecl.CheckTypeVariance (parameter_type,
  				(modFlags & Parameter.Modifier.RefOutMask) != 0 ? Variance.None : Variance.Contravariant,
  				rc);
  
  			if (parameter_type.IsStatic) {
  				rc.Module.Compiler.Report.Error (721, Location, "`{0}': static types cannot be used as parameters",
  					texpr.GetSignatureForError ());
  				return parameter_type;
  			}
  
  			if ((modFlags & Modifier.This) != 0 && (parameter_type.IsPointer || parameter_type.BuiltinType == BuiltinTypeSpec.Type.Dynamic)) {
  				rc.Module.Compiler.Report.Error (1103, Location, "The extension method cannot be of type `{0}'",
  					parameter_type.GetSignatureForError ());
  			}
  
  			return parameter_type;
  		}
  
  		void ResolveCallerAttributes (ResolveContext rc)
  		{
  			var pa = rc.Module.PredefinedAttributes;
  			TypeSpec caller_type;
  			Attribute callerMemberName = null, callerFilePath = null;
  
  			foreach (var attr in attributes.Attrs) {
  				var atype = attr.ResolveTypeForComparison ();
  				if (atype == null)
  					continue;
  
  				if (atype == pa.CallerMemberNameAttribute) {
  					caller_type = rc.BuiltinTypes.String;
  					if (caller_type != parameter_type && !Convert.ImplicitReferenceConversionExists (caller_type, parameter_type)) {
  						rc.Report.Error (4019, attr.Location,
  							"The CallerMemberName attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'",
  							caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ());
  					}
  
  					if (!IsValidCallerContext (rc.CurrentMemberDefinition)) {
  						rc.Report.Warning (4026, 1, attr.Location,
  							"The CallerMemberName applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments",
  							name);
  					}
  
  					modFlags |= Modifier.CallerMemberName;
  					callerMemberName = attr;
  					continue;
  				}
  
  				if (atype == pa.CallerLineNumberAttribute) {
  					caller_type = rc.BuiltinTypes.Int;
  					if (caller_type != parameter_type && !Convert.ImplicitNumericConversionExists (caller_type, parameter_type)) {
  						rc.Report.Error (4017, attr.Location,
  							"The CallerLineNumberAttribute attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'",
  							caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ());
  					}
  
  					if (!IsValidCallerContext (rc.CurrentMemberDefinition)) {
  						rc.Report.Warning (4024, 1, attr.Location,
  							"The CallerLineNumberAttribute applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments",
  							name);
  					}
  
  					modFlags |= Modifier.CallerLineNumber;
  					continue;
  				}
  
  				if (atype == pa.CallerFilePathAttribute) {
  					caller_type = rc.BuiltinTypes.String;
  					if (caller_type != parameter_type && !Convert.ImplicitReferenceConversionExists (caller_type, parameter_type)) {
  						rc.Report.Error (4018, attr.Location,
  							"The CallerFilePath attribute cannot be applied because there is no standard conversion from `{0}' to `{1}'",
  							caller_type.GetSignatureForError (), parameter_type.GetSignatureForError ());
  					}
  
  					if (!IsValidCallerContext (rc.CurrentMemberDefinition)) {
  						rc.Report.Warning (4025, 1, attr.Location,
  							"The CallerFilePath applied to parameter `{0}' will have no effect because it applies to a member that is used in context that do not allow optional arguments",
  							name);
  					}
  
  					modFlags |= Modifier.CallerFilePath;
  					callerFilePath = attr;
  					continue;
  				}
  			}
  
  			if ((modFlags & Modifier.CallerLineNumber) != 0) {
  				if (callerMemberName != null) {
  					rc.Report.Warning (7081, 1, callerMemberName.Location,
  						"The CallerMemberNameAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerLineNumberAttribute",
  						Name);
  				}
  
  				if (callerFilePath != null) {
  					rc.Report.Warning (7082, 1, callerFilePath.Location,
  						"The CallerFilePathAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerLineNumberAttribute",
  						name);
  				}
  			}
  
  			if ((modFlags & Modifier.CallerMemberName) != 0) {
  				if (callerFilePath != null) {
  					rc.Report.Warning (7080, 1, callerFilePath.Location,
  						"The CallerMemberNameAttribute applied to parameter `{0}' will have no effect. It is overridden by the CallerFilePathAttribute",
  						name);
  				}
  
  			}
  		}
  
  		public void ResolveDefaultValue (ResolveContext rc)
  		{
  			//
  			// Default value was specified using an expression
  			//
  			if (default_expr != null) {
  				((DefaultParameterValueExpression)default_expr).Resolve (rc, this);
  				if (attributes != null)
  					ResolveCallerAttributes (rc);
  
  				return;
  			}
  
  			if (attributes == null)
  				return;
  
  			var pa = rc.Module.PredefinedAttributes;
  			var def_attr = attributes.Search (pa.DefaultParameterValue);
  			if (def_attr != null) {
  				if (def_attr.Resolve () == null)
  					return;
  
  				var default_expr_attr = def_attr.GetParameterDefaultValue ();
  				if (default_expr_attr == null)
  					return;
  
  				var dpa_rc = def_attr.CreateResolveContext ();
  				default_expr = default_expr_attr.Resolve (dpa_rc);
  
  				if (default_expr is BoxedCast)
  					default_expr = ((BoxedCast) default_expr).Child;
  
  				Constant c = default_expr as Constant;
  				if (c == null) {
  					if (parameter_type.BuiltinType == BuiltinTypeSpec.Type.Object) {
  						rc.Report.Error (1910, default_expr.Location,
  							"Argument of type `{0}' is not applicable for the DefaultParameterValue attribute",
  							default_expr.Type.GetSignatureForError ());
  					} else {
  						rc.Report.Error (1909, default_expr.Location,
  							"The DefaultParameterValue attribute is not applicable on parameters of type `{0}'",
  							default_expr.Type.GetSignatureForError ());
  					}
  
  					default_expr = null;
  					return;
  				}
  
  				if (TypeSpecComparer.IsEqual (default_expr.Type, parameter_type) ||
  					(default_expr is NullConstant && TypeSpec.IsReferenceType (parameter_type) && !parameter_type.IsGenericParameter) ||
  					parameter_type.BuiltinType == BuiltinTypeSpec.Type.Object) {
  					return;
  				}
  
  				//
  				// LAMESPEC: Some really weird csc behaviour which we have to mimic
  				// User operators returning same type as parameter type are considered
  				// valid for this attribute only
  				//
  				// struct S { public static implicit operator S (int i) {} }
  				//
  				// void M ([DefaultParameterValue (3)]S s)
  				//
  				var expr = Convert.ImplicitUserConversion (dpa_rc, default_expr, parameter_type, loc);
  				if (expr != null && TypeSpecComparer.IsEqual (expr.Type, parameter_type)) {
  					return;
  				}
  				
  				rc.Report.Error (1908, default_expr.Location, "The type of the default value should match the type of the parameter");
  				return;
  			}
  
  
gt: 			var opt_attr = attributes.Search (pa.OptionalParameter);