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 (TypeContainer 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 (TypeContainer 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)
200 OriginalSourceBlock = block;
204 public void AddCapturedThisField (EmitContext ec)
206 TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location);
207 Field f = AddCompilerGeneratedField ("<>f__this", type_expr);
209 hoisted_this = new HoistedThis (this, f);
211 // Inflated type instance has to be updated manually
212 if (Instance.Type is InflatedTypeSpec) {
213 var inflator = new TypeParameterInflator (this, Instance.Type, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
214 Instance.Type.MemberCache.AddMember (f.Spec.InflateMember (inflator));
216 inflator = new TypeParameterInflator (this, f.Parent.CurrentType, TypeParameterSpec.EmptyTypes, TypeSpec.EmptyTypes);
217 f.Parent.CurrentType.MemberCache.AddMember (f.Spec.InflateMember (inflator));
221 public Field AddCapturedVariable (string name, TypeSpec type)
223 CheckMembersDefined ();
225 FullNamedExpression field_type = new TypeExpression (type, Location);
227 return AddCompilerGeneratedField (name, field_type);
229 const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
230 Field f = new HoistedField (this, field_type, mod, name, null, Location);
235 protected Field AddCompilerGeneratedField (string name, FullNamedExpression type)
237 const Modifiers mod = Modifiers.INTERNAL | Modifiers.COMPILER_GENERATED;
238 Field f = new Field (this, type, mod, new MemberName (name, Location), null);
244 // Creates a link between hoisted variable block and the anonymous method storey
246 // An anonymous method can reference variables from any outer block, but they are
247 // hoisted in their own ExplicitBlock. When more than one block is referenced we
248 // need to create another link between those variable storeys
250 public void AddReferenceFromChildrenBlock (ExplicitBlock block)
252 if (children_references == null)
253 children_references = new List<ExplicitBlock> ();
255 if (!children_references.Contains (block))
256 children_references.Add (block);
259 public void AddParentStoreyReference (EmitContext ec, AnonymousMethodStorey storey)
261 CheckMembersDefined ();
263 if (used_parent_storeys == null)
264 used_parent_storeys = new List<StoreyFieldPair> ();
265 else if (used_parent_storeys.Exists (i => i.Storey == storey))
268 TypeExpr type_expr = storey.CreateStoreyTypeExpression (ec);
269 Field f = AddCompilerGeneratedField ("<>f__ref$" + storey.ID, type_expr);
270 used_parent_storeys.Add (new StoreyFieldPair (storey, f));
273 public void CaptureLocalVariable (ResolveContext ec, LocalVariable local_info)
275 ec.CurrentBlock.Explicit.HasCapturedVariable = true;
276 if (ec.CurrentBlock.Explicit != local_info.Block.Explicit)
277 AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit);
279 if (local_info.HoistedVariant != null)
282 HoistedVariable var = new HoistedLocalVariable (this, local_info, GetVariableMangledName (local_info));
283 local_info.HoistedVariant = var;
285 if (hoisted_locals == null)
286 hoisted_locals = new List<HoistedVariable> ();
288 hoisted_locals.Add (var);
291 public void CaptureParameter (ResolveContext ec, ParameterReference param_ref)
293 ec.CurrentBlock.Explicit.HasCapturedVariable = true;
294 AddReferenceFromChildrenBlock (ec.CurrentBlock.Explicit);
296 if (param_ref.GetHoistedVariable (ec) != null)
299 if (hoisted_params == null)
300 hoisted_params = new List<HoistedParameter> (2);
302 var expr = new HoistedParameter (this, param_ref);
303 param_ref.Parameter.HoistedVariant = expr;
304 hoisted_params.Add (expr);
307 TypeExpr CreateStoreyTypeExpression (EmitContext ec)
310 // Create an instance of storey type
312 TypeExpr storey_type_expr;
313 if (CurrentTypeParameters != null) {
315 // Use current method type parameter (MVAR) for top level storey only. All
316 // nested storeys use class type parameter (VAR)
318 TypeParameter[] tparams = ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null ?
319 ec.CurrentAnonymousMethod.Storey.TypeParameters :
320 ec.CurrentTypeParameters;
322 TypeArguments targs = new TypeArguments ();
325 // Use type parameter name instead of resolved type parameter
326 // specification to resolve to correctly nested type parameters
328 for (int i = 0; i < tparams.Length; ++i)
329 targs.Add (new SimpleName (tparams [i].Name, Location)); // new TypeParameterExpr (tparams[i], Location));
331 storey_type_expr = new GenericTypeExpr (Definition, targs, Location);
333 storey_type_expr = new TypeExpression (CurrentType, Location);
336 return storey_type_expr;
339 public void SetNestedStoryParent (AnonymousMethodStorey parentStorey)
341 Parent = parentStorey;
343 spec.IsGeneric = false;
344 spec.DeclaringType = parentStorey.CurrentType;
345 MemberName.TypeArguments = null;
348 protected override bool DoResolveTypeParameters ()
350 // Although any storey can have type parameters they are all clones of method type
351 // parameters therefore have to mutate MVAR references in any of cloned constraints
352 if (type_params != null) {
353 for (int i = 0; i < type_params.Length; ++i) {
354 var spec = type_params[i].Type;
355 spec.BaseType = mutator.Mutate (spec.BaseType);
356 if (spec.InterfacesDefined != null) {
357 var mutated = new TypeSpec[spec.InterfacesDefined.Length];
358 for (int ii = 0; ii < mutated.Length; ++ii) {
359 mutated[ii] = mutator.Mutate (spec.InterfacesDefined[ii]);
362 spec.InterfacesDefined = mutated;
365 if (spec.TypeArguments != null) {
366 spec.TypeArguments = mutator.Mutate (spec.TypeArguments);
372 // Update parent cache as we most likely passed the point
373 // where the cache was constructed
375 Parent.CurrentType.MemberCache.AddMember (this.spec);
381 // Initializes all hoisted variables
383 public void EmitStoreyInstantiation (EmitContext ec, ExplicitBlock block)
385 // There can be only one instance variable for each storey type
386 if (Instance != null)
387 throw new InternalErrorException ();
389 SymbolWriter.OpenCompilerGeneratedBlock (ec);
392 // Create an instance of a storey
394 var storey_type_expr = CreateStoreyTypeExpression (ec);
396 ResolveContext rc = new ResolveContext (ec.MemberContext);
397 rc.CurrentBlock = block;
398 Expression e = new New (storey_type_expr, null, Location).Resolve (rc);
401 Instance = new LocalTemporary (storey_type_expr.Type);
404 EmitHoistedFieldsInitialization (rc, ec);
406 SymbolWriter.DefineScopeVariable (ID, Instance.Builder);
407 SymbolWriter.CloseCompilerGeneratedBlock (ec);
410 void EmitHoistedFieldsInitialization (ResolveContext rc, EmitContext ec)
413 // Initialize all storey reference fields by using local or hoisted variables
415 if (used_parent_storeys != null) {
416 foreach (StoreyFieldPair sf in used_parent_storeys) {
418 // Get instance expression of storey field
420 Expression instace_expr = GetStoreyInstanceExpression (ec);
421 var fs = sf.Field.Spec;
422 if (TypeManager.IsGenericType (instace_expr.Type))
423 fs = MemberCache.GetMember (instace_expr.Type, fs);
425 FieldExpr f_set_expr = new FieldExpr (fs, Location);
426 f_set_expr.InstanceExpression = instace_expr;
428 SimpleAssign a = new SimpleAssign (f_set_expr, sf.Storey.GetStoreyInstanceExpression (ec));
429 if (a.Resolve (rc) != null)
430 a.EmitStatement (ec);
435 // Define hoisted `this' in top-level storey only
437 if (OriginalSourceBlock.Explicit.HasCapturedThis && !(Parent is AnonymousMethodStorey)) {
438 AddCapturedThisField (ec);
439 rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this));
443 // Setting currect anonymous method to null blocks any further variable hoisting
445 AnonymousExpression ae = ec.CurrentAnonymousMethod;
446 ec.CurrentAnonymousMethod = null;
448 if (hoisted_params != null) {
449 EmitHoistedParameters (ec, hoisted_params);
452 ec.CurrentAnonymousMethod = ae;
455 protected virtual void EmitHoistedParameters (EmitContext ec, IList<HoistedParameter> hoisted)
457 foreach (HoistedParameter hp in hoisted) {
458 hp.EmitHoistingAssignment (ec);
462 public override void EmitType ()
464 SymbolWriter.DefineAnonymousScope (ID);
466 if (hoisted_this != null)
467 hoisted_this.EmitSymbolInfo ();
469 if (hoisted_locals != null) {
470 foreach (HoistedVariable local in hoisted_locals)
471 local.EmitSymbolInfo ();
474 if (hoisted_params != null) {
475 foreach (HoistedParameter param in hoisted_params)
476 param.EmitSymbolInfo ();
479 if (used_parent_storeys != null) {
480 foreach (StoreyFieldPair sf in used_parent_storeys) {
481 SymbolWriter.DefineCapturedScope (ID, sf.Storey.ID, sf.Field.Name);
489 // Returns a field which holds referenced storey instance
491 Field GetReferencedStoreyField (AnonymousMethodStorey storey)
493 if (used_parent_storeys == null)
496 foreach (StoreyFieldPair sf in used_parent_storeys) {
497 if (sf.Storey == storey)
505 // Creates storey instance expression regardless of currect IP
507 public Expression GetStoreyInstanceExpression (EmitContext ec)
509 AnonymousExpression am = ec.CurrentAnonymousMethod;
512 // Access from original block -> storey
518 // Access from anonymous method implemented as a static -> storey
520 if (am.Storey == null)
523 Field f = am.Storey.GetReferencedStoreyField (this);
525 if (am.Storey == this) {
527 // Access inside of same storey (S -> S)
529 return new CompilerGeneratedThis (CurrentType, Location);
532 // External field access
538 // Storey was cached to local field
540 FieldExpr f_ind = new FieldExpr (f, Location);
541 f_ind.InstanceExpression = new CompilerGeneratedThis (CurrentType, Location);
545 protected virtual string GetVariableMangledName (LocalVariable local_info)
548 // No need to mangle anonymous method hoisted variables cause they
549 // are hoisted in their own scopes
551 return local_info.Name;
554 public HoistedThis HoistedThis {
555 get { return hoisted_this; }
558 public IList<ExplicitBlock> ReferencesFromChildrenBlock {
559 get { return children_references; }
562 public static void Reset ()
568 public abstract class HoistedVariable
571 // Hoisted version of variable references used in expression
572 // tree has to be delayed until we know its location. The variable
573 // doesn't know its location until all stories are calculated
575 class ExpressionTreeVariableReference : Expression
577 readonly HoistedVariable hv;
579 public ExpressionTreeVariableReference (HoistedVariable hv)
584 public override Expression CreateExpressionTree (ResolveContext ec)
586 return hv.CreateExpressionTree ();
589 protected override Expression DoResolve (ResolveContext ec)
591 eclass = ExprClass.Value;
592 type = ec.Module.PredefinedTypes.Expression.Resolve (Location);
596 public override void Emit (EmitContext ec)
598 ResolveContext rc = new ResolveContext (ec.MemberContext);
599 Expression e = hv.GetFieldExpression (ec).CreateExpressionTree (rc);
600 // This should never fail
607 protected readonly AnonymousMethodStorey storey;
608 protected Field field;
609 Dictionary<AnonymousExpression, FieldExpr> cached_inner_access; // TODO: Hashtable is too heavyweight
610 FieldExpr cached_outer_access;
612 protected HoistedVariable (AnonymousMethodStorey storey, string name, TypeSpec type)
613 : this (storey, storey.AddCapturedVariable (name, type))
617 protected HoistedVariable (AnonymousMethodStorey storey, Field field)
619 this.storey = storey;
623 public void AddressOf (EmitContext ec, AddressOp mode)
625 GetFieldExpression (ec).AddressOf (ec, mode);
628 public Expression CreateExpressionTree ()
630 return new ExpressionTreeVariableReference (this);
633 public void Emit (EmitContext ec)
635 GetFieldExpression (ec).Emit (ec);
639 // Creates field access expression for hoisted variable
641 protected virtual FieldExpr GetFieldExpression (EmitContext ec)
643 if (ec.CurrentAnonymousMethod == null || ec.CurrentAnonymousMethod.Storey == null) {
644 if (cached_outer_access != null)
645 return cached_outer_access;
648 // When setting top-level hoisted variable in generic storey
649 // change storey generic types to method generic types (VAR -> MVAR)
651 if (storey.Instance.Type.IsGenericOrParentIsGeneric) {
652 var fs = MemberCache.GetMember (storey.Instance.Type, field.Spec);
653 cached_outer_access = new FieldExpr (fs, field.Location);
655 cached_outer_access = new FieldExpr (field, field.Location);
658 cached_outer_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec);
659 return cached_outer_access;
662 FieldExpr inner_access;
663 if (cached_inner_access != null) {
664 if (!cached_inner_access.TryGetValue (ec.CurrentAnonymousMethod, out inner_access))
668 cached_inner_access = new Dictionary<AnonymousExpression, FieldExpr> (4);
671 if (inner_access == null) {
672 if (field.Parent.IsGeneric) {
673 var fs = MemberCache.GetMember (field.Parent.CurrentType, field.Spec);
674 inner_access = new FieldExpr (fs, field.Location);
676 inner_access = new FieldExpr (field, field.Location);
679 inner_access.InstanceExpression = storey.GetStoreyInstanceExpression (ec);
680 cached_inner_access.Add (ec.CurrentAnonymousMethod, inner_access);
686 public abstract void EmitSymbolInfo ();
688 public void Emit (EmitContext ec, bool leave_copy)
690 GetFieldExpression (ec).Emit (ec, leave_copy);
693 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
695 GetFieldExpression (ec).EmitAssign (ec, source, leave_copy, false);
699 public class HoistedParameter : HoistedVariable
701 sealed class HoistedFieldAssign : Assign
703 public HoistedFieldAssign (Expression target, Expression source)
704 : base (target, source, source.Location)
708 protected override Expression ResolveConversions (ResolveContext ec)
711 // Implicit conversion check fails for hoisted type arguments
712 // as they are of different types (!!0 x !0)
718 readonly ParameterReference parameter;
720 public HoistedParameter (AnonymousMethodStorey scope, ParameterReference par)
721 : base (scope, par.Name, par.Type)
723 this.parameter = par;
726 public HoistedParameter (HoistedParameter hp, string name)
727 : base (hp.storey, name, hp.parameter.Type)
729 this.parameter = hp.parameter;
732 public void EmitHoistingAssignment (EmitContext ec)
735 // Remove hoisted redirection to emit assignment from original parameter
737 HoistedVariable temp = parameter.Parameter.HoistedVariant;
738 parameter.Parameter.HoistedVariant = null;
740 Assign a = new HoistedFieldAssign (GetFieldExpression (ec), parameter);
741 if (a.Resolve (new ResolveContext (ec.MemberContext)) != null)
742 a.EmitStatement (ec);
744 parameter.Parameter.HoistedVariant = temp;
747 public override void EmitSymbolInfo ()
749 SymbolWriter.DefineCapturedParameter (storey.ID, field.Name, field.Name);
753 get { return field; }
757 class HoistedLocalVariable : HoistedVariable
759 readonly string name;
761 public HoistedLocalVariable (AnonymousMethodStorey scope, LocalVariable local, string name)
762 : base (scope, name, local.Type)
764 this.name = local.Name;
767 public override void EmitSymbolInfo ()
769 SymbolWriter.DefineCapturedLocal (storey.ID, name, field.Name);
773 public class HoistedThis : HoistedVariable
775 public HoistedThis (AnonymousMethodStorey storey, Field field)
776 : base (storey, field)
780 public void EmitHoistingAssignment (EmitContext ec)
782 SimpleAssign a = new SimpleAssign (GetFieldExpression (ec), new CompilerGeneratedThis (ec.CurrentType, field.Location));
783 if (a.Resolve (new ResolveContext (ec.MemberContext)) != null)
784 a.EmitStatement (ec);
787 public override void EmitSymbolInfo ()
789 SymbolWriter.DefineCapturedThis (storey.ID, field.Name);
793 get { return field; }
798 // Anonymous method expression as created by parser
800 public class AnonymousMethodExpression : Expression
803 // Special conversion for nested expression tree lambdas
805 class Quote : ShimExpression
807 public Quote (Expression expr)
812 public override Expression CreateExpressionTree (ResolveContext ec)
814 var args = new Arguments (1);
815 args.Add (new Argument (expr.CreateExpressionTree (ec)));
816 return CreateExpressionFactoryCall (ec, "Quote", args);
819 protected override Expression DoResolve (ResolveContext rc)
821 expr = expr.Resolve (rc);
825 eclass = expr.eclass;
831 Dictionary<TypeSpec, Expression> compatibles;
832 public ParametersBlock Block;
834 public AnonymousMethodExpression (Location loc)
837 this.compatibles = new Dictionary<TypeSpec, Expression> ();
840 public override string ExprClassName {
842 return "anonymous method";
846 public virtual bool HasExplicitParameters {
848 return Parameters != ParametersCompiled.Undefined;
852 public ParametersCompiled Parameters {
853 get { return Block.Parameters; }
857 // Returns true if the body of lambda expression can be implicitly
858 // converted to the delegate of type `delegate_type'
860 public bool ImplicitStandardConversionExists (ResolveContext ec, TypeSpec delegate_type)
862 using (ec.With (ResolveContext.Options.InferReturnType, false)) {
863 using (ec.Set (ResolveContext.Options.ProbingMode)) {
864 return Compatible (ec, delegate_type) != null;
869 TypeSpec CompatibleChecks (ResolveContext ec, TypeSpec delegate_type)
871 if (delegate_type.IsDelegate)
872 return delegate_type;
874 if (delegate_type.IsExpressionTreeType) {
875 delegate_type = delegate_type.TypeArguments [0];
876 if (delegate_type.IsDelegate)
877 return delegate_type;
879 ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'",
880 GetSignatureForError (), TypeManager.CSharpName (delegate_type));
884 ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'",
885 GetSignatureForError (), TypeManager.CSharpName (delegate_type));
889 protected bool VerifyExplicitParameters (ResolveContext ec, TypeSpec delegate_type, AParametersCollection parameters)
891 if (VerifyParameterCompatibility (ec, delegate_type, parameters, ec.IsInProbingMode))
894 if (!ec.IsInProbingMode)
895 ec.Report.Error (1661, loc,
896 "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch",
897 GetSignatureForError (), TypeManager.CSharpName (delegate_type));
902 protected bool VerifyParameterCompatibility (ResolveContext ec, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
904 if (Parameters.Count != invoke_pd.Count) {
908 ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
909 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
913 bool has_implicit_parameters = !HasExplicitParameters;
916 for (int i = 0; i < Parameters.Count; ++i) {
917 Parameter.Modifier p_mod = invoke_pd.FixedParameters [i].ModFlags;
918 if (Parameters.FixedParameters [i].ModFlags != p_mod && p_mod != Parameter.Modifier.PARAMS) {
922 if (p_mod == Parameter.Modifier.NONE)
923 ec.Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
924 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.FixedParameters [i].ModFlags));
926 ec.Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
927 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
931 if (has_implicit_parameters)
934 TypeSpec type = invoke_pd.Types [i];
936 // We assume that generic parameters are always inflated
937 if (TypeManager.IsGenericParameter (type))
940 if (TypeManager.HasElementType (type) && TypeManager.IsGenericParameter (TypeManager.GetElementType (type)))
943 if (!TypeSpecComparer.IsEqual (invoke_pd.Types [i], Parameters.Types [i])) {
947 ec.Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
949 TypeManager.CSharpName (Parameters.Types [i]),
950 TypeManager.CSharpName (invoke_pd.Types [i]));
959 // Infers type arguments based on explicit arguments
961 public bool ExplicitTypeInference (ResolveContext ec, TypeInferenceContext type_inference, TypeSpec delegate_type)
963 if (!HasExplicitParameters)
966 if (!delegate_type.IsDelegate) {
967 if (!delegate_type.IsExpressionTreeType)
970 delegate_type = TypeManager.GetTypeArguments (delegate_type) [0];
971 if (!delegate_type.IsDelegate)
975 AParametersCollection d_params = Delegate.GetParameters (delegate_type);
976 if (d_params.Count != Parameters.Count)
979 for (int i = 0; i < Parameters.Count; ++i) {
980 TypeSpec itype = d_params.Types [i];
981 if (!TypeManager.IsGenericParameter (itype)) {
982 if (!TypeManager.HasElementType (itype))
985 if (!TypeManager.IsGenericParameter (TypeManager.GetElementType (itype)))
988 type_inference.ExactInference (Parameters.Types [i], itype);
993 public TypeSpec InferReturnType (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type)
996 AnonymousExpression am;
998 if (compatibles.TryGetValue (delegate_type, out expr)) {
999 am = expr as AnonymousExpression;
1000 return am == null ? null : am.ReturnType;
1003 using (ec.Set (ResolveContext.Options.ProbingMode | ResolveContext.Options.InferReturnType)) {
1004 am = CompatibleMethodBody (ec, tic, InternalType.Arglist, delegate_type);
1006 am = am.Compatible (ec);
1012 // compatibles.Add (delegate_type, am);
1013 return am.ReturnType;
1017 // Returns AnonymousMethod container if this anonymous method
1018 // expression can be implicitly converted to the delegate type `delegate_type'
1020 public Expression Compatible (ResolveContext ec, TypeSpec type)
1023 if (compatibles.TryGetValue (type, out am))
1026 TypeSpec delegate_type = CompatibleChecks (ec, type);
1027 if (delegate_type == null)
1031 // At this point its the first time we know the return type that is
1032 // needed for the anonymous method. We create the method here.
1035 var invoke_mb = Delegate.GetInvokeMethod (delegate_type);
1036 TypeSpec return_type = invoke_mb.ReturnType;
1039 // Second: the return type of the delegate must be compatible with
1040 // the anonymous type. Instead of doing a pass to examine the block
1041 // we satisfy the rule by setting the return type on the EmitContext
1042 // to be the delegate type return type.
1045 var body = CompatibleMethodBody (ec, null, return_type, delegate_type);
1049 bool etree_conversion = delegate_type != type;
1052 if (etree_conversion) {
1053 if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion)) {
1055 // Nested expression tree lambda use same scope as parent
1056 // lambda, this also means no variable capturing between this
1059 am = body.Compatible (ec, ec.CurrentAnonymousMethod);
1062 // Quote nested expression tree
1065 am = new Quote (am);
1067 int errors = ec.Report.Errors;
1069 using (ec.Set (ResolveContext.Options.ExpressionTreeConversion)) {
1070 am = body.Compatible (ec);
1074 // Rewrite expressions into expression tree when targeting Expression<T>
1076 if (am != null && errors == ec.Report.Errors)
1077 am = CreateExpressionTree (ec, delegate_type);
1080 am = body.Compatible (ec);
1082 } catch (CompletionResult) {
1084 } catch (Exception e) {
1085 throw new InternalErrorException (e, loc);
1088 if (!ec.IsInProbingMode) {
1089 compatibles.Add (type, am ?? EmptyExpression.Null);
1095 protected virtual Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type)
1097 return CreateExpressionTree (ec);
1100 public override Expression CreateExpressionTree (ResolveContext ec)
1102 ec.Report.Error (1946, loc, "An anonymous method cannot be converted to an expression tree");
1106 protected virtual ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type)
1108 var delegate_parameters = Delegate.GetParameters (delegate_type);
1110 if (Parameters == ParametersCompiled.Undefined) {
1112 // We provide a set of inaccessible parameters
1114 Parameter[] fixedpars = new Parameter[delegate_parameters.Count];
1116 for (int i = 0; i < delegate_parameters.Count; i++) {
1117 Parameter.Modifier i_mod = delegate_parameters.FixedParameters [i].ModFlags;
1118 if (i_mod == Parameter.Modifier.OUT) {
1119 if (!ec.IsInProbingMode) {
1120 ec.Report.Error (1688, loc,
1121 "Cannot convert anonymous method block without a parameter list to delegate type `{0}' because it has one or more `out' parameters",
1122 delegate_type.GetSignatureForError ());
1127 fixedpars[i] = new Parameter (
1128 new TypeExpression (delegate_parameters.Types [i], loc), null,
1129 delegate_parameters.FixedParameters [i].ModFlags, null, loc);
1132 return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types);
1135 if (!VerifyExplicitParameters (ec, delegate_type, delegate_parameters)) {
1142 protected override Expression DoResolve (ResolveContext ec)
1144 if (ec.HasSet (ResolveContext.Options.ConstantScope)) {
1145 ec.Report.Error (1706, loc, "Anonymous methods and lambda expressions cannot be used in the current context");
1150 // Set class type, set type
1153 eclass = ExprClass.Value;
1156 // This hack means `The type is not accessible
1157 // anywhere', we depend on special conversion
1160 type = InternalType.AnonymousMethod;
1162 if (!DoResolveParameters (ec))
1165 // FIXME: The emitted code isn't very careful about reachability
1166 // so, ensure we have a 'ret' at the end
1167 BlockContext bc = ec as BlockContext;
1168 if (bc != null && bc.CurrentBranching != null && bc.CurrentBranching.CurrentUsageVector.IsUnreachable)
1169 bc.NeedReturnLabel ();
1174 protected virtual bool DoResolveParameters (ResolveContext rc)
1176 return Parameters.Resolve (rc);
1179 public override void Emit (EmitContext ec)
1181 // nothing, as we only exist to not do anything.
1184 public static void Error_AddressOfCapturedVar (ResolveContext ec, IVariableReference var, Location loc)
1186 ec.Report.Error (1686, loc,
1187 "Local variable or parameter `{0}' cannot have their address taken and be used inside an anonymous method or lambda expression",
1191 public override string GetSignatureForError ()
1193 return ExprClassName;
1196 AnonymousMethodBody CompatibleMethodBody (ResolveContext ec, TypeInferenceContext tic, TypeSpec return_type, TypeSpec delegate_type)
1198 ParametersCompiled p = ResolveParameters (ec, tic, delegate_type);
1202 ParametersBlock b = ec.IsInProbingMode ? (ParametersBlock) Block.PerformClone () : Block;
1204 return CompatibleMethodFactory (return_type, delegate_type, p, b);
1208 protected virtual AnonymousMethodBody CompatibleMethodFactory (TypeSpec return_type, TypeSpec delegate_type, ParametersCompiled p, ParametersBlock b)
1210 return new AnonymousMethodBody (p, b, return_type, delegate_type, loc);
1213 protected override void CloneTo (CloneContext clonectx, Expression t)
1215 AnonymousMethodExpression target = (AnonymousMethodExpression) t;
1217 target.Block = (ParametersBlock) clonectx.LookupBlock (Block);
1222 // Abstract expression for any block which requires variables hoisting
1224 public abstract class AnonymousExpression : Expression
1226 protected class AnonymousMethodMethod : Method
1228 public readonly AnonymousExpression AnonymousMethod;
1229 public readonly AnonymousMethodStorey Storey;
1230 readonly string RealName;
1232 public AnonymousMethodMethod (DeclSpace parent, AnonymousExpression am, AnonymousMethodStorey storey,
1233 GenericMethod generic, TypeExpr return_type,
1234 Modifiers mod, string real_name, MemberName name,
1235 ParametersCompiled parameters)
1236 : base (parent, generic, return_type, mod | Modifiers.COMPILER_GENERATED,
1237 name, parameters, null)
1239 this.AnonymousMethod = am;
1240 this.Storey = storey;
1241 this.RealName = real_name;
1243 Parent.PartialContainer.AddMethod (this);
1244 Block = new ToplevelBlock (am.block, parameters);
1247 public override EmitContext CreateEmitContext (ILGenerator ig)
1249 EmitContext ec = new EmitContext (this, ig, ReturnType);
1250 ec.CurrentAnonymousMethod = AnonymousMethod;
1251 if (AnonymousMethod.return_label != null) {
1252 ec.HasReturnLabel = true;
1253 ec.ReturnLabel = (Label) AnonymousMethod.return_label;
1259 protected override void DefineTypeParameters ()
1261 // Type parameters were cloned
1264 protected override bool ResolveMemberType ()
1266 if (!base.ResolveMemberType ())
1269 if (Storey != null && Storey.Mutator != null) {
1270 if (!parameters.IsEmpty) {
1271 var mutated = Storey.Mutator.Mutate (parameters.Types);
1272 if (mutated != parameters.Types)
1273 parameters = ParametersCompiled.CreateFullyResolved ((Parameter[]) parameters.FixedParameters, mutated);
1276 member_type = Storey.Mutator.Mutate (member_type);
1282 public override void Emit ()
1284 if (MethodBuilder == null) {
1291 public override void EmitExtraSymbolInfo (SourceMethod source)
1293 source.SetRealMethodName (RealName);
1297 protected ParametersBlock block;
1299 public TypeSpec ReturnType;
1301 object return_label;
1303 protected AnonymousExpression (ParametersBlock block, TypeSpec return_type, Location loc)
1305 this.ReturnType = return_type;
1310 public abstract string ContainerType { get; }
1311 public abstract bool IsIterator { get; }
1312 public abstract AnonymousMethodStorey Storey { get; }
1314 public AnonymousExpression Compatible (ResolveContext ec)
1316 return Compatible (ec, this);
1319 public AnonymousExpression Compatible (ResolveContext ec, AnonymousExpression ae)
1324 // TODO: Implement clone
1325 BlockContext aec = new BlockContext (ec, block, ReturnType);
1326 aec.CurrentAnonymousMethod = ae;
1328 ResolveContext.Options flags = 0;
1330 var am = this as AnonymousMethodBody;
1332 if (ec.HasSet (ResolveContext.Options.InferReturnType) && am != null) {
1333 am.ReturnTypeInference = new TypeInferenceContext ();
1336 if (ec.IsInProbingMode)
1337 flags |= ResolveContext.Options.ProbingMode;
1339 if (ec.HasSet (ResolveContext.Options.FieldInitializerScope))
1340 flags |= ResolveContext.Options.FieldInitializerScope;
1342 if (ec.HasSet (ResolveContext.Options.ExpressionTreeConversion))
1343 flags |= ResolveContext.Options.ExpressionTreeConversion;
1347 var errors = ec.Report.Errors;
1349 bool res = Block.Resolve (ec.CurrentBranching, aec, null);
1351 if (aec.HasReturnLabel)
1352 return_label = aec.ReturnLabel;
1354 if (am != null && am.ReturnTypeInference != null) {
1355 am.ReturnTypeInference.FixAllTypes (ec);
1356 ReturnType = am.ReturnTypeInference.InferredTypeArguments [0];
1357 am.ReturnTypeInference = null;
1360 if (res && errors != ec.Report.Errors)
1363 return res ? this : null;
1366 public void SetHasThisAccess ()
1368 ExplicitBlock b = block;
1370 if (b.HasCapturedThis)
1373 b.HasCapturedThis = true;
1374 b = b.Parent == null ? null : b.Parent.Explicit;
1375 } while (b != null);
1379 // The block that makes up the body for the anonymous method
1381 public ParametersBlock Block {
1389 public class AnonymousMethodBody : AnonymousExpression
1391 protected readonly ParametersCompiled parameters;
1392 AnonymousMethodStorey storey;
1394 AnonymousMethodMethod method;
1397 TypeInferenceContext return_inference;
1399 static int unique_id;
1401 public AnonymousMethodBody (ParametersCompiled parameters,
1402 ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type,
1404 : base (block, return_type, loc)
1406 this.type = delegate_type;
1407 this.parameters = parameters;
1412 public override string ContainerType {
1413 get { return "anonymous method"; }
1416 public override bool IsIterator {
1417 get { return false; }
1420 public TypeInferenceContext ReturnTypeInference {
1422 return return_inference;
1425 return_inference = value;
1429 public override AnonymousMethodStorey Storey {
1430 get { return storey; }
1435 public override Expression CreateExpressionTree (ResolveContext ec)
1437 ec.Report.Error (1945, loc, "An expression tree cannot contain an anonymous method expression");
1441 bool Define (ResolveContext ec)
1443 if (!Block.Resolved && Compatible (ec) == null)
1446 if (block_name == null) {
1447 MemberCore mc = (MemberCore) ec.MemberContext;
1448 block_name = mc.MemberName.Basename;
1455 // Creates a host for the anonymous method
1457 AnonymousMethodMethod DoCreateMethodHost (EmitContext ec)
1460 // Anonymous method body can be converted to
1462 // 1, an instance method in current scope when only `this' is hoisted
1463 // 2, a static method in current scope when neither `this' nor any variable is hoisted
1464 // 3, an instance method in compiler generated storey when any hoisted variable exists
1467 Modifiers modifiers;
1468 if (Block.HasCapturedVariable || Block.HasCapturedThis) {
1469 storey = FindBestMethodStorey ();
1470 modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE;
1472 if (ec.CurrentAnonymousMethod != null)
1473 storey = ec.CurrentAnonymousMethod.Storey;
1475 modifiers = Modifiers.STATIC | Modifiers.PRIVATE;
1478 TypeContainer parent = storey != null ? storey : ec.CurrentTypeDefinition.Parent.PartialContainer;
1480 MemberCore mc = ec.MemberContext as MemberCore;
1481 string name = CompilerGeneratedClass.MakeName (parent != storey ? block_name : null,
1482 "m", null, unique_id++);
1484 MemberName member_name;
1485 GenericMethod generic_method;
1486 if (storey == null && mc.MemberName.TypeArguments != null) {
1487 member_name = new MemberName (name, mc.MemberName.TypeArguments.Clone (), Location);
1489 var hoisted_tparams = ec.CurrentTypeParameters;
1490 var type_params = new TypeParameter[hoisted_tparams.Length];
1491 for (int i = 0; i < type_params.Length; ++i) {
1492 type_params[i] = hoisted_tparams[i].CreateHoistedCopy (parent, null);
1495 generic_method = new GenericMethod (parent.NamespaceEntry, parent, member_name, type_params,
1496 new TypeExpression (ReturnType, Location), parameters);
1498 member_name = new MemberName (name, Location);
1499 generic_method = null;
1502 string real_name = String.Format (
1503 "{0}~{1}{2}", mc.GetSignatureForError (), GetSignatureForError (),
1504 parameters.GetSignatureForError ());
1506 return new AnonymousMethodMethod (parent,
1507 this, storey, generic_method, new TypeExpression (ReturnType, Location), modifiers,
1508 real_name, member_name, parameters);
1511 protected override Expression DoResolve (ResolveContext ec)
1516 eclass = ExprClass.Value;
1520 public override void Emit (EmitContext ec)
1523 // Use same anonymous method implementation for scenarios where same
1524 // code is used from multiple blocks, e.g. field initializers
1526 if (method == null) {
1528 // Delay an anonymous method definition to avoid emitting unused code
1529 // for unreachable blocks or expression trees
1531 method = DoCreateMethodHost (ec);
1535 bool is_static = (method.ModFlags & Modifiers.STATIC) != 0;
1536 if (is_static && am_cache == null) {
1538 // Creates a field cache to store delegate instance if it's not generic
1540 if (!method.MemberName.IsGeneric) {
1541 TypeContainer parent = method.Parent.PartialContainer;
1542 int id = parent.Fields == null ? 0 : parent.Fields.Count;
1543 var cache_type = storey != null && storey.Mutator != null ? storey.Mutator.Mutate (type) : type;
1545 am_cache = new Field (parent, new TypeExpression (cache_type, loc),
1546 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
1547 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "am$cache", id), loc), null);
1549 parent.AddField (am_cache);
1551 // TODO: Implement caching of generated generic static methods
1555 // Some extra class is needed to capture variable generic type
1556 // arguments. Maybe we could re-use anonymous types, with a unique
1557 // anonymous method id, but they are quite heavy.
1559 // Consider : "() => typeof(T);"
1561 // We need something like
1562 // static class Wrap<Tn, Tm, DelegateType> {
1563 // public static DelegateType cache;
1566 // We then specialize local variable to capture all generic parameters
1567 // and delegate type, e.g. "Wrap<Ta, Tb, DelegateTypeInst> cache;"
1572 Label l_initialized = ec.DefineLabel ();
1574 if (am_cache != null) {
1575 ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
1576 ec.Emit (OpCodes.Brtrue_S, l_initialized);
1580 // Load method delegate implementation
1584 ec.Emit (OpCodes.Ldnull);
1585 } else if (storey != null) {
1586 Expression e = storey.GetStoreyInstanceExpression (ec).Resolve (new ResolveContext (ec.MemberContext));
1590 ec.Emit (OpCodes.Ldarg_0);
1593 var delegate_method = method.Spec;
1594 if (storey != null && storey.MemberName.IsGeneric) {
1595 TypeSpec t = storey.Instance.Type;
1598 // Mutate anonymous method instance type if we are in nested
1599 // hoisted generic anonymous method storey
1601 if (ec.CurrentAnonymousMethod != null &&
1602 ec.CurrentAnonymousMethod.Storey != null &&
1603 ec.CurrentAnonymousMethod.Storey.Mutator != null) {
1604 t = storey.Mutator.Mutate (t);
1607 ec.Emit (OpCodes.Ldftn, TypeBuilder.GetMethod (t.GetMetaInfo (), (MethodInfo) delegate_method.GetMetaInfo ()));
1609 if (delegate_method.IsGeneric)
1610 delegate_method = delegate_method.MakeGenericMethod (ec.MemberContext, method.TypeParameters);
1612 ec.Emit (OpCodes.Ldftn, delegate_method);
1615 var constructor_method = Delegate.GetConstructor (type);
1616 ec.Emit (OpCodes.Newobj, constructor_method);
1618 if (am_cache != null) {
1619 ec.Emit (OpCodes.Stsfld, am_cache.Spec);
1620 ec.MarkLabel (l_initialized);
1621 ec.Emit (OpCodes.Ldsfld, am_cache.Spec);
1626 // Look for the best storey for this anonymous method
1628 AnonymousMethodStorey FindBestMethodStorey ()
1631 // Use the nearest parent block which has a storey
1633 for (Block b = Block.Parent; b != null; b = b.Parent) {
1634 AnonymousMethodStorey s = b.Explicit.AnonymousMethodStorey;
1642 public override string GetSignatureForError ()
1644 return TypeManager.CSharpName (type);
1647 public static void Reset ()
1654 // Anonymous type container
1656 public class AnonymousTypeClass : CompilerGeneratedClass
1658 // TODO: Merge with AnonymousTypeParameter
1659 public class GeneratedParameter : Parameter
1661 public GeneratedParameter (FullNamedExpression type, AnonymousTypeParameter p)
1662 : base (type, p.Name, Modifier.NONE, null, p.Location)
1667 static int types_counter;
1668 public const string ClassNamePrefix = "<>__AnonType";
1669 public const string SignatureForError = "anonymous type";
1671 readonly IList<AnonymousTypeParameter> parameters;
1673 private AnonymousTypeClass (TypeContainer parent, MemberName name, IList<AnonymousTypeParameter> parameters, Location loc)
1674 : base (parent, name, (parent.Module.Evaluator != null ? Modifiers.PUBLIC : 0) | Modifiers.SEALED)
1676 this.parameters = parameters;
1679 public static AnonymousTypeClass Create (TypeContainer parent, IList<AnonymousTypeParameter> parameters, Location loc)
1681 string name = ClassNamePrefix + types_counter++;
1683 ParametersCompiled all_parameters;
1684 TypeParameterName[] t_params;
1685 SimpleName[] t_args;
1687 if (parameters.Count == 0) {
1688 all_parameters = ParametersCompiled.EmptyReadOnlyParameters;
1689 t_params = new TypeParameterName[0];
1692 t_args = new SimpleName[parameters.Count];
1693 t_params = new TypeParameterName[parameters.Count];
1694 Parameter[] ctor_params = new Parameter[parameters.Count];
1695 for (int i = 0; i < parameters.Count; ++i) {
1696 AnonymousTypeParameter p = parameters[i];
1698 t_args[i] = new SimpleName ("<" + p.Name + ">__T", p.Location);
1699 t_params[i] = new TypeParameterName (t_args[i].Name, null, p.Location);
1700 ctor_params[i] = new GeneratedParameter (t_args[i], p);
1703 all_parameters = new ParametersCompiled (ctor_params);
1707 // Create generic anonymous type host with generic arguments
1708 // named upon properties names
1710 AnonymousTypeClass a_type = new AnonymousTypeClass (parent.NamespaceEntry.SlaveDeclSpace,
1711 new MemberName (name, new TypeArguments (t_params), loc), parameters, loc);
1713 if (parameters.Count > 0)
1714 a_type.SetParameterInfo (null);
1716 Constructor c = new Constructor (a_type, name, Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
1717 null, all_parameters, null, loc);
1718 c.Block = new ToplevelBlock (parent.Module.Compiler, c.ParameterInfo, loc);
1721 // Create fields and contructor body with field initialization
1724 for (int i = 0; i < parameters.Count; ++i) {
1725 AnonymousTypeParameter p = parameters [i];
1727 Field f = new Field (a_type, t_args [i], Modifiers.PRIVATE | Modifiers.READONLY,
1728 new MemberName ("<" + p.Name + ">", p.Location), null);
1730 if (!a_type.AddField (f)) {
1735 c.Block.AddStatement (new StatementExpression (
1736 new SimpleAssign (new MemberAccess (new This (p.Location), f.Name),
1737 c.Block.GetParameterReference (i, p.Location))));
1739 ToplevelBlock get_block = new ToplevelBlock (parent.Module.Compiler, p.Location);
1740 get_block.AddStatement (new Return (
1741 new MemberAccess (new This (p.Location), f.Name), p.Location));
1743 Property prop = new Property (a_type, t_args [i], Modifiers.PUBLIC,
1744 new MemberName (p.Name, p.Location), null);
1745 prop.Get = new Property.GetMethod (prop, 0, null, p.Location);
1746 prop.Get.Block = get_block;
1747 a_type.AddProperty (prop);
1753 a_type.AddConstructor (c);
1757 public static void Reset ()
1762 protected override bool AddToContainer (MemberCore symbol, string name)
1764 MemberCore mc = GetDefinition (name);
1767 defined_names.Add (name, symbol);
1771 // A conflict between anonymous type members will be reported
1772 if (symbol is TypeParameter) {
1773 Report.SymbolRelatedToPreviousError (symbol);
1777 // Ignore other conflicts
1781 protected override bool DoDefineMembers ()
1783 if (!base.DoDefineMembers ())
1786 Location loc = Location;
1788 var equals_parameters = ParametersCompiled.CreateFullyResolved (
1789 new Parameter (new TypeExpression (Compiler.BuiltinTypes.Object, loc), "obj", 0, null, loc), Compiler.BuiltinTypes.Object);
1791 Method equals = new Method (this, null, new TypeExpression (Compiler.BuiltinTypes.Bool, loc),
1792 Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("Equals", loc),
1793 equals_parameters, null);
1795 equals_parameters[0].Resolve (equals, 0);
1797 Method tostring = new Method (this, null, new TypeExpression (Compiler.BuiltinTypes.String, loc),
1798 Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc),
1799 Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
1801 ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc);
1803 TypeExpr current_type;
1804 if (type_params != null) {
1805 var targs = new TypeArguments ();
1806 foreach (var type_param in type_params)
1807 targs.Add (new TypeParameterExpr (type_param, type_param.Location));
1809 current_type = new GenericTypeExpr (Definition, targs, loc);
1811 current_type = new TypeExpression (Definition, loc);
1814 var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc);
1815 equals_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_other.Type, loc), li_other));
1816 var other_variable = new LocalVariableReference (li_other, loc);
1818 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
1819 new QualifiedAliasMember ("global", "System", loc), "Collections", loc), "Generic", loc);
1821 Expression rs_equals = null;
1822 Expression string_concat = new StringConstant (Compiler.BuiltinTypes, "{", loc);
1823 Expression rs_hashcode = new IntConstant (Compiler.BuiltinTypes, -2128831035, loc);
1824 for (int i = 0; i < parameters.Count; ++i) {
1825 var p = parameters [i];
1828 MemberAccess equality_comparer = new MemberAccess (new MemberAccess (
1829 system_collections_generic, "EqualityComparer",
1830 new TypeArguments (new SimpleName (CurrentTypeParameters [i].Name, loc)), loc),
1833 Arguments arguments_equal = new Arguments (2);
1834 arguments_equal.Add (new Argument (new MemberAccess (new This (f.Location), f.Name)));
1835 arguments_equal.Add (new Argument (new MemberAccess (other_variable, f.Name)));
1837 Expression field_equal = new Invocation (new MemberAccess (equality_comparer,
1838 "Equals", loc), arguments_equal);
1840 Arguments arguments_hashcode = new Arguments (1);
1841 arguments_hashcode.Add (new Argument (new MemberAccess (new This (f.Location), f.Name)));
1842 Expression field_hashcode = new Invocation (new MemberAccess (equality_comparer,
1843 "GetHashCode", loc), arguments_hashcode);
1845 IntConstant FNV_prime = new IntConstant (Compiler.BuiltinTypes, 16777619, loc);
1846 rs_hashcode = new Binary (Binary.Operator.Multiply,
1847 new Binary (Binary.Operator.ExclusiveOr, rs_hashcode, field_hashcode, loc),
1850 Expression field_to_string = new Conditional (new BooleanExpression (new Binary (Binary.Operator.Inequality,
1851 new MemberAccess (new This (f.Location), f.Name), new NullLiteral (loc), loc)),
1852 new Invocation (new MemberAccess (
1853 new MemberAccess (new This (f.Location), f.Name), "ToString"), null),
1854 new StringConstant (Compiler.BuiltinTypes, string.Empty, loc), loc);
1856 if (rs_equals == null) {
1857 rs_equals = field_equal;
1858 string_concat = new Binary (Binary.Operator.Addition,
1860 new Binary (Binary.Operator.Addition,
1861 new StringConstant (Compiler.BuiltinTypes, " " + p.Name + " = ", loc),
1869 // Implementation of ToString () body using string concatenation
1871 string_concat = new Binary (Binary.Operator.Addition,
1872 new Binary (Binary.Operator.Addition,
1874 new StringConstant (Compiler.BuiltinTypes, ", " + p.Name + " = ", loc),
1879 rs_equals = new Binary (Binary.Operator.LogicalAnd, rs_equals, field_equal, loc);
1882 string_concat = new Binary (Binary.Operator.Addition,
1884 new StringConstant (Compiler.BuiltinTypes, " }", loc),
1888 // Equals (object obj) override
1890 var other_variable_assign = new TemporaryVariableReference (li_other, loc);
1891 equals_block.AddStatement (new StatementExpression (
1892 new SimpleAssign (other_variable_assign,
1893 new As (equals_block.GetParameterReference (0, loc),
1894 current_type, loc), loc)));
1896 Expression equals_test = new Binary (Binary.Operator.Inequality, other_variable, new NullLiteral (loc), loc);
1897 if (rs_equals != null)
1898 equals_test = new Binary (Binary.Operator.LogicalAnd, equals_test, rs_equals, loc);
1899 equals_block.AddStatement (new Return (equals_test, loc));
1901 equals.Block = equals_block;
1906 // GetHashCode () override
1908 Method hashcode = new Method (this, null, new TypeExpression (Compiler.BuiltinTypes.Int, loc),
1909 Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN,
1910 new MemberName ("GetHashCode", loc),
1911 Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
1914 // Modified FNV with good avalanche behavior and uniform
1915 // distribution with larger hash sizes.
1917 // const int FNV_prime = 16777619;
1918 // int hash = (int) 2166136261;
1919 // foreach (int d in data)
1920 // hash = (hash ^ d) * FNV_prime;
1921 // hash += hash << 13;
1922 // hash ^= hash >> 7;
1923 // hash += hash << 3;
1924 // hash ^= hash >> 17;
1925 // hash += hash << 5;
1927 ToplevelBlock hashcode_top = new ToplevelBlock (Compiler, loc);
1928 Block hashcode_block = new Block (hashcode_top, loc, loc);
1929 hashcode_top.AddStatement (new Unchecked (hashcode_block, loc));
1931 var li_hash = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Int, hashcode_top, loc);
1932 hashcode_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_hash.Type, loc), li_hash));
1933 LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc);
1934 hashcode_block.AddStatement (new StatementExpression (
1935 new SimpleAssign (hash_variable_assign, rs_hashcode)));
1937 var hash_variable = new LocalVariableReference (li_hash, loc);
1938 hashcode_block.AddStatement (new StatementExpression (
1939 new CompoundAssign (Binary.Operator.Addition, hash_variable,
1940 new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 13, loc), loc), loc)));
1941 hashcode_block.AddStatement (new StatementExpression (
1942 new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable,
1943 new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 7, loc), loc), loc)));
1944 hashcode_block.AddStatement (new StatementExpression (
1945 new CompoundAssign (Binary.Operator.Addition, hash_variable,
1946 new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 3, loc), loc), loc)));
1947 hashcode_block.AddStatement (new StatementExpression (
1948 new CompoundAssign (Binary.Operator.ExclusiveOr, hash_variable,
1949 new Binary (Binary.Operator.RightShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 17, loc), loc), loc)));
1950 hashcode_block.AddStatement (new StatementExpression (
1951 new CompoundAssign (Binary.Operator.Addition, hash_variable,
1952 new Binary (Binary.Operator.LeftShift, hash_variable, new IntConstant (Compiler.BuiltinTypes, 5, loc), loc), loc)));
1954 hashcode_block.AddStatement (new Return (hash_variable, loc));
1955 hashcode.Block = hashcode_top;
1957 AddMethod (hashcode);
1960 // ToString () override
1963 ToplevelBlock tostring_block = new ToplevelBlock (Compiler, loc);
1964 tostring_block.AddStatement (new Return (string_concat, loc));
1965 tostring.Block = tostring_block;
1967 AddMethod (tostring);
1972 public override string GetSignatureForError ()
1974 return SignatureForError;
1977 public IList<AnonymousTypeParameter> Parameters {