Avoid creating a temporary variable when target await expression is does not include...
[mono.git] / mcs / mcs / ecore.cs
index b45b1925261ab2941be6ffd10c2c3012af91ed6b..b61fc91782f0880fad06edc990d10bb8eeedfe25 100644 (file)
@@ -519,22 +519,33 @@ namespace Mono.CSharp {
                        if (IsSideEffectFree)
                                return this;
 
+                       bool needs_temporary = ContainsEmitWithAwait ();
+                       if (!needs_temporary)
+                               ec.EmitThis ();
+
                        // Emit original code
                        EmitToFieldSource (ec);
 
-                       // Create temporary local (we cannot load this before Emit)
-                       var temp = ec.GetTemporaryLocal (type);
-                       ec.Emit (OpCodes.Stloc, temp, type);
-
                        //
-                       // Store the result to temporary field
+                       // Store the result to temporary field when we
+                       // cannot load this directly
                        //
                        var field = ec.GetTemporaryField (type);
-                       ec.EmitThis ();
-                       ec.Emit (OpCodes.Ldloc, temp, type);
-                       field.EmitAssignFromStack (ec);
+                       if (needs_temporary) {
+                               //
+                               // Create temporary local (we cannot load this before Emit)
+                               //
+                               var temp = ec.GetTemporaryLocal (type);
+                               ec.Emit (OpCodes.Stloc, temp);
+
+                               ec.EmitThis ();
+                               ec.Emit (OpCodes.Ldloc, temp);
+                               field.EmitAssignFromStack (ec);
 
-                       ec.FreeTemporaryLocal (temp, type);
+                               ec.FreeTemporaryLocal (temp, type);
+                       } else {
+                               field.EmitAssignFromStack (ec);
+                       }
 
                        return field;
                }
@@ -600,7 +611,7 @@ namespace Mono.CSharp {
                        return null;
                }
 
-               protected static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc)
+               public static MethodSpec ConstructorLookup (ResolveContext rc, TypeSpec type, ref Arguments args, Location loc)
                {
                        var ctors = MemberCache.FindMembers (type, Constructor.ConstructorName, true);
                        if (ctors == null) {
@@ -1862,6 +1873,11 @@ namespace Mono.CSharp {
                                this.loc = orig.Location;
                        }
 
+                       public override bool ContainsEmitWithAwait ()
+                       {
+                               return stm.ContainsEmitWithAwait ();
+                       }
+
                        public override Expression CreateExpressionTree (ResolveContext ec)
                        {
                                return orig_expr.CreateExpressionTree (ec);
@@ -1904,6 +1920,11 @@ namespace Mono.CSharp {
 
                #endregion
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return expr.ContainsEmitWithAwait ();
+               }
+
                //
                // Creates fully resolved expression switcher
                //
@@ -1987,6 +2008,11 @@ namespace Mono.CSharp {
                        this.loc = expr.Location;
                }
 
+               public override bool ContainsEmitWithAwait ()
+               {
+                       return expr.ContainsEmitWithAwait ();
+               }
+
                public override Expression CreateExpressionTree (ResolveContext rc)
                {
                        return expr.CreateExpressionTree (rc);
@@ -2932,6 +2958,7 @@ namespace Mono.CSharp {
                                        InstanceExpression.Emit (ec);
                                        t.Store (ec);
                                        t.AddressOf (ec, AddressOp.Store);
+                                       t.Release (ec);
                                }
                        } else {
                                InstanceExpression.Emit (ec);
@@ -2948,18 +2975,57 @@ namespace Mono.CSharp {
                public abstract void SetTypeArguments (ResolveContext ec, TypeArguments ta);
        }
 
+       public class ExtensionMethodCandidates
+       {
+               NamespaceContainer container;
+               Namespace ns;
+               IList<MethodSpec> methods;
+
+               public ExtensionMethodCandidates (IList<MethodSpec> methods, NamespaceContainer nsContainer)
+                       : this (methods, nsContainer, null)
+               {
+               }
+
+               public ExtensionMethodCandidates (IList<MethodSpec> methods, NamespaceContainer nsContainer, Namespace ns)
+               {
+                       this.methods = methods;
+                       this.container = nsContainer;
+                       this.ns = ns;
+               }
+
+               public NamespaceContainer Container {
+                       get {
+                               return container;
+                       }
+               }
+
+               public bool HasUninspectedMembers { get; set; }
+
+               public Namespace Namespace {
+                       get {
+                               return ns;
+                       }
+               }
+
+               public IList<MethodSpec> Methods {
+                       get {
+                               return methods;
+                       }
+               }
+       }
+
        // 
        // Represents a group of extension method candidates for whole namespace
        // 
        class ExtensionMethodGroupExpr : MethodGroupExpr, OverloadResolver.IErrorHandler
        {
-               NamespaceContainer namespace_entry;
+               ExtensionMethodCandidates candidates;
                public readonly Expression ExtensionExpression;
 
-               public ExtensionMethodGroupExpr (IList<MethodSpec> list, NamespaceContainer n, Expression extensionExpr, Location l)
-                       : base (list.Cast<MemberSpec>().ToList (), extensionExpr.Type, l)
+               public ExtensionMethodGroupExpr (ExtensionMethodCandidates candidates, Expression extensionExpr, Location loc)
+                       : base (candidates.Methods.Cast<MemberSpec>().ToList (), extensionExpr.Type, loc)
                {
-                       this.namespace_entry = n;
+                       this.candidates = candidates;
                        this.ExtensionExpression = extensionExpr;
                }
 
@@ -2967,21 +3033,55 @@ namespace Mono.CSharp {
                        get { return true; }
                }
 
+               //
+               // For extension methodgroup we are not looking for base members but parent
+               // namespace extension methods
+               //
                public override IList<MemberSpec> GetBaseMembers (TypeSpec baseType)
                {
-                       if (namespace_entry == null)
+                       // TODO: candidates are null only when doing error reporting, that's
+                       // incorrect. We have to discover same extension methods in error mode
+                       if (candidates == null)
                                return null;
 
+                       int arity = type_arguments == null ? 0 : type_arguments.Count;
+
                        //
-                       // For extension methodgroup we are not looking for base members but parent
-                       // namespace extension methods
+                       // Here we try to resume the search for extension method at the point
+                       // where the last bunch of candidates was found. It's more tricky than
+                       // it seems as we have to check both namespace containers and namespace
+                       // in correct order.
                        //
-                       int arity = type_arguments == null ? 0 : type_arguments.Count;
-                       var found = namespace_entry.LookupExtensionMethod (DeclaringType, Name, arity, ref namespace_entry);
-                       if (found == null)
+                       // Consider:
+                       // 
+                       // namespace A {
+                       //      using N1;
+                       //  namespace B.C.D {
+                       //              <our first search found candidates in A.B.C.D
+                       //  }
+                       // }
+                       //
+                       // In the example above namespace A.B.C.D, A.B.C and A.B have to be
+                       // checked before we hit A.N1 using
+                       //
+                       if (candidates.Namespace == null) {
+                               Namespace scope;
+                               var methods = candidates.Container.NS.LookupExtensionMethod (candidates.Container, ExtensionExpression.Type, Name, arity, out scope);
+                               if (methods != null) {
+                                       candidates = new ExtensionMethodCandidates (null, candidates.Container, scope);
+                                       return methods.Cast<MemberSpec> ().ToList ();
+                               }
+                       }
+
+                       var ns_container = candidates.HasUninspectedMembers ? candidates.Container : candidates.Container.Parent;
+                       if (ns_container == null)
+                               return null;
+
+                       candidates = ns_container.LookupExtensionMethod (ExtensionExpression.Type, Name, arity);
+                       if (candidates == null)
                                return null;
 
-                       return found.Cast<MemberSpec> ().ToList ();
+                       return candidates.Methods.Cast<MemberSpec> ().ToList ();
                }
 
                public override MethodGroupExpr LookupExtensionMethod (ResolveContext rc)
@@ -3317,12 +3417,11 @@ namespace Mono.CSharp {
                                return null;
 
                        int arity = type_arguments == null ? 0 : type_arguments.Count;
-                       NamespaceContainer methods_scope = null;
-                       var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity, ref methods_scope);
+                       var methods = rc.LookupExtensionMethod (InstanceExpression.Type, Methods[0].Name, arity);
                        if (methods == null)
                                return null;
 
-                       var emg = new ExtensionMethodGroupExpr (methods, methods_scope, InstanceExpression, loc);
+                       var emg = new ExtensionMethodGroupExpr (methods, InstanceExpression, loc);
                        emg.SetTypeArguments (rc, type_arguments);
                        return emg;
                }
@@ -5050,7 +5149,7 @@ namespace Mono.CSharp {
                                } else if (var != null && var.IsHoisted) {
                                        AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, var, loc);
                                }
-                               
+
                                return new FixedBufferPtr (this, fb.ElementType, loc).Resolve (ec);
                        }
 
@@ -5231,13 +5330,22 @@ namespace Mono.CSharp {
 
                public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
                {
-                       if (ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait ()) {
-                               source = source.EmitToField (ec);
+                       bool has_await_source = ec.HasSet (BuilderContext.Options.AsyncBody) && source.ContainsEmitWithAwait ();
+                       if (isCompound && !(source is DynamicExpressionStatement)) {
+                               if (has_await_source) {
+                                       if (IsInstance)
+                                               InstanceExpression = InstanceExpression.EmitToField (ec);
+                               } else {
+                                       prepared = true;
+                               }
                        }
 
-                       prepared = isCompound && !(source is DynamicExpressionStatement);
-                       if (IsInstance)
+                       if (IsInstance) {
+                               if (has_await_source)
+                                       source = source.EmitToField (ec);
+
                                EmitInstance (ec, prepared);
+                       }
 
                        source.Emit (ec);
 
@@ -5318,8 +5426,8 @@ namespace Mono.CSharp {
                        if (need_copy) {
                                Emit (ec);
                                var temp = ec.GetTemporaryLocal (type);
-                               ec.Emit (OpCodes.Stloc, temp, type);
-                               ec.Emit (OpCodes.Ldloca, temp, type);
+                               ec.Emit (OpCodes.Stloc, temp);
+                               ec.Emit (OpCodes.Ldloca, temp);
                                ec.FreeTemporaryLocal (temp, type);
                                return;
                        }