By : Yonik <yonik@mainsoft.com>
[mono.git] / mcs / mcs / lambda.cs
1 //
2 // lambda.cs: support for lambda expressions
3 //
4 // Authors: Miguel de Icaza (miguel@gnu.org)
5 //
6 // Licensed under the terms of the GNU GPL
7 //
8 // (C) 2007 Novell, Inc
9 //
10
11 using System;
12 using System.Collections;
13 using System.Reflection;
14 using System.Reflection.Emit;
15
16 namespace Mono.CSharp {
17         public class LambdaExpression : AnonymousMethodExpression {
18                 bool explicit_parameters;
19
20                 //
21                 // The parameters can either be:
22                 //    A list of Parameters (explicitly typed parameters)
23                 //    An ImplicitLambdaParameter
24                 //
25                 public LambdaExpression (AnonymousMethodExpression parent,
26                                          GenericMethod generic, TypeContainer host,
27                                          Parameters parameters, Block container,
28                                          Location loc)
29                         : base (parent, generic, host, parameters, container, loc)
30                 {
31                         explicit_parameters = parameters.FixedParameters [0].TypeName != null;
32                 }
33
34                 public bool HasExplicitParameters {
35                         get {
36                                 return explicit_parameters;
37                         }
38                 }
39                 
40                 public override Expression DoResolve (EmitContext ec)
41                 {
42                         //
43                         // Only explicit parameters can be resolved at this point
44                         //
45                         if (explicit_parameters) {
46                                 if (!Parameters.Resolve (ec))
47                                         return null;
48                         }
49
50                         eclass = ExprClass.Value;
51                         type = TypeManager.anonymous_method_type;                                               
52                         return this;
53                 }
54
55                 public override bool ImplicitStandardConversionExists (Type delegate_type)
56                 {
57                         EmitContext ec = EmitContext.TempEc;
58
59                         bool result;
60
61                         try {
62                                 Report.DisableErrors ();
63                                 result = DoCompatibleTest (ec, delegate_type, true) != null;
64                         } finally {
65                                 Report.EnableErrors ();
66                         }
67                         
68                         // Ignore the result
69                         anonymous = null;
70
71                         return result;
72                 }
73                 
74                 //
75                 // Returns true if this anonymous method can be implicitly
76                 // converted to the delegate type `delegate_type'
77                 //
78                 public override Expression Compatible (EmitContext ec, Type delegate_type)
79                 {
80                         return DoCompatibleTest (ec, delegate_type, false);
81                 }
82
83                 Expression DoCompatibleTest (EmitContext ec, Type delegate_type, bool clone)
84                 {
85                         if (anonymous != null)
86                                 return anonymous.AnonymousDelegate;
87
88                         if (CompatibleChecks (ec, delegate_type) == null)
89                                 return null;
90
91                         MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (
92                                 ec.ContainerType, delegate_type, loc);
93                         MethodInfo invoke_mb = (MethodInfo) invoke_mg.Methods [0];
94                         ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
95
96                         //
97                         // The lambda expression is compatible with the delegate type,
98                         // provided that:
99                         //
100
101                         //
102                         // D and L have the same number of arguments.
103                         if (Parameters.Count != invoke_pd.Count)
104                                 return null;
105
106                         if (explicit_parameters){
107                                 //
108                                 // If L has an explicitly typed parameter list, each parameter
109                                 // in D has the same type and modifiers as the corresponding
110                                 // parameter in L.
111                                 //
112                                 if (!VerifyExplicitParameterCompatibility (delegate_type, invoke_pd, false))
113                                         return null;
114                         } else {
115                                 //
116                                 // If L has an implicitly typed parameter list, D has no ref or
117                                 // out parameters
118                                 //
119                                 // Note: We currently do nothing, because the preview does not
120                                 // follow the above rule.
121
122                                 //
123                                 // each parameter of L is given the type of the corresponding parameter in D
124                                 //
125
126                                 for (int i = 0; i < invoke_pd.Count; i++)
127                                         Parameters [i].TypeName = new TypeExpression (
128                                                 invoke_pd.ParameterType (i),
129                                                 Parameters [i].Location);
130                         }
131
132                         return CoreCompatibilityTest (ec, clone, invoke_mb.ReturnType, delegate_type);
133                 }
134
135                 Expression CoreCompatibilityTest (EmitContext ec, bool clone, Type return_type, Type delegate_type)
136                 {
137                         //
138                         // The return type of the delegate must be compatible with 
139                         // the anonymous type.   Instead of doing a pass to examine the block
140                         // we satisfy the rule by setting the return type on the EmitContext
141                         // to be the delegate type return type.
142                         //
143
144                         ToplevelBlock b = clone ? (ToplevelBlock) Block.PerformClone () : Block;
145                         
146                         anonymous = new AnonymousMethod (
147                                 Parent != null ? Parent.AnonymousMethod : null, RootScope, Host,
148                                 GenericMethod, Parameters, Container, b, return_type,
149                                 delegate_type, loc);
150
151                         bool r;
152                         if (clone)
153                                 r = anonymous.ResolveNoDefine (ec);
154                         else
155                                 r = anonymous.Resolve (ec);
156
157                         // Resolution failed.
158                         if (!r)
159                                 return null;
160
161                         return anonymous.AnonymousDelegate;
162                 }
163                 
164                 public override string GetSignatureForError ()
165                 {
166                         return "lambda expression";
167                 }
168
169                 //
170                 // TryBuild: tries to compile this LambdaExpression with the given
171                 // types as the lambda expression parameter types.   
172                 //
173                 // If the lambda expression successfully builds with those types, the
174                 // return value will be the inferred type for the lambda expression,
175                 // otherwise the result will be null.
176                 //
177                 public Type TryBuild (EmitContext ec, Type [] types)
178                 {
179                         for (int i = 0; i < types.Length; i++)
180                                 Parameters [i].TypeName = new TypeExpression (types [i], Parameters [i].Location);
181
182                         Expression e;
183                         try {
184                                 Report.DisableErrors ();
185                                 e = CoreCompatibilityTest (ec, true, null, null);
186                         } finally {
187                                 Report.EnableErrors ();
188                         }
189                         
190                         if (e == null)
191                                 return null;
192                         
193                         return e.Type;
194                 }
195         }
196
197         //
198         // This is a return statement that is prepended lambda expression bodies that happen
199         // to be expressions.  Depending on the return type of the delegate this will behave
200         // as either { expr (); return (); } or { return expr (); }
201         //
202         public class ContextualReturn : Statement {
203                 public Expression Expr;
204                 
205                 public ContextualReturn (Expression e)
206                 {
207                         Expr = e;
208                         loc = Expr.Location;
209                 }
210
211                 bool unwind_protect;
212
213                 public override bool Resolve (EmitContext ec)
214                 {
215                         AnonymousContainer am = ec.CurrentAnonymousMethod;
216                         if ((am != null) && am.IsIterator && ec.InIterator) {
217                                 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
218                                               "statement to return a value, or yield break to end the iteration");
219                                 return false;
220                         }
221
222                         Expr = Expr.Resolve (ec);
223                         if (Expr == null)
224                                 return false;
225
226                         if (ec.ReturnType == null){
227                                 ec.ReturnType = Expr.Type;
228                         } else {
229                                 if (Expr.Type != ec.ReturnType) {
230                                         Expression nExpr = Convert.ImplicitConversionRequired (
231                                                 ec, Expr, ec.ReturnType, loc);
232                                         if (nExpr == null){
233                                                 Report.Error (1662, loc, "Could not implicitly convert from {0} to {1}",
234                                                               TypeManager.CSharpName (Expr.Type),
235                                                               TypeManager.CSharpName (ec.ReturnType));
236                                                 return false;
237                                         }
238                                         Expr = nExpr;
239                                 }
240                         }
241
242                         int errors = Report.Errors;
243                         unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
244                         if (unwind_protect)
245                                 ec.NeedReturnLabel ();
246                         ec.CurrentBranching.CurrentUsageVector.Goto ();
247                         return errors == Report.Errors;
248                 }
249                 
250                 protected override void DoEmit (EmitContext ec)
251                 {
252                         Expr.Emit (ec);
253
254                         if (unwind_protect){
255                                 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
256                                 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
257                         } 
258                         ec.ig.Emit (OpCodes.Ret);
259                 }
260
261                 protected override void CloneTo (CloneContext clonectx, Statement t)
262                 {
263                         ContextualReturn cr = (ContextualReturn) t;
264
265                         cr.Expr = Expr.Clone (clonectx);
266                 }
267         }
268 }