2 // anonymous.cs: Support for anonymous methods and types
5 // Miguel de Icaza (miguel@ximain.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003-2008 Novell, Inc.
13 using System.Collections.Generic;
16 using IKVM.Reflection;
17 using IKVM.Reflection.Emit;
19 using System.Reflection;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class CompilerGeneratedClass : Class
27 protected CompilerGeneratedClass (DeclSpace parent, MemberName name, Modifiers mod)
28 : base (parent.NamespaceEntry, parent, name, mod | Modifiers.COMPILER_GENERATED, null)
32 protected void CheckMembersDefined ()
34 if (HasMembersDefined)
35 throw new InternalErrorException ("Helper class already defined!");
38 protected static MemberName MakeMemberName (MemberBase host, string name, int unique_id, TypeParameter[] tparams, Location loc)
40 string host_name = host == null ? null : host.Name;
41 string tname = MakeName (host_name, "c", name, unique_id);
42 TypeArguments args = null;
43 if (tparams != null) {
44 args = new TypeArguments ();
45 foreach (TypeParameter tparam in tparams)
46 args.Add (new TypeParameterName (tparam.Name, null, loc));
49 return new MemberName (tname, args, loc);
52 public static string MakeName (string host, string typePrefix, string name, int id)
54 return "<" + host + ">" + typePrefix + "__" + name + id.ToString ("X");
58 public class HoistedStoreyClass : CompilerGeneratedClass
60 public sealed class HoistedField : Field
62 public HoistedField (HoistedStoreyClass parent, FullNamedExpression type, Modifiers mod, string name,
63 Attributes attrs, Location loc)
64 : base (parent, type, mod, new MemberName (name, loc), attrs)
68 protected override bool ResolveMemberType ()
70 if (!base.ResolveMemberType ())
73 HoistedStoreyClass parent = ((HoistedStoreyClass) Parent).GetGenericStorey ();
74 if (parent != null && parent.Mutator != null)
75 member_type = parent.Mutator.Mutate (MemberType);
81 protected TypeParameterMutator mutator;
83 public HoistedStoreyClass (DeclSpace parent, MemberName name, TypeParameter[] tparams, Modifiers mod)
84 : base (parent, name, mod | Modifiers.PRIVATE)
86 if (tparams != null) {
87 type_params = new TypeParameter[tparams.Length];
88 var src = new TypeParameterSpec[tparams.Length];
89 var dst = new TypeParameterSpec[tparams.Length];
91 for (int i = 0; i < type_params.Length; ++i) {
92 type_params[i] = tparams[i].CreateHoistedCopy (this, spec);
94 src[i] = tparams[i].Type;
95 dst[i] = type_params[i].Type;
98 // A copy is not enough, inflate any type parameter constraints
99 // using a new type parameters
100 var inflator = new TypeParameterInflator (this, null, src, dst);
101 for (int i = 0; i < type_params.Length; ++i) {
102 src[i].InflateConstraints (inflator, dst[i]);
109 public TypeParameterMutator Mutator {
120 public HoistedStoreyClass GetGenericStorey ()
122 DeclSpace storey = this;
123 while (storey != null && storey.CurrentTypeParameters == null)
124 storey = storey.Parent;
126 return storey as HoistedStoreyClass;
132 // Anonymous method storey is created when an anonymous method uses
133 // variable or parameter from outer scope. They are then hoisted to
134 // anonymous method storey (captured)
136 public class AnonymousMethodStorey : HoistedStoreyClass
138 struct StoreyFieldPair
140 public readonly AnonymousMethodStorey Storey;
141 public readonly Field Field;
143 public StoreyFieldPair (AnonymousMethodStorey storey, Field field)
145 this.Storey = storey;
151 // Needed to delay hoisted _this_ initialization. When an anonymous
152 // method is used inside ctor and _this_ is hoisted, base ctor has to
153 // be called first, otherwise _this_ will be initialized with
154 // uninitialized value.
156 sealed class ThisInitializer : Statement
158 readonly HoistedThis hoisted_this;
160 public ThisInitializer (HoistedThis hoisted_this)
162 this.hoisted_this = hoisted_this;
165 protected override void DoEmit (EmitContext ec)
167 hoisted_this.EmitHoistingAssignment (ec);
170 protected override void CloneTo (CloneContext clonectx, Statement target)
177 public readonly int ID;
178 static int unique_id;
180 public readonly Block OriginalSourceBlock;
182 // A list of StoreyFieldPair with local field keeping parent storey instance
183 List<StoreyFieldPair> used_parent_storeys;
184 List<ExplicitBlock> children_references;
186 // A list of hoisted parameters
187 protected List<HoistedParameter> hoisted_params;
188 protected List<HoistedVariable> hoisted_locals;
191 protected HoistedThis hoisted_this;
193 // Local variable which holds this storey instance
194 public LocalTemporary Instance;
196 public AnonymousMethodStorey (Block block, TypeContainer parent, MemberBase host, TypeParameter[] tparams, string name)
197 : base (parent, MakeMemberName (host, name, unique_id, tparams, block.StartLocation),
198 tparams, Modifiers.SEALED)
201 OriginalSourceBlock = block;
205 public void AddCapturedThisField (EmitContext ec)
207 TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location);
208 Field f = AddCompilerGeneratedField ("<>f__this", type_expr);
210 hoisted_this = new HoistedThis (this, f);
212 // Inflated type instance has to be updated manually
213 if (Instance.Type is InflatedTypeSpec) {
214 var inflator = new TypeParameterInflator (this, Instance.Type, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
215 Instance.Type.MemberCache.AddMember (f.Spec.InflateMember (inflator));
217 inflator = new TypeParameterInflator (this, f.Parent.CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
218 f.Parent.CurrentType.MemberCache.AddMember (f.Spec.InflateMember (inflator));
222 public Field AddCapturedVariable (string name, TypeSpec type)
224 CheckMembersDefined ();
226 FullNamedExpression field_type = new TypeExpression (type, Location);
228 return AddCompilerGeneratedField (name, field_type);
230 const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
231 Field f = new HoistedField (this, field_type, mod, name, null, Location);
236 protected Field AddCompilerGeneratedField (string name, FullNamedExpression type)
238 const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
239 Field f = new Field (this, type, mod, new MemberName (name, Location), null);
245 // Creates a link between hoisted variable block and the anonymous method storey
247 // An anonymous method can reference variables from any outer block, but they are
248 // hoisted in their own ExplicitBlock. When more than one block is referenced we
249 // need to create another link between those variable storeys
251 public void AddReferenceFromChildrenBlock (ExplicitBlock block)
253 if (children_references == null)
254 children_references = new List<ExplicitBlock> ();
256 if (!children_references.Contains (block))
257 children_references.Add (block);
260 public void AddParentStoreyReference (EmitContext ec, AnonymousMethodStorey storey)
262 CheckMembersDefined ();
264 if (used_parent_storeys == null)
265 used_parent_storeys = new List<StoreyFieldPair> ();
266 else if (used_parent_storeys.Exists (i => i.Storey == storey))
269 TypeExpr type_expr = storey.CreateStoreyTypeExpression (ec);
270 Field f = AddCompilerGeneratedField ("<>f__ref$" + storey.ID, type_expr);
271 used_parent_storeys.Add (new StoreyFieldPair (storey, f));
274 public void CaptureLocalVariable (ResolveContext ec, LocalVariable local_info)
276 ec.CurrentBlock.Explicit.HasCapturedVariable = true;
277 if (ec.CurrentBlock.Explicit != local_info.Block.Explicit)
278 AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit);
280 if (local_info.HoistedVariant != null)
283 HoistedVariable var = new HoistedLocalVariable (this, local_info, GetVariableMangledName (local_info));
284 local_info.HoistedVariant = var;
286 if (hoisted_locals == null)
287 hoisted_locals = new List<HoistedVariable> ();
289 hoisted_locals.Add (var);
292 public void CaptureParameter (ResolveContext ec, ParameterReference param_ref)
294 ec.CurrentBlock.Explicit.HasCapturedVariable = true;
295 AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit);
297 if (param_ref.GetHoistedVariable (ec) != null)
300 if (hoisted_params == null)
301 hoisted_params = new List<HoistedParameter> (2);
303 var expr = new HoistedParameter (this, param_ref);
304 param_ref.Parameter.HoistedVariant = expr;
305 hoisted_params.Add (expr);
308 TypeExpr CreateStoreyTypeExpression (EmitContext ec)
311 // Create an instance of storey type
313 TypeExpr storey_type_expr;
314 if (CurrentTypeParameters != null) {
316 // Use current method type parameter (MVAR) for top level storey only. All
317 // nested storeys use class type parameter (VAR)
319 TypeParameter[] tparams = ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null ?
320 ec.CurrentAnonymousMethod.Storey.TypeParameters :
321 ec.CurrentTypeParameters;
323 TypeArguments targs = new TypeArguments ();
326 // Use type parameter name instead of resolved type parameter
327 // specification to resolve to correctly nested type parameters
329 for (int i = 0; i < tparams.Length; ++i)
330 targs.Add (new SimpleName (tparams [i].Name, Location)); // new TypeParameterExpr (tparams[i], Location));
332 storey_type_expr = new GenericTypeExpr (Definition, targs, Location);
334 storey_type_expr = new TypeExpression (CurrentType, Location);
337 return storey_type_expr;
340 public void SetNestedStoryParent (AnonymousMethodStorey parentStorey)
342 Parent = parentStorey;
344 spec.IsGeneric = false;
345 spec.DeclaringType = parentStorey.CurrentType;
346 MemberName.TypeArguments = null;
349 protected override bool DoResolveTypeParameters ()
351 // Although any storey can have type parameters they are all clones of method type
352 // parameters therefore have to mutate MVAR references in any of cloned constraints
353 if (type_params != null) {
354 for (int i = 0; i < type_params.Length; ++i) {
355 var spec = type_params[i].Type;
356 spec.BaseType = mutator.Mutate (spec.BaseType);
357 if (spec.InterfacesDefined != null) {
358 var mutated = new TypeSpec[spec.InterfacesDefined.Length];
359 for (int ii = 0; ii < mutated.Length; ++ii) {
360 mutated[ii] = mutator.Mutate (spec.InterfacesDefined[ii]);
363 spec.InterfacesDefined = mutated;
366 if (spec.TypeArguments != null) {
367 spec.TypeArguments = mutator.Mutate (spec.TypeArguments);
373 // Update parent cache as we most likely passed the point
374 // where the cache was constructed
376 Parent.CurrentType.MemberCache.AddMember (this.spec);
382 // Initializes all hoisted variables
384 public void EmitStoreyInstantiation (EmitContext ec, ExplicitBlock block)
386 // There can be only one instance variable for each storey type
387 if (Instance != null)
388 throw new InternalErrorException ();
390 SymbolWriter.OpenCompilerGeneratedBlock (ec);
393 // Create an instance of a storey
395 var storey_type_expr = CreateStoreyTypeExpression (ec);
397 ResolveContext rc = new ResolveContext (ec.MemberContext);
398 rc.CurrentBlock = block;
399 Expression e = new New (storey_type_expr, null, Location).Resolve (rc);
402 Instance = new LocalTemporary (storey_type_expr.Type);
405 EmitHoistedFieldsInitialization (rc, ec);
407 SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
408 SymbolWriter.CloseCompilerGeneratedBlock (ec);
411 void EmitHoistedFieldsInitialization (ResolveContext rc, EmitContext ec)
414 // Initialize all storey reference fields by using local or hoisted variables
416 if (used_parent_storeys != null) {
417 foreach (StoreyFieldPair sf in used_parent_storeys) {
419 // Get instance expression of storey field
421 Expression instace_expr = GetStoreyInstanceExpression (ec);
422 var fs = sf.Field.Spec;
423 if (TypeManager.IsGenericType (instace_expr.Type))
424 fs = MemberCache.GetMember (instace_expr.Type, fs);
426 FieldExpr f_set_expr = new FieldExpr (fs, Location);
427 f_set_expr.InstanceExpression = instace_expr;
429 SimpleAssign a = new SimpleAssign (f_set_expr, sf.Storey.GetStoreyInstanceExpression (ec));
430 if (a.Resolve (rc) != null)
431 a.EmitStatement (ec);
436 // Define hoisted `this' in top-level storey only
438 if (OriginalSourceBlock.Explicit.HasCapturedThis && !(Parent is AnonymousMethodStorey)) {
439 AddCapturedThisField (ec);
440 rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this));
444 // Setting currect anonymous method to null blocks any further variable hoisting
446 AnonymousExpression ae = ec.CurrentAnonymousMethod;
447 ec.CurrentAnonymousMethod = null;
449 if (hoisted_params != null) {
450 EmitHoistedParameters (ec, hoisted_params);
453 ec.CurrentAnonymousMethod = ae;
456 protected virtual void EmitHoistedParameters (EmitContext ec, IList<HoistedParameter> hoisted)
458 foreach (HoistedParameter hp in hoisted) {
459 hp.EmitHoistingAssignment (ec);
463 public override void EmitType ()
465 SymbolWriter.DefineAnonymousScope (ID);
467 if (hoisted_this != null)
468 hoisted_this.EmitSymbolInfo ();
470 if (hoisted_locals != null) {
471 foreach (HoistedVariable local in hoisted_locals)
472 local.EmitSymbolInfo ();
475 if (hoisted_params != null) {
476 foreach (HoistedParameter param in hoisted_params)
477 param.EmitSymbolInfo ();
480 if (used_parent_storeys != null) {
481 foreach (StoreyFieldPair sf in used_parent_storeys) {
482 SymbolWriter.DefineCapturedScope (ID, sf.Storey.ID, sf.Field.Name);
490 // Returns a field which holds referenced storey instance
492 Field GetReferencedStoreyField (AnonymousMethodStorey storey)
494 if (used_parent_storeys == null)
497 foreach (StoreyFieldPair sf in used_parent_storeys) {
498 if (sf.Storey == storey)
506 // Creates storey instance expression regardless of currect IP
508 public Expression GetStoreyInstanceExpression (EmitContext ec)
510 AnonymousExpression am = ec.CurrentAnonymousMethod;
513 // Access from original block -> storey
519 // Access from anonymous method implemented as a static -> storey
521 if (am.Storey == null)
524 Field f = am.Storey.GetReferencedStoreyField (this);
526 if (am.Storey == this) {
528 // Access inside of same storey (S -> S)
530 return new CompilerGeneratedThis (CurrentType, Location);
533 // External field access
539 // Storey was cached to local field
541 FieldExpr f_ind = new FieldExpr (f, Location);
542 f_ind.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location);
546 protected virtual string GetVariableMangledName (LocalVariable local_info)
549 // No need to mangle anonymous method hoisted variables cause they
550 // are hoisted in their own scopes
552 return local_info.Name;
555 public HoistedThis HoistedThis {
556 get { return hoisted_this; }
559 public IList<ExplicitBlock> ReferencesFromChildrenBlock {
560 get { return children_references; }
563 public static void Reset ()
569 public abstract class HoistedVariable
572 // Hoisted version of variable references used in expression
573 // tree has to be delayed until we know its location. The variable
574 // doesn't know its location until all stories are calculated
576 class ExpressionTreeVariableReference : Expression
578 readonly HoistedVariable hv;
580 public ExpressionTreeVariableReference (HoistedVariable hv)
585 public override Expression CreateExpressionTree (ResolveContext ec)
587 return hv.CreateExpressionTree ();
590 protected override Expression DoResolve (ResolveContext ec)
592 eclass = ExprClass.Value;
593 type = ec.Module.PredefinedTypes.Expression.Resolve (Location);
597 public override void Emit (EmitContext ec)
599 ResolveContext rc = new ResolveContext (ec.MemberContext);
600 Expression e = hv.GetFieldExpression (ec).CreateExpressionTree (rc);
601 // This should never fail
608 protected readonly AnonymousMethodStorey storey;
609 protected Field field;
610 Dictionary<AnonymousExpression, FieldExpr> cached_inner_access; // TODO: Hashtable is too heavyweight
611 FieldExpr cached_outer_access;
613 protected HoistedVariable (AnonymousMethodStorey storey, string name, TypeSpec type)
614 : this (storey, storey.AddCapturedVariable (name, type))
618 protected HoistedVariable (AnonymousMethodStorey storey, Field field)
620 this.storey = storey;
624 public void AddressOf (EmitContext ec, AddressOp mode)
626 GetFieldExpression (ec).AddressOf (ec, mode);
629 public Expression CreateExpressionTree ()
631 return new ExpressionTreeVariableReference (this);
634 public void Emit (EmitContext ec)
636 GetFieldExpression (ec).Emit (ec);
640 // Creates field access expression for hoisted variable
642 protected virtual FieldExpr GetFieldExpression (EmitContext ec)
644 if (ec.CurrentAnonymousMethod == null || ec.CurrentAnonymousMethod.Storey == null) {
645 if (cached_outer_access != null)
646 return cached_outer_access;
649 // When setting top-level hoisted variable in generic storey
650 // change storey generic types to method generic types (VAR -> MVAR)
652 if (storey.Instance.Type.IsGenericOrParentIsGeneric) {
653 var fs = MemberCache.GetMember (storey.Instance.Type, field.Spec);
654 cached_outer_access = new FieldExpr (fs, field.Location);
656 cached_outer_access = new FieldExpr (field, field.Location);
659 cached_outer_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec);
660 return cached_outer_access;
663 FieldExpr inner_access;
664 if (cached_inner_access != null) {
665 if (!cached_inner_access.TryGetValue (ec.CurrentAnonymousMethod, out inner_access))
669 cached_inner_access = new Dictionary<AnonymousExpression, FieldExpr> (4);
672 if (inner_access == null) {
673 if (field.Parent.IsGeneric) {
674 var fs = MemberCache.GetMember (field.Parent.CurrentType, field.Spec);
675 inner_access = new FieldExpr (fs, field.Location);
677 inner_access = new FieldExpr (field, field.Location);
680 inner_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec);
681 cached_inner_access.Add (ec.CurrentAnonymousMethod, inner_access);
687 public abstract void EmitSymbolInfo ();
689 public void Emit (EmitContext ec, bool leave_copy)
691 GetFieldExpression (ec).Emit (ec, leave_copy);
694 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
696 GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false);
700 public class HoistedParameter : HoistedVariable
702 sealed class HoistedFieldAssign : Assign
704 public HoistedFieldAssign (Expression target, Expression source)
705 : base (target, source, source.Location)
709 protected override Expression ResolveConversions (ResolveContext ec)
712 // Implicit conversion check fails for hoisted type arguments
713 // as they are of different types (!!0 x !0)
719 readonly ParameterReference parameter;
721 public HoistedParameter (AnonymousMethodStorey scope, ParameterReference par)
722 : base (scope, par.Name, par.Type)
724 this.parameter = par;
727 public HoistedParameter (HoistedParameter hp, string name)
728 : base (hp.storey, name, hp.parameter.Type)
730 this.parameter = hp.parameter;
733 public void EmitHoistingAssignment (EmitContext ec)
736 // Remove hoisted redirection to emit assignment from original parameter
738 HoistedVariable temp = parameter.Parameter.HoistedVariant;
739 parameter.Parameter.HoistedVariant = null;
741 Assign a = new HoistedFieldAssign (GetFieldExpression (ec), parameter);
742 if (a.Resolve (new ResolveContext (ec.MemberContext)) != null)
743 a.EmitStatement (ec);
745 parameter.Parameter.HoistedVariant = temp;
748 public override void EmitSymbolInfo ()
750 SymbolWriter.DefineCapturedParameter (storey.ID, field.Name, field.Name);
754 get { return field; }
758 class HoistedLocalVariable : HoistedVariable
760 readonly string name;
762 public HoistedLocalVariable (AnonymousMethodStorey scope, LocalVariable local, string name)
763 : base (scope, name, local.Type)
765 this.name = local.Name;
768 public override void EmitSymbolInfo ()
770 SymbolWriter.DefineCapturedLocal (storey.ID, name, field.Name);
774 public class HoistedThis : HoistedVariable
776 public HoistedThis (AnonymousMethodStorey storey, Field field)
777 : base (storey, field)
781 public void EmitHoistingAssignment (EmitContext ec)
783 SimpleAssign a = new SimpleAssign (GetFieldExpression (ec), new CompilerGeneratedThis (ec.CurrentType, field.Location));
784 if (a.Resolve (new ResolveContext (ec.MemberContext)) != null)
785 a.EmitStatement (ec);
788 public override void EmitSymbolInfo ()
790 SymbolWriter.DefineCapturedThis (storey.ID, field.Name);
794 get { return field; }
799 // Anonymous method expression as created by parser
801 public class AnonymousMethodExpression : Expression
804 // Special conversion for nested expression tree lambdas
806 class Quote : ShimExpression
808 public Quote (Expression expr)
813 public override Expression CreateExpressionTree (ResolveContext ec)
815 var args = new Arguments (1);
816 args.Add (new Argument (expr.CreateExpressionTree (ec)));
817 return CreateExpressionFactoryCall (ec, "Quote", args);
820 protected override Expression DoResolve (ResolveContext rc)
822 expr = expr.Resolve (rc);
826 eclass = expr.eclass;
832 Dictionary<TypeSpec, Expression> compatibles;
833 public ParametersBlock Block;
835 public AnonymousMethodExpression (Location loc)
838 this.compatibles = new Dictionary<TypeSpec, Expression> ();
841 public override string ExprClassName {
843 return "anonymous method";
847 public virtual bool HasExplicitParameters {
849 return Parameters != ParametersCompiled.Undefined;
853 public ParametersCompiled Parameters {
854 get { return Block.Parameters; }
858 // Returns true if the body of lambda expression can be implicitly
859 // converted to the delegate of type `delegate_type'
861 public bool ImplicitStandardConversionExists (ResolveContext ec, TypeSpec delegate_type)
863 using (ec.With (ResolveContext.Options.InferReturnType, false)) {
864 using (ec.Set (ResolveContext.Options.ProbingMode)) {
865 return Compatible (ec, delegate_type) != null;
870 TypeSpec CompatibleChecks (ResolveContext ec, TypeSpec delegate_type)
872 if (delegate_type.IsDelegate)
873 return delegate_type;
875 if (delegate_type.IsGeneric && delegate_type.GetDefinition () == TypeManager.expression_type) {
876 delegate_type = delegate_type.TypeArguments [0];
877 if (delegate_type.IsDelegate)
878 return delegate_type;
880 ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'",
881 GetSignatureForError (), TypeManager.CSharpName (delegate_type));
885 ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'",
886 GetSignatureForError (), TypeManager.CSharpName (delegate_type));
890 protected bool VerifyExplicitParameters (ResolveContext ec, TypeSpec delegate_type, AParametersCollection parameters)
892 if (VerifyParameterCompatibility (ec, delegate_type, parameters, ec.IsInProbingMode))
895 if (!ec.IsInProbingMode)
896 ec.Report.Error (1661, loc,
897 "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch",
898 GetSignatureForError (), TypeManager.CSharpName (delegate_type));
903 protected bool VerifyParameterCompatibility (ResolveContext ec, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
905 if (Parameters.Count != invoke_pd.Count) {
909 ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
910 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
914 bool has_implicit_parameters = !HasExplicitParameters;
917 for (int i = 0; i < Parameters.Count; ++i) {
918 Parameter.Modifier p_mod = invoke_pd.FixedParameters [i].ModFlags;
919 if (Parameters.FixedParameters [i].ModFlags != p_mod && p_mod != Parameter.Modifier.PARAMS) {
923 if (p_mod == Parameter.Modifier.NONE)
924 ec.Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
925 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.FixedParameters [i].ModFlags));
927 ec.Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
928 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
932 if (has_implicit_parameters)
935 TypeSpec type = invoke_pd.Types [i];
937 // We assume that generic parameters are always inflated
938 if (TypeManager.IsGenericParameter (type))
941 if (TypeManager.HasElementType (type) && TypeManager.IsGenericParameter (TypeManager.GetElementType (type)))
944 if (!TypeSpecComparer.IsEqual (invoke_pd.Types [i], Parameters.Types [i])) {
948 ec.Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
950 TypeManager.CSharpName (Parameters.Types [i]),
951 TypeManager.CSharpName (invoke_pd.Types [i]));
960 // Infers type arguments based on explicit arguments
962 public bool ExplicitTypeInference (ResolveContext ec, TypeInferenceContext type_inference, TypeSpec delegate_type)
964 if (!HasExplicitParameters)
967 if (!delegate_type.IsDelegate) {
968 if (delegate_type.GetDefinition () != TypeManager.expression_type)
971 delegate_type = TypeManager.GetTypeArguments (delegate_type) [0];
972 if (!delegate_type.IsDelegate)
976 AParametersCollection d_params = Delegate.GetParameters (delegate_type);
977 if (d_params.Count != Parameters.Count)
980 for (int i = 0; i < Parameters.Count; ++i) {
981 TypeSpec itype = d_params.Types [i];
982 if (!TypeManager.IsGenericParameter (itype)) {
983 if (!TypeManager.HasElementType (itype))
986 if (!TypeManager.IsGenericParameter (TypeManager.GetElementType (itype)))
989 type_inference.ExactInference (Parameters.Types [i], itype);
994 public TypeSpec InferReturnType (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type)
997 AnonymousExpression am;
999 if (compatibles.TryGetValue (delegate_type, out expr)) {
1000 am = expr as AnonymousExpression;
1001 return am == null ? null : am.ReturnType;
1004 using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) {
1005 am = CompatibleMethodBody (ec, tic, InternalType.Arglist, delegate_type);
1007 am = am.Compatible (ec);
1013 // compatibles.Add (delegate_type, am);
1014 return am.ReturnType;
1018 // Returns AnonymousMethod container if this anonymous method
1019 // expression can be implicitly converted to the delegate type `delegate_type'
1021 public Expression Compatible (ResolveContext ec, TypeSpec type)
1024 if (compatibles.TryGetValue (type, out am))
1027 TypeSpec delegate_type = CompatibleChecks (ec, type);
1028 if (delegate_type == null)
1032 // At this point its the first time we know the return type that is
1033 // needed for the anonymous method. We create the method here.
1036 var invoke_mb = Delegate.GetInvokeMethod (delegate_type);
1037 TypeSpec return_type = invoke_mb.ReturnType;
1040 // Second: the return type of the delegate must be compatible with
1041 // the anonymous type. Instead of doing a pass to examine the block
1042 // we satisfy the rule by setting the return type on the EmitContext
1043 // to be the delegate type return type.
1046 var body = CompatibleMethodBody (ec, null, return_type, delegate_type);
1050 bool etree_conversion = delegate_type != type;
1053 if (etree_conversion) {
1054 if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
1056 // Nested expression tree lambda use same scope as parent
1057 // lambda, this also means no variable capturing between this
1060 am = body.Compatible (ec, ec.CurrentAnonymousMethod);
1063 // Quote nested expression tree
1066 am = new Quote (am);
1068 int errors = ec.Report.Errors;
1070 using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) {
1071 am = body.Compatible (ec);
1075 // Rewrite expressions into expression tree when targeting Expression<T>
1077 if (am != null && errors == ec.Report.Errors)
1078 am = CreateExpressionTree (ec, delegate_type);
1081 am = body.Compatible (ec);
1083 } catch (CompletionResult) {
1085 } catch (Exception e) {
1086 throw new InternalErrorException (e, loc);
1089 if (!ec.IsInProbingMode) {
1090 compatibles.Add (type, am ?? EmptyExpression.Null);
1096 protected virtual Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type)
1098 return CreateExpressionTree (ec);
1101 public override Expression CreateExpressionTree (ResolveContext ec)
1103 ec.Report.Error (1946, loc, "An anonymous method cannot be converted to an expression tree");
1107 protected virtual ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type)
1109 var delegate_parameters = Delegate.GetParameters (delegate_type);
1111 if (Parameters == ParametersCompiled.Undefined) {
1113 // We provide a set of inaccessible parameters
1115 Parameter[] fixedpars = new Parameter[delegate_parameters.Count];
1117 for (int i = 0; i < delegate_parameters.Count; i++) {
1118 Parameter.Modifier i_mod = delegate_parameters.FixedParameters [i].ModFlags;
1119 if (i_mod == Parameter.Modifier.OUT) {
1120 if (!ec.IsInProbingMode) {
1121 ec.Report.Error (1688, loc,
1122 "Cannot convert anonymous method block without a parameter list to delegate type `{0}' because it has one or more `out' parameters",
1123 delegate_type.GetSignatureForError ());
1128 fixedpars[i] = new Parameter (
1129 new TypeExpression (delegate_parameters.Types [i], loc), null,
1130 delegate_parameters.FixedParameters [i].ModFlags, null, loc);
1133 return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types);
1136 if (!VerifyExplicitParameters (ec, delegate_type, delegate_parameters)) {
1143 protected override Expression DoResolve (ResolveContext ec)
1145 if (ec.HasSet (ResolveContext.Options.ConstantScope)) {
1146 ec.Report.Error (1706, loc, "Anonymous methods and lambda expressions cannot be used in the current context");
1151 // Set class type, set type
1154 eclass = ExprClass.Value;
1157 // This hack means `The type is not accessible
1158 // anywhere', we depend on special conversion
1161 type = InternalType.AnonymousMethod;
1163 if (!DoResolveParameters (ec))
1166 // FIXME: The emitted code isn't very careful about reachability
1167 // so, ensure we have a 'ret' at the end
1168 BlockContext bc = ec as BlockContext;
1169 if (bc != null && bc.CurrentBranching != null && bc.CurrentBranching.CurrentUsageVector.IsUnreachable)
1170 bc.NeedReturnLabel ();
1175 protected virtual bool DoResolveParameters (ResolveContext rc)
1177 return Parameters.Resolve (rc);
1180 public override void Emit (EmitContext ec)
1182 // nothing, as we only exist to not do anything.
1185 public static void Error_AddressOfCapturedVar (ResolveContext ec, IVariableReference var, Location loc)
1187 ec.Report.Error (1686, loc,
1188 "Local variable or parameter `{0}' cannot have their address taken and be used inside an anonymous method or lambda expression",
1192 public override string GetSignatureForError ()
1194 return ExprClassName;
1197 AnonymousMethodBody CompatibleMethodBody (ResolveContext ec, TypeInferenceContext tic, TypeSpec return_type, TypeSpec delegate_type)
1199 ParametersCompiled p = ResolveParameters (ec, tic, delegate_type);
1203 ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block;
1205 return CompatibleMethodFactory (return_type, delegate_type, p, b);
1209 protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b)
1211 return new AnonymousMethodBody (p, b, return_type, delegate_type, loc);
1214 protected override void CloneTo (CloneContext clonectx, Expression t)
1216 AnonymousMethodExpression target = (AnonymousMethodExpression) t;
1218 target.Block = (ParametersBlock) clonectx.LookupBlock (Block);
1223 // Abstract expression for any block which requires variables hoisting
1225 public abstract class AnonymousExpression : Expression
1227 protected class AnonymousMethodMethod : Method
1229 public readonly AnonymousExpression AnonymousMethod;
1230 public readonly AnonymousMethodStorey Storey;
1231 readonly string RealName;
1233 public AnonymousMethodMethod (DeclSpace parent, AnonymousExpression am, AnonymousMethodStorey storey,
1234 GenericMethod generic, TypeExpr return_type,
1235 Modifiers mod, string real_name, MemberName name,
1236 ParametersCompiled parameters)
1237 : base (parent, generic, return_type, mod | Modifiers.COMPILER_GENERATED,
1238 name, parameters, null)
1240 this.AnonymousMethod = am;
1241 this.Storey = storey;
1242 this.RealName = real_name;
1244 Parent.PartialContainer.AddMethod (this);
1245 Block = new ToplevelBlock (am.block, parameters);
1248 public override EmitContext CreateEmitContext (ILGenerator ig)
1250 EmitContext ec = new EmitContext (this, ig, ReturnType);
1251 ec.CurrentAnonymousMethod = AnonymousMethod;
1252 if (AnonymousMethod.return_label != null) {
1253 ec.HasReturnLabel = true;
1254 ec.ReturnLabel = (Label) AnonymousMethod.return_label;
1260 protected override void DefineTypeParameters ()
1262 // Type parameters were cloned
1265 protected override bool ResolveMemberType ()
1267 if (!base.ResolveMemberType ())
1270 if (Storey != null && Storey.Mutator != null) {
1271 if (!parameters.IsEmpty) {
1272 var mutated = Storey.Mutator.Mutate (parameters.Types);
1273 if (mutated != parameters.Types)
1274 parameters = ParametersCompiled.CreateFullyResolved ((Parameter[]) parameters.FixedParameters, mutated);
1277 member_type = Storey.Mutator.Mutate (member_type);
1283 public override void Emit ()
1285 if (MethodBuilder == null) {
1292 public override void EmitExtraSymbolInfo (SourceMethod source)
1294 source.SetRealMethodName (RealName);
1298 protected ParametersBlock block;
1300 public TypeSpec ReturnType;
1302 object return_label;
1304 protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc)
1306 this.ReturnType = return_type;
1311 public abstract string ContainerType { get; }
1312 public abstract bool IsIterator { get; }
1313 public abstract AnonymousMethodStorey Storey { get; }
1315 public AnonymousExpression Compatible (ResolveContext ec)
1317 return Compatible (ec, this);
1320 public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae)
1325 // TODO: Implement clone
1326 BlockContext aec = new BlockContext (ec, block, ReturnType);
1327 aec.CurrentAnonymousMethod = ae;
1329 ResolveContext.Options flags = 0;
1331 var am = this as AnonymousMethodBody;
1333 if (ec.HasSet (ResolveContext.Options.InferReturnType) && am != null) {
1334 am.ReturnTypeInference = new TypeInferenceContext ();
1337 if (ec.IsInProbingMode)
1338 flags |= ResolveContext.Options.ProbingMode;
1340 if (ec.HasSet (ResolveContext.Options.FieldInitializerScope))
1341 flags |= ResolveContext.Options.FieldInitializerScope;
1343 if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion))
1344 flags |= ResolveContext.Options.ExpressionTreeConversion;
1348 var errors = ec.Report.Errors;
1350 bool res = Block.Resolve (ec.CurrentBranching, aec, null);
1352 if (aec.HasReturnLabel)
1353 return_label = aec.ReturnLabel;
1355 if (am != null && am.ReturnTypeInference != null) {
1356 am.ReturnTypeInference.FixAllTypes (ec);
1357 ReturnType = am.ReturnTypeInference.InferredTypeArguments [0];
1358 am.ReturnTypeInference = null;
1361 if (res && errors != ec.Report.Errors)
1364 return res ? this : null;
1367 public void SetHasThisAccess ()
1369 ExplicitBlock b = block;
1371 if (b.HasCapturedThis)
1374 b.HasCapturedThis = true;
1375 b = b.Parent == null ? null : b.Parent.Explicit;
1376 } while (b != null);
1380 // The block that makes up the body for the anonymous method
1382 public ParametersBlock Block {
1390 public class AnonymousMethodBody : AnonymousExpression
1392 protected readonly ParametersCompiled parameters;
1393 AnonymousMethodStorey storey;
1395 AnonymousMethodMethod method;
1398 TypeInferenceContext return_inference;
1400 static int unique_id;
1402 public AnonymousMethodBody (ParametersCompiled parameters,
1403 ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type,
1405 : base (block, return_type, loc)
1407 this.type = delegate_type;
1408 this.parameters = parameters;
1413 public override string ContainerType {
1414 get { return "anonymous method"; }
1417 public override bool IsIterator {
1418 get { return false; }
1421 public TypeInferenceContext ReturnTypeInference {
1423 return return_inference;
1426 return_inference = value;
1430 public override AnonymousMethodStorey Storey {
1431 get { return storey; }
1436 public override Expression CreateExpressionTree (ResolveContext ec)
1438 ec.Report.Error (1945, loc, "An expression tree cannot contain an anonymous method expression");
1442 bool Define (ResolveContext ec)
1444 if (!Block.Resolved && Compatible (ec) == null)
1447 if (block_name == null) {
1448 MemberCore mc = (MemberCore) ec.MemberContext;
1449 block_name = mc.MemberName.Basename;
1456 // Creates a host for the anonymous method
1458 AnonymousMethodMethod DoCreateMethodHost (EmitContext ec)
1461 // Anonymous method body can be converted to
1463 // 1, an instance method in current scope when only `this' is hoisted
1464 // 2, a static method in current scope when neither `this' nor any variable is hoisted
1465 // 3, an instance method in compiler generated storey when any hoisted variable exists
1468 Modifiers modifiers;
1469 if (Block.HasCapturedVariable || Block.HasCapturedThis) {
1470 storey = FindBestMethodStorey ();
1471 modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE;
1473 if (ec.CurrentAnonymousMethod != null)
1474 storey = ec.CurrentAnonymousMethod.Storey;
1476 modifiers = Modifiers.STATIC | Modifiers.PRIVATE;
1479 TypeContainer parent = storey != null ? storey : ec.CurrentTypeDefinition.Parent.PartialContainer;
1481 MemberCore mc = ec.MemberContext as MemberCore;
1482 string name = CompilerGeneratedClass.MakeName (parent != storey ? block_name : null,
1483 "m", null, unique_id++);
1485 MemberName member_name;
1486 GenericMethod generic_method;
1487 if (storey == null && mc.MemberName.TypeArguments != null) {
1488 member_name = new MemberName (name, mc.MemberName.TypeArguments.Clone (), Location);
1490 var hoisted_tparams = ec.CurrentTypeParameters;
1491 var type_params = new TypeParameter[hoisted_tparams.Length];
1492 for (int i = 0; i < type_params.Length; ++i) {
1493 type_params[i] = hoisted_tparams[i].CreateHoistedCopy (parent, null);
1496 generic_method = new GenericMethod (parent.NamespaceEntry, parent, member_name, type_params,
1497 new TypeExpression (ReturnType, Location), parameters);
1499 member_name = new MemberName (name, Location);
1500 generic_method = null;
1503 string real_name = String.Format (
1504 "{0}~{1}{2}", mc.GetSignatureForError (), GetSignatureForError (),
1505 parameters.GetSignatureForError ());
1507 return new AnonymousMethodMethod (parent,
1508 this, storey, generic_method, new TypeExpression (ReturnType, Location), modifiers,
1509 real_name, member_name, parameters);
1512 protected override Expression DoResolve (ResolveContext ec)
1517 eclass = ExprClass.Value;
1521 public override void Emit (EmitContext ec)
1524 // Use same anonymous method implementation for scenarios where same
1525 // code is used from multiple blocks, e.g. field initializers
1527 if (method == null) {
1529 // Delay an anonymous method definition to avoid emitting unused code
1530 // for unreachable blocks or expression trees
1532 method = DoCreateMethodHost (ec);
1536 bool is_static = (method.ModFlags & Modifiers.STATIC) != 0;
1537 if (is_static && am_cache == null) {
1539 // Creates a field cache to store delegate instance if it's not generic
1541 if (!method.MemberName.IsGeneric) {
1542 TypeContainer parent = method.Parent.PartialContainer;
1543 int id = parent.Fields == null ? 0 : parent.Fields.Count;
1544 var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type;
1546 am_cache = new Field (parent, new TypeExpression (cache_type, loc),
1547 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
1548 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "am$cache", id), loc), null);
1550 parent.AddField (am_cache);
1552 // TODO: Implement caching of generated generic static methods
1556 // Some extra class is needed to capture variable generic type
1557 // arguments. Maybe we could re-use anonymous types, with a unique
1558 // anonymous method id, but they are quite heavy.
1560 // Consider : "() => typeof(T);"
1562 // We need something like
1563 // static class Wrap<Tn, Tm, DelegateType> {
1564 // public static DelegateType cache;
1567 // We then specialize local variable to capture all generic parameters
1568 // and delegate type, e.g. "Wrap<Ta, Tb, DelegateTypeInst> cache;"
1573 Label l_initialized = ec.DefineLabel ();
1575 if (am_cache != null) {
1576 ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
1577 ec.Emit (OpCodes.Brtrue_S, l_initialized);
1581 // Load method delegate implementation
1585 ec.Emit (OpCodes.Ldnull);
1586 } else if (storey != null) {
1587 Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext));
1591 ec.Emit (OpCodes.Ldarg_0);
1594 var delegate_method = method.Spec;
1595 if (storey != null && storey.MemberName.IsGeneric) {
1596 TypeSpec t = storey.Instance.Type;
1599 // Mutate anonymous method instance type if we are in nested
1600 // hoisted generic anonymous method storey
1602 if (ec.CurrentAnonymousMethod != null &&
1603 ec.CurrentAnonymousMethod.Storey != null &&
1604 ec.CurrentAnonymousMethod.Storey.Mutator != null) {
1605 t = storey.Mutator.Mutate (t);
1608 ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ()));
1610 if (delegate_method.IsGeneric)
1611 delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, method.TypeParameters);
1613 ec.Emit (OpCodes.Ldftn, delegate_method);
1616 var constructor_method = Delegate.GetConstructor (type);
1617 ec.Emit (OpCodes.Newobj, constructor_method);
1619 if (am_cache != null) {
1620 ec.Emit (OpCodes.Stsfld, am_cache.Spec);
1621 ec.MarkLabel (l_initialized);
1622 ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
1627 // Look for the best storey for this anonymous method
1629 AnonymousMethodStorey FindBestMethodStorey ()
1632 // Use the nearest parent block which has a storey
1634 for (Block b = Block.Parent; b != null; b = b.Parent) {
1635 AnonymousMethodStorey s = b.Explicit.AnonymousMethodStorey;
1643 public override string GetSignatureForError ()
1645 return TypeManager.CSharpName (type);
1648 public static void Reset ()
1655 // Anonymous type container
1657 public class AnonymousTypeClass : CompilerGeneratedClass
1659 // TODO: Merge with AnonymousTypeParameter
1660 public class GeneratedParameter : Parameter
1662 public GeneratedParameter (FullNamedExpression type, AnonymousTypeParameter p)
1663 : base (type, p.Name, Modifier.NONE, null, p.Location)
1668 static int types_counter;
1669 public const string ClassNamePrefix = "<>__AnonType";
1670 public const string SignatureForError = "anonymous type";
1672 readonly IList<AnonymousTypeParameter> parameters;
1674 private AnonymousTypeClass (DeclSpace parent, MemberName name, IList<AnonymousTypeParameter> parameters, Location loc)
1675 : base (parent, name, (parent.Module.Evaluator != null ? Modifiers.PUBLIC : 0) | Modifiers.SEALED)
1677 this.parameters = parameters;
1680 public static AnonymousTypeClass Create (TypeContainer parent, IList<AnonymousTypeParameter> parameters, Location loc)
1682 string name = ClassNamePrefix + types_counter++;
1684 ParametersCompiled all_parameters;
1685 TypeParameterName[] t_params;
1686 SimpleName[] t_args;
1688 if (parameters.Count == 0) {
1689 all_parameters = ParametersCompiled.EmptyReadOnlyParameters;
1690 t_params = new TypeParameterName[0];
1693 t_args = new SimpleName[parameters.Count];
1694 t_params = new TypeParameterName[parameters.Count];
1695 Parameter[] ctor_params = new Parameter[parameters.Count];
1696 for (int i = 0; i < parameters.Count; ++i) {
1697 AnonymousTypeParameter p = parameters[i];
1699 t_args[i] = new SimpleName ("<" + p.Name + ">__T", p.Location);
1700 t_params[i] = new TypeParameterName (t_args[i].Name, null, p.Location);
1701 ctor_params[i] = new GeneratedParameter (t_args[i], p);
1704 all_parameters = new ParametersCompiled (ctor_params);
1708 // Create generic anonymous type host with generic arguments
1709 // named upon properties names
1711 AnonymousTypeClass a_type = new AnonymousTypeClass (parent.NamespaceEntry.SlaveDeclSpace,
1712 new MemberName (name, new TypeArguments (t_params), loc), parameters, loc);
1714 if (parameters.Count > 0)
1715 a_type.SetParameterInfo (null);
1717 Constructor c = new Constructor (a_type, name, Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
1718 null, all_parameters, null, loc);
1719 c.Block = new ToplevelBlock (parent.Module.Compiler, c.ParameterInfo, loc);
1722 // Create fields and contructor body with field initialization
1725 for (int i = 0; i < parameters.Count; ++i) {
1726 AnonymousTypeParameter p = parameters [i];
1728 Field f = new Field (a_type, t_args [i], Modifiers.PRIVATE | Modifiers.READONLY,
1729 new MemberName ("<" + p.Name + ">", p.Location), null);
1731 if (!a_type.AddField (f)) {
1736 c.Block.AddStatement (new StatementExpression (
1737 new SimpleAssign (new MemberAccess (new This (p.Location), f.Name),
1738 c.Block.GetParameterReference (i, p.Location))));
1740 ToplevelBlock get_block = new ToplevelBlock (parent.Module.Compiler, p.Location);
1741 get_block.AddStatement (new Return (
1742 new MemberAccess (new This (p.Location), f.Name), p.Location));
1744 Property prop = new Property (a_type, t_args [i], Modifiers.PUBLIC,
1745 new MemberName (p.Name, p.Location), null);
1746 prop.Get = new Property.GetMethod (prop, 0, null, p.Location);
1747 prop.Get.Block = get_block;
1748 a_type.AddProperty (prop);
1754 a_type.AddConstructor (c);
1758 public static void Reset ()
1763 protected override bool AddToContainer (MemberCore symbol, string name)
1765 MemberCore mc = GetDefinition (name);
1768 defined_names.Add (name, symbol);
1772 // A conflict between anonymous type members will be reported
1773 if (symbol is TypeParameter) {
1774 Report.SymbolRelatedToPreviousError (symbol);
1778 // Ignore other conflicts
1782 protected override bool DoDefineMembers ()
1784 if (!base.DoDefineMembers ())
1787 Location loc = Location;
1789 var equals_parameters = ParametersCompiled.CreateFullyResolved (
1790 new Parameter (new TypeExpression (TypeManager.object_type, loc), "obj", 0, null, loc), TypeManager.object_type);
1792 Method equals = new Method (this, null, new TypeExpression (TypeManager.bool_type, loc),
1793 Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("Equals", loc),
1794 equals_parameters, null);
1796 equals_parameters[0].Resolve (equals, 0);
1798 Method tostring = new Method (this, null, new TypeExpression (TypeManager.string_type, loc),
1799 Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc),
1800 Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
1802 ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc);
1804 TypeExpr current_type;
1805 if (type_params != null) {
1806 var targs = new TypeArguments ();
1807 foreach (var type_param in type_params)
1808 targs.Add (new TypeParameterExpr (type_param, type_param.Location));
1810 current_type = new GenericTypeExpr (Definition, targs, loc);
1812 current_type = new TypeExpression (Definition, loc);
1815 var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc);
1816 equals_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_other.Type, loc), li_other));
1817 var other_variable = new LocalVariableReference (li_other, loc);
1819 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
1820 new QualifiedAliasMember ("global", "System", loc), "Collections", loc), "Generic", loc);
1822 Expression rs_equals = null;
1823 Expression string_concat = new StringConstant ("{", loc);
1824 Expression rs_hashcode = new IntConstant (-2128831035, loc);
1825 for (int i = 0; i < parameters.Count; ++i) {
1826 var p = parameters [i];
1829 MemberAccess equality_comparer = new MemberAccess (new MemberAccess (
1830 system_collections_generic, "EqualityComparer",
1831 new TypeArguments (new SimpleName (CurrentTypeParameters [i].Name, loc)), loc),
1834 Arguments arguments_equal = new Arguments (2);
1835 arguments_equal.Add (new Argument (new MemberAccess (new This (f.Location), f.Name)));
1836 arguments_equal.Add (new Argument (new MemberAccess (other_variable, f.Name)));
1838 Expression field_equal = new Invocation (new MemberAccess (equality_comparer,
1839 "Equals", loc), arguments_equal);
1841 Arguments arguments_hashcode = new Arguments (1);
1842 arguments_hashcode.Add (new Argument (new MemberAccess (new This (f.Location), f.Name)));
1843 Expression field_hashcode = new Invocation (new MemberAccess (equality_comparer,
1844 "GetHashCode", loc), arguments_hashcode);
1846 IntConstant FNV_prime = new IntConstant (16777619, loc);
1847 rs_hashcode = new Binary (Binary.Operator.Multiply,
1848 new Binary (Binary.Operator.ExclusiveOr, rs_hashcode, field_hashcode, loc),
1851 Expression field_to_string = new Conditional (new BooleanExpression (new Binary (Binary.Operator.Inequality,
1852 new MemberAccess (new This (f.Location), f.Name), new NullLiteral (loc), loc)),
1853 new Invocation (new MemberAccess (
1854 new MemberAccess (new This (f.Location), f.Name), "ToString"), null),
1855 new StringConstant (string.Empty, loc), loc);
1857 if (rs_equals == null) {
1858 rs_equals = field_equal;
1859 string_concat = new Binary (Binary.Operator.Addition,
1861 new Binary (Binary.Operator.Addition,
1862 new StringConstant (" " + p.Name + " = ", loc),
1870 // Implementation of ToString () body using string concatenation
1872 string_concat = new Binary (Binary.Operator.Addition,
1873 new Binary (Binary.Operator.Addition,
1875 new StringConstant (", " + p.Name + " = ", loc),
1880 rs_equals = new Binary (Binary.Operator.LogicalAnd, rs_equals, field_equal, loc);
1883 string_concat = new Binary (Binary.Operator.Addition,
1885 new StringConstant (" }", loc),
1889 // Equals (object obj) override
1891 var other_variable_assign = new TemporaryVariableReference (li_other, loc);
1892 equals_block.AddStatement (new StatementExpression (
1893 new SimpleAssign (other_variable_assign,
1894 new As (equals_block.GetParameterReference (0, loc),
1895 current_type, loc), loc)));
1897 Expression equals_test = new Binary (Binary.Operator.Inequality, other_variable, new NullLiteral (loc), loc);
1898 if (rs_equals != null)
1899 equals_test = new Binary (Binary.Operator.LogicalAnd, equals_test, rs_equals, loc);
1900 equals_block.AddStatement (new Return (equals_test, loc));
1902 equals.Block = equals_block;
1907 // GetHashCode () override
1909 Method hashcode = new Method (this, null, new TypeExpression (TypeManager.int32_type, loc),
1910 Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN,
1911 new MemberName ("GetHashCode", loc),
1912 Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
1915 // Modified FNV with good avalanche behavior and uniform
1916 // distribution with larger hash sizes.
1918 // const int FNV_prime = 16777619;
1919 // int hash = (int) 2166136261;
1920 // foreach (int d in data)
1921 // hash = (hash ^ d) * FNV_prime;
1922 // hash += hash << 13;
1923 // hash ^= hash >> 7;
1924 // hash += hash << 3;
1925 // hash ^= hash >> 17;
1926 // hash += hash << 5;
1928 ToplevelBlock hashcode_top = new ToplevelBlock (Compiler, loc);
1929 Block hashcode_block = new Block (hashcode_top, loc, loc);
1930 hashcode_top.AddStatement (new Unchecked (hashcode_block, loc));
1932 var li_hash = LocalVariable.CreateCompilerGenerated (TypeManager.int32_type, hashcode_top, loc);
1933 hashcode_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_hash.Type, loc), li_hash));
1934 LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc);
1935 hashcode_block.AddStatement (new StatementExpression (
1936 new SimpleAssign (hash_variable_assign, rs_hashcode)));
1938 var hash_variable = new LocalVariableReference (li_hash, loc);
1939 hashcode_block.AddStatement (new StatementExpression (
1940 new CompoundAssign (Binary.Operator.Addition, hash_variable,
1941 new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (13, loc), loc), loc)));
1942 hashcode_block.AddStatement (new StatementExpression (
1943 new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable,
1944 new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (7, loc), loc), loc)));
1945 hashcode_block.AddStatement (new StatementExpression (
1946 new CompoundAssign (Binary.Operator.Addition, hash_variable,
1947 new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (3, loc), loc), loc)));
1948 hashcode_block.AddStatement (new StatementExpression (
1949 new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable,
1950 new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (17, loc), loc), loc)));
1951 hashcode_block.AddStatement (new StatementExpression (
1952 new CompoundAssign (Binary.Operator.Addition, hash_variable,
1953 new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (5, loc), loc), loc)));
1955 hashcode_block.AddStatement (new Return (hash_variable, loc));
1956 hashcode.Block = hashcode_top;
1958 AddMethod (hashcode);
1961 // ToString () override
1964 ToplevelBlock tostring_block = new ToplevelBlock (Compiler, loc);
1965 tostring_block.AddStatement (new Return (string_concat, loc));
1966 tostring.Block = tostring_block;
1968 AddMethod (tostring);
1973 public override string GetSignatureForError ()
1975 return SignatureForError;
1978 public IList<AnonymousTypeParameter> Parameters {