Better check for instance equality of generic types. Fixes #15100
[mono.git] / mcs / mcs / anonymous.cs
index 0c855c51cbd2b12ff5d40023100beedaa2cee698..ce83e7674c14d88cc1b6c60145409209a83def58 100644 (file)
@@ -191,15 +191,27 @@ namespace Mono.CSharp {
                sealed class ThisInitializer : Statement
                {
                        readonly HoistedThis hoisted_this;
+                       readonly AnonymousMethodStorey parent;
 
-                       public ThisInitializer (HoistedThis hoisted_this)
+                       public ThisInitializer (HoistedThis hoisted_this, AnonymousMethodStorey parent)
                        {
                                this.hoisted_this = hoisted_this;
+                               this.parent = parent;
                        }
 
                        protected override void DoEmit (EmitContext ec)
                        {
-                               hoisted_this.EmitAssign (ec, new CompilerGeneratedThis (ec.CurrentType, loc), false, false);
+                               Expression source;
+
+                               if (parent == null)
+                                       source = new CompilerGeneratedThis (ec.CurrentType, loc);
+                               else {
+                                       source = new FieldExpr (parent.HoistedThis.Field, Location.Null) {
+                                               InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location.Null)
+                                       };
+                               }
+
+                               hoisted_this.EmitAssign (ec, source, false, false);
                        }
 
                        protected override void CloneTo (CloneContext clonectx, Statement target)
@@ -229,6 +241,7 @@ namespace Mono.CSharp {
                public Expression Instance;
 
                bool initialize_hoisted_this;
+               AnonymousMethodStorey hoisted_this_parent;
 
                public AnonymousMethodStorey (ExplicitBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind)
                        : base (parent, MakeMemberName (host, name, parent.Module.CounterAnonymousContainers, tparams, block.StartLocation),
@@ -238,13 +251,14 @@ namespace Mono.CSharp {
                        ID = parent.Module.CounterAnonymousContainers++;
                }
 
-               public void AddCapturedThisField (EmitContext ec)
+               public void AddCapturedThisField (EmitContext ec, AnonymousMethodStorey parent)
                {
                        TypeExpr type_expr = new TypeExpression (ec.CurrentType, Location);
                        Field f = AddCompilerGeneratedField ("$this", type_expr);
                        hoisted_this = new HoistedThis (this, f);
 
                        initialize_hoisted_this = true;
+                       hoisted_this_parent = parent;
                }
 
                public Field AddCapturedVariable (string name, TypeSpec type)
@@ -315,8 +329,11 @@ namespace Mono.CSharp {
 
                        var hoisted = localVariable.HoistedVariant;
                        if (hoisted != null && hoisted.Storey != this && hoisted.Storey is StateMachine) {
-                               // TODO: It's too late the field is defined in HoistedLocalVariable ctor
+                               //
+                               // Variable is already hoisted but we need it in storey which can be shared
+                               //
                                hoisted.Storey.hoisted_locals.Remove (hoisted);
+                               hoisted.Storey.Members.Remove (hoisted.Field);
                                hoisted = null;
                        }
 
@@ -550,7 +567,7 @@ namespace Mono.CSharp {
                        // referenced indirectly
                        //
                        if (initialize_hoisted_this) {
-                               rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this));
+                               rc.CurrentBlock.AddScopeStatement (new ThisInitializer (hoisted_this, hoisted_this_parent));
                        }
 
                        //
@@ -569,6 +586,9 @@ namespace Mono.CSharp {
                protected virtual void EmitHoistedParameters (EmitContext ec, List<HoistedParameter> hoisted)
                {
                        foreach (HoistedParameter hp in hoisted) {
+                               if (hp == null)
+                                       continue;
+
                                //
                                // Parameters could be proxied via local fields for value type storey
                                //
@@ -725,6 +745,12 @@ namespace Mono.CSharp {
                        this.field = field;
                }
 
+               public Field Field {
+                       get {
+                               return field;
+                       }
+               }
+
                public AnonymousMethodStorey Storey {
                        get {
                                return storey;
@@ -845,11 +871,7 @@ namespace Mono.CSharp {
 
                #region Properties
 
-               public Field Field {
-                       get {
-                               return field;
-                       }
-               }
+               public bool IsAssigned { get; set; }
 
                public ParameterReference Parameter {
                        get {
@@ -888,12 +910,6 @@ namespace Mono.CSharp {
                        : base (storey, field)
                {
                }
-
-               public Field Field {
-                       get {
-                               return field;
-                       }
-               }
        }
 
        //
@@ -954,6 +970,12 @@ namespace Mono.CSharp {
                        }
                }
 
+               public override bool IsSideEffectFree {
+                       get {
+                               return true;
+                       }
+               }
+
                public ParametersCompiled Parameters {
                        get {
                                return Block.Parameters;
@@ -996,36 +1018,36 @@ namespace Mono.CSharp {
                                        return delegate_type;
 
                                ec.Report.Error (835, loc, "Cannot convert `{0}' to an expression tree of non-delegate type `{1}'",
-                                       GetSignatureForError (), TypeManager.CSharpName (delegate_type));
+                                       GetSignatureForError (), delegate_type.GetSignatureForError ());
                                return null;
                        }
 
                        ec.Report.Error (1660, loc, "Cannot convert `{0}' to non-delegate type `{1}'",
-                                     GetSignatureForError (), TypeManager.CSharpName (delegate_type));
+                                     GetSignatureForError (), delegate_type.GetSignatureForError ());
                        return null;
                }
 
-               protected bool VerifyExplicitParameters (ResolveContext ec, TypeSpec delegate_type, AParametersCollection parameters)
+               protected bool VerifyExplicitParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection parameters)
                {
-                       if (VerifyParameterCompatibility (ec, delegate_type, parameters, ec.IsInProbingMode))
+                       if (VerifyParameterCompatibility (ec, tic, delegate_type, parameters, ec.IsInProbingMode))
                                return true;
 
                        if (!ec.IsInProbingMode)
                                ec.Report.Error (1661, loc,
                                        "Cannot convert `{0}' to delegate type `{1}' since there is a parameter mismatch",
-                                       GetSignatureForError (), TypeManager.CSharpName (delegate_type));
+                                       GetSignatureForError (), delegate_type.GetSignatureForError ());
 
                        return false;
                }
 
-               protected bool VerifyParameterCompatibility (ResolveContext ec, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
+               protected bool VerifyParameterCompatibility (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegate_type, AParametersCollection invoke_pd, bool ignore_errors)
                {
                        if (Parameters.Count != invoke_pd.Count) {
                                if (ignore_errors)
                                        return false;
                                
                                ec.Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
-                                             TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
+                                             delegate_type.GetSignatureForError (), Parameters.Count.ToString ());
                                return false;
                        }
 
@@ -1039,10 +1061,10 @@ namespace Mono.CSharp {
                                                return false;
                                        
                                        if (p_mod == Parameter.Modifier.NONE)
-                                               ec.Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
-                                                             (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.FixedParameters [i].ModFlags));
+                                               ec.Report.Error (1677, Parameters[i].Location, "Parameter `{0}' should not be declared with the `{1}' keyword",
+                                                             (i + 1).ToString (), Parameter.GetModifierSignature (Parameters [i].ModFlags));
                                        else
-                                               ec.Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
+                                               ec.Report.Error (1676, Parameters[i].Location, "Parameter `{0}' must be declared with the `{1}' keyword",
                                                              (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
                                        error = true;
                                }
@@ -1051,22 +1073,18 @@ namespace Mono.CSharp {
                                        continue;
 
                                TypeSpec type = invoke_pd.Types [i];
+
+                               if (tic != null)
+                                       type = tic.InflateGenericArgument (ec, type);
                                
-                               // We assume that generic parameters are always inflated
-                               if (TypeManager.IsGenericParameter (type))
-                                       continue;
-                               
-                               if (TypeManager.HasElementType (type) && TypeManager.IsGenericParameter (TypeManager.GetElementType (type)))
-                                       continue;
-                               
-                               if (!TypeSpecComparer.IsEqual (invoke_pd.Types [i], Parameters.Types [i])) {
+                               if (!TypeSpecComparer.IsEqual (type, Parameters.Types [i])) {
                                        if (ignore_errors)
                                                return false;
                                        
-                                       ec.Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
+                                       ec.Report.Error (1678, Parameters [i].Location, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
                                                      (i+1).ToString (),
-                                                     TypeManager.CSharpName (Parameters.Types [i]),
-                                                     TypeManager.CSharpName (invoke_pd.Types [i]));
+                                                     Parameters.Types [i].GetSignatureForError (),
+                                                     invoke_pd.Types [i].GetSignatureForError ());
                                        error = true;
                                }
                        }
@@ -1077,7 +1095,7 @@ namespace Mono.CSharp {
                //
                // Infers type arguments based on explicit arguments
                //
-               public bool ExplicitTypeInference (ResolveContext ec, TypeInferenceContext type_inference, TypeSpec delegate_type)
+               public bool ExplicitTypeInference (TypeInferenceContext type_inference, TypeSpec delegate_type)
                {
                        if (!HasExplicitParameters)
                                return false;
@@ -1224,6 +1242,17 @@ namespace Mono.CSharp {
                                        }
                                } else {
                                        am = body.Compatible (ec);
+
+                                       if (body.DirectMethodGroupConversion != null) {
+                                               var errors_printer = new SessionReportPrinter ();
+                                               var old = ec.Report.SetPrinter (errors_printer);
+                                               var expr = new ImplicitDelegateCreation (delegate_type, body.DirectMethodGroupConversion, loc) {
+                                                       AllowSpecialMethodsInvocation = true
+                                               }.Resolve (ec);
+                                               ec.Report.SetPrinter (old);
+                                               if (expr != null && errors_printer.ErrorsCount == 0)
+                                                       am = expr;
+                                       }
                                }
                        } catch (CompletionResult) {
                                throw;
@@ -1280,7 +1309,7 @@ namespace Mono.CSharp {
                                return ParametersCompiled.CreateFullyResolved (fixedpars, delegate_parameters.Types);
                        }
 
-                       if (!VerifyExplicitParameters (ec, delegate_type, delegate_parameters)) {
+                       if (!VerifyExplicitParameters (ec, tic, delegate_type, delegate_parameters)) {
                                return null;
                        }
 
@@ -1359,7 +1388,7 @@ namespace Mono.CSharp {
                                        return null;
                                }
 
-                               b = b.ConvertToAsyncTask (ec, ec.CurrentMemberDefinition.Parent.PartialContainer, p, return_type, loc);
+                               b = b.ConvertToAsyncTask (ec, ec.CurrentMemberDefinition.Parent.PartialContainer, p, return_type, delegate_type, loc);
                        }
 
                        return CompatibleMethodFactory (return_type ?? InternalType.ErrorType, delegate_type, p, b);
@@ -1562,6 +1591,14 @@ namespace Mono.CSharp {
                        get { return "anonymous method"; }
                }
 
+               //
+               // Method-group instance for lambdas which can be replaced with
+               // simple method group call
+               //
+               public MethodGroupExpr DirectMethodGroupConversion {
+                       get; set;
+               }
+
                public override bool IsIterator {
                        get {
                                return false;
@@ -1584,7 +1621,9 @@ namespace Mono.CSharp {
                }
 
                public override AnonymousMethodStorey Storey {
-                       get { return storey; }
+                       get {
+                               return storey;
+                       }
                }
 
                #endregion
@@ -1635,26 +1674,32 @@ namespace Mono.CSharp {
                                        if (src_block.HasCapturedThis) {
                                                //
                                                // Remove hoisted 'this' request when simple instance method is
-                                               // enough (no hoisted variables only 'this')
+                                               // enough. No hoisted variables only 'this' and don't need to
+                                               // propagate this to value type state machine.
                                                //
-                                               if (src_block.ParametersBlock.StateMachine == null)
+                                               StateMachine sm_parent;
+                                               var pb = src_block.ParametersBlock;
+                                               do {
+                                                       sm_parent = pb.StateMachine;
+                                                       pb = pb.Parent == null ? null : pb.Parent.ParametersBlock;
+                                               } while (sm_parent == null && pb != null);
+
+                                               if (sm_parent == null) {
                                                        top_block.RemoveThisReferenceFromChildrenBlock (src_block);
-
-                                               //
-                                               // Special case where parent class is used to emit instance method
-                                               // because currect storey is of value type (async host). We cannot
-                                               // use ldftn on non-boxed instances either to share mutated state
-                                               //
-                                               if (sm != null && sm.Kind == MemberKind.Struct) {
-                                                       parent = sm.Parent.PartialContainer;
+                                               } else if (sm_parent.Kind == MemberKind.Struct) {
+                                                       //
+                                                       // Special case where parent class is used to emit instance method
+                                                       // because currect storey is of value type (async host) and we cannot
+                                                       // use ldftn on non-boxed instances either to share mutated state
+                                                       //
+                                                       parent = sm_parent.Parent.PartialContainer;
+                                               } else if (sm is IteratorStorey) {
+                                                       //
+                                                       // For iterators we can host everything in one class
+                                                       //
+                                                       parent = storey = sm;
                                                }
                                        }
-
-                                       //
-                                       // For iterators we can host everything in one class
-                                       //
-                                       if (sm is IteratorStorey)
-                                               parent = storey = sm;
                                }
 
                                modifiers = storey != null ? Modifiers.INTERNAL : Modifiers.PRIVATE;
@@ -1712,6 +1757,7 @@ namespace Mono.CSharp {
                                //
                                method = DoCreateMethodHost (ec);
                                method.Define ();
+                               method.PrepareEmit ();
                        }
 
                        bool is_static = (method.ModFlags & Modifiers.STATIC) != 0;
@@ -1776,12 +1822,8 @@ namespace Mono.CSharp {
                                // Special case for value type storey where this is not lifted but
                                // droped off to parent class
                                //
-                               for (var b = Block.Parent; b != null; b = b.Parent) {
-                                       if (b.ParametersBlock.StateMachine != null) {
-                                               ec.Emit (OpCodes.Ldfld, b.ParametersBlock.StateMachine.HoistedThis.Field.Spec);
-                                               break;
-                                       }
-                               }
+                               if (ec.CurrentAnonymousMethod != null && ec.AsyncTaskStorey != null)
+                                       ec.Emit (OpCodes.Ldfld, ec.AsyncTaskStorey.HoistedThis.Field.Spec);
                        }
 
                        var delegate_method = method.Spec;
@@ -1838,7 +1880,7 @@ namespace Mono.CSharp {
 
                public override string GetSignatureForError ()
                {
-                       return TypeManager.CSharpName (type);
+                       return type.GetSignatureForError ();
                }
        }
 
@@ -1960,7 +2002,7 @@ namespace Mono.CSharp {
 
                        Method tostring = new Method (this, new TypeExpression (Compiler.BuiltinTypes.String, loc),
                                Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN, new MemberName ("ToString", loc),
-                               Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
+                               ParametersCompiled.EmptyReadOnlyParameters, null);
 
                        ToplevelBlock equals_block = new ToplevelBlock (Compiler, equals.ParameterInfo, loc);
 
@@ -1977,7 +2019,7 @@ namespace Mono.CSharp {
                        }
 
                        var li_other = LocalVariable.CreateCompilerGenerated (CurrentType, equals_block, loc);
-                       equals_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_other.Type, loc), li_other));
+                       equals_block.AddStatement (new BlockVariable (new TypeExpression (li_other.Type, loc), li_other));
                        var other_variable = new LocalVariableReference (li_other, loc);
 
                        MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
@@ -2060,6 +2102,7 @@ namespace Mono.CSharp {
 
                        equals.Block = equals_block;
                        equals.Define ();
+                       equals.PrepareEmit ();
                        Members.Add (equals);
 
                        //
@@ -2068,7 +2111,7 @@ namespace Mono.CSharp {
                        Method hashcode = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Int, loc),
                                Modifiers.PUBLIC | Modifiers.OVERRIDE | Modifiers.DEBUGGER_HIDDEN,
                                new MemberName ("GetHashCode", loc),
-                               Mono.CSharp.ParametersCompiled.EmptyReadOnlyParameters, null);
+                               ParametersCompiled.EmptyReadOnlyParameters, null);
 
                        //
                        // Modified FNV with good avalanche behavior and uniform
@@ -2089,7 +2132,7 @@ namespace Mono.CSharp {
                        hashcode_top.AddStatement (new Unchecked (hashcode_block, loc));
 
                        var li_hash = LocalVariable.CreateCompilerGenerated (Compiler.BuiltinTypes.Int, hashcode_top, loc);
-                       hashcode_block.AddStatement (new BlockVariableDeclaration (new TypeExpression (li_hash.Type, loc), li_hash));
+                       hashcode_block.AddStatement (new BlockVariable (new TypeExpression (li_hash.Type, loc), li_hash));
                        LocalVariableReference hash_variable_assign = new LocalVariableReference (li_hash, loc);
                        hashcode_block.AddStatement (new StatementExpression (
                                new SimpleAssign (hash_variable_assign, rs_hashcode)));
@@ -2114,6 +2157,7 @@ namespace Mono.CSharp {
                        hashcode_block.AddStatement (new Return (hash_variable, loc));
                        hashcode.Block = hashcode_top;
                        hashcode.Define ();
+                       hashcode.PrepareEmit ();
                        Members.Add (hashcode);
 
                        //
@@ -2124,6 +2168,7 @@ namespace Mono.CSharp {
                        tostring_block.AddStatement (new Return (string_concat, loc));
                        tostring.Block = tostring_block;
                        tostring.Define ();
+                       tostring.PrepareEmit ();
                        Members.Add (tostring);
 
                        return true;