2007-07-13 Marek Safar <marek.safar@gmail.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                 //
56                 // Returns true if the body of lambda expression can be implicitly
57                 // converted to the delegate of type `delegate_type'
58                 //
59                 public override bool ImplicitStandardConversionExists (Type delegate_type)
60                 {
61                         EmitContext ec = EmitContext.TempEc;
62
63                         using (ec.Set (EmitContext.Flags.ProbingMode)) {
64                                 bool r = DoImplicitStandardConversion (ec, delegate_type) != null;
65
66                                 // Ignore the result
67                                 anonymous = null;
68
69                                 return r;
70                         }
71                 }
72                 
73                 //
74                 // Returns true if this anonymous method can be implicitly
75                 // converted to the delegate type `delegate_type'
76                 //
77                 public override Expression Compatible (EmitContext ec, Type delegate_type)
78                 {
79                         if (anonymous != null)
80                                 return anonymous.AnonymousDelegate;
81
82                         return DoImplicitStandardConversion (ec, delegate_type);
83                 }
84
85                 Expression DoImplicitStandardConversion (EmitContext ec, Type delegate_type)
86                 {
87                         if (CompatibleChecks (ec, delegate_type) == null)
88                                 return null;
89
90                         MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (
91                                 ec.ContainerType, delegate_type, loc);
92                         MethodInfo invoke_mb = (MethodInfo) invoke_mg.Methods [0];
93                         ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
94
95                         //
96                         // The lambda expression is compatible with the delegate type,
97                         // provided that:
98                         //
99
100                         //
101                         // D and L have the same number of arguments.
102                         if (Parameters.Count != invoke_pd.Count)
103                                 return null;
104
105                         if (explicit_parameters){
106                                 //
107                                 // If L has an explicitly typed parameter list, each parameter
108                                 // in D has the same type and modifiers as the corresponding
109                                 // parameter in L.
110                                 //
111                                 if (!VerifyExplicitParameterCompatibility (delegate_type, invoke_pd, false))
112                                         return null;
113                         } else {
114                                 //
115                                 // If L has an implicitly typed parameter list
116                                 //
117                                 for (int i = 0; i < invoke_pd.Count; i++) {
118                                         // D has no ref or out parameters
119                                         if ((invoke_pd.ParameterModifier (i) & Parameter.Modifier.ISBYREF) != 0)
120                                                 return null;
121
122                                         //
123                                         // Makes implicit parameters explicit
124                                         // Set each parameter of L is given the type of the corresponding parameter in D
125                                         //
126                                         Parameters[i].ParameterType = invoke_pd.Types[i];
127                                         
128                                 }
129                         }
130
131                         return CoreCompatibilityTest (ec, invoke_mb.ReturnType, delegate_type);
132                 }
133
134                 Expression CoreCompatibilityTest (EmitContext ec, Type return_type, Type delegate_type)
135                 {
136                         //
137                         // The return type of the delegate must be compatible with 
138                         // the anonymous type.   Instead of doing a pass to examine the block
139                         // we satisfy the rule by setting the return type on the EmitContext
140                         // to be the delegate type return type.
141                         //
142
143                         ToplevelBlock b = ec.IsInProbingMode ? (ToplevelBlock) Block.PerformClone () : Block;
144
145                         anonymous = new AnonymousMethod (
146                                 Parent != null ? Parent.AnonymousMethod : null, RootScope, Host,
147                                 GenericMethod, Parameters, Container, b, return_type,
148                                 delegate_type, loc);
149
150                         bool r;
151                         if (ec.IsInProbingMode)
152                                 r = anonymous.ResolveNoDefine (ec);
153                         else
154                                 r = anonymous.Resolve (ec);
155
156                         // Resolution failed.
157                         if (!r)
158                                 return null;
159
160                         return anonymous.AnonymousDelegate;
161                 }
162                 
163                 public override string GetSignatureForError ()
164                 {
165                         return "lambda expression";
166                 }
167
168                 //
169                 // TryBuild: tries to compile this LambdaExpression with the given
170                 // types as the lambda expression parameter types.   
171                 //
172                 // If the lambda expression successfully builds with those types, the
173                 // return value will be the inferred type for the lambda expression,
174                 // otherwise the result will be null.
175                 //
176                 public Type TryBuild (EmitContext ec, Type [] types)
177                 {
178                         for (int i = 0; i < types.Length; i++)
179                                 Parameters [i].TypeName = new TypeExpression (types [i], Parameters [i].Location);
180
181                         // TODO: temporary hack
182                         ec.InferReturnType = true;
183                         
184                         Expression e;
185                         using (ec.Set (EmitContext.Flags.ProbingMode)) {
186                                 e = CoreCompatibilityTest (ec, null, null);
187                         }
188                         
189                         if (e == null)
190                                 return null;
191                         
192                         return e.Type;
193                 }
194         }
195
196         //
197         // This is a return statement that is prepended lambda expression bodies that happen
198         // to be expressions.  Depending on the return type of the delegate this will behave
199         // as either { expr (); return (); } or { return expr (); }
200
201         public class ContextualReturn : Return
202         {
203                 public ContextualReturn (Expression expr)
204                         : base (expr, expr.Location)
205                 {
206                 }
207         }
208 }