2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / mcs / lambda.cs
1 //
2 // lambda.cs: support for lambda expressions
3 //
4 // Authors: Miguel de Icaza (miguel@gnu.org)
5 //          Marek Safar (marek.safar@gmail.com)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2007-2008 Novell, Inc
10 //
11
12 using System;
13 using System.Collections;
14 using System.Reflection;
15 using System.Reflection.Emit;
16
17 namespace Mono.CSharp {
18         public class LambdaExpression : AnonymousMethodExpression
19         {
20                 //
21                 // The parameters can either be:
22                 //    A list of Parameters (explicitly typed parameters)
23                 //    An ImplicitLambdaParameter
24                 //
25                 public LambdaExpression (Location loc)
26                         : base (loc)
27                 {
28                 }
29
30                 protected override Expression CreateExpressionTree (EmitContext ec, Type delegate_type)
31                 {
32                         if (ec.IsInProbingMode)
33                                 return this;
34                         
35                         Expression args = Parameters.CreateExpressionTree (ec, loc);
36                         Expression expr = Block.CreateExpressionTree (ec);
37                         if (expr == null)
38                                 return null;
39
40                         Arguments arguments = new Arguments (2);
41                         arguments.Add (new Argument (expr));
42                         arguments.Add (new Argument (args));
43                         return CreateExpressionFactoryCall ("Lambda",
44                                 new TypeArguments (new TypeExpression (delegate_type, loc)),
45                                 arguments);
46                 }
47
48                 public override bool HasExplicitParameters {
49                         get {
50                                 return Parameters.Count > 0 && !(Parameters.FixedParameters [0] is ImplicitLambdaParameter);
51                         }
52                 }
53
54                 protected override ParametersCompiled ResolveParameters (EmitContext ec, TypeInferenceContext tic, Type delegateType)
55                 {
56                         if (!TypeManager.IsDelegateType (delegateType))
57                                 return null;
58
59                         AParametersCollection d_params = TypeManager.GetDelegateParameters (delegateType);
60
61                         if (HasExplicitParameters) {
62                                 if (!VerifyExplicitParameters (delegateType, d_params, ec.IsInProbingMode))
63                                         return null;
64
65                                 return Parameters;
66                         }
67
68                         //
69                         // If L has an implicitly typed parameter list we make implicit parameters explicit
70                         // Set each parameter of L is given the type of the corresponding parameter in D
71                         //
72                         if (!VerifyParameterCompatibility (delegateType, d_params, ec.IsInProbingMode))
73                                 return null;
74
75                         Type [] ptypes = new Type [Parameters.Count];
76                         for (int i = 0; i < d_params.Count; i++) {
77                                 // D has no ref or out parameters
78                                 if ((d_params.FixedParameters [i].ModFlags & Parameter.Modifier.ISBYREF) != 0)
79                                         return null;
80
81                                 Type d_param = d_params.Types [i];
82
83 #if MS_COMPATIBLE
84                                 // Blablabla, because reflection does not work with dynamic types
85                                 if (d_param.IsGenericParameter)
86                                         d_param = delegateType.GetGenericArguments () [d_param.GenericParameterPosition];
87 #endif
88                                 //
89                                 // When type inference context exists try to apply inferred type arguments
90                                 //
91                                 if (tic != null) {
92                                         d_param = tic.InflateGenericArgument (d_param);
93                                 }
94
95                                 ptypes [i] = d_param;
96                                 ((ImplicitLambdaParameter) Parameters.FixedParameters [i]).Type = d_param;
97                         }
98
99                         Parameters.Types = ptypes;
100                         return Parameters;
101                 }
102
103                 public override Expression DoResolve (EmitContext ec)
104                 {
105                         //
106                         // Only explicit parameters can be resolved at this point
107                         //
108                         if (HasExplicitParameters) {
109                                 if (!Parameters.Resolve (ec))
110                                         return null;
111                         }
112
113                         eclass = ExprClass.Value;
114                         type = InternalType.AnonymousMethod;
115                         return this;
116                 }
117
118                 protected override AnonymousMethodBody CompatibleMethodFactory (Type returnType, Type delegateType, ParametersCompiled p, ToplevelBlock b)
119                 {
120                         return new LambdaMethod (p, b, returnType, delegateType, loc);
121                 }
122
123                 public override string GetSignatureForError ()
124                 {
125                         return "lambda expression";
126                 }
127         }
128
129         public class LambdaMethod : AnonymousMethodBody
130         {
131                 public LambdaMethod (ParametersCompiled parameters,
132                                         ToplevelBlock block, Type return_type, Type delegate_type,
133                                         Location loc)
134                         : base (parameters, block, return_type, delegate_type, loc)
135                 {
136                 }
137
138                 protected override void CloneTo (CloneContext clonectx, Expression target)
139                 {
140                         // TODO: nothing ??
141                 }
142
143                 public override string ContainerType {
144                         get {
145                                 return "lambda expression";
146                         }
147                 }
148
149                 public override Expression CreateExpressionTree (EmitContext ec)
150                 {
151                         Expression args = parameters.CreateExpressionTree (ec, loc);
152                         Expression expr = Block.CreateExpressionTree (ec);
153                         if (expr == null)
154                                 return null;
155
156                         Arguments arguments = new Arguments (2);
157                         arguments.Add (new Argument (expr));
158                         arguments.Add (new Argument (args));
159                         return CreateExpressionFactoryCall ("Lambda",
160                                 new TypeArguments (new TypeExpression (type, loc)),
161                                 arguments);
162                 }
163         }
164
165         //
166         // This is a return statement that is prepended lambda expression bodies that happen
167         // to be expressions.  Depending on the return type of the delegate this will behave
168         // as either { expr (); return (); } or { return expr (); }
169         //
170         public class ContextualReturn : Return
171         {
172                 public ContextualReturn (Expression expr)
173                         : base (expr, expr.Location)
174                 {
175                 }
176
177                 public override Expression CreateExpressionTree (EmitContext ec)
178                 {
179                         return Expr.CreateExpressionTree (ec);
180                 }
181
182                 public override void Emit (EmitContext ec)
183                 {
184                         if (ec.ReturnType == TypeManager.void_type) {
185                                 ((ExpressionStatement) Expr).EmitStatement (ec);
186                                 ec.ig.Emit (OpCodes.Ret);
187                                 return;
188                         }
189
190                         base.Emit (ec);
191                 }
192
193                 protected override bool DoResolve (EmitContext ec)
194                 {       
195                         //
196                         // When delegate returns void, only expression statements can be used
197                         //
198                         if (ec.ReturnType == TypeManager.void_type) {
199                                 Expr = Expr.Resolve (ec);
200                                 if (Expr == null)
201                                         return false;
202                                 
203                                 if (Expr is ExpressionStatement)
204                                         return true;
205                                 
206                                 Expr.Error_InvalidExpressionStatement ();
207                                 return false;
208                         }
209
210                         return base.DoResolve (ec);
211                 }
212         }
213 }