b11dc6000e254d2c8c6beec1f9ce9445c9891e56
[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                 readonly bool explicit_parameters;
21
22                 //
23                 // The parameters can either be:
24                 //    A list of Parameters (explicitly typed parameters)
25                 //    An ImplicitLambdaParameter
26                 //
27                 public LambdaExpression (TypeContainer host, Parameters parameters, Location loc)
28                         : base (host, parameters, loc)
29                 {
30                         if (parameters.Count > 0)
31                                 explicit_parameters = !(parameters.FixedParameters [0] is ImplicitLambdaParameter);
32                 }
33
34                 protected override Expression CreateExpressionTree (EmitContext ec, Type delegate_type)
35                 {
36                         if (ec.IsInProbingMode)
37                                 return this;
38                         
39                         Expression args = Parameters.CreateExpressionTree (ec, loc);
40                         Expression expr = Block.CreateExpressionTree (ec);
41                         if (expr == null)
42                                 return null;
43
44                         ArrayList arguments = new ArrayList (2);
45                         arguments.Add (new Argument (expr));
46                         arguments.Add (new Argument (args));
47                         return CreateExpressionFactoryCall ("Lambda",
48                                 new TypeArguments (loc, new TypeExpression (delegate_type, loc)),
49                                 arguments);
50                 }
51
52                 public override bool HasExplicitParameters {
53                         get {
54                                 return explicit_parameters;
55                         }
56                 }
57
58                 protected override Parameters ResolveParameters (EmitContext ec, TypeInferenceContext tic, Type delegateType)
59                 {
60                         if (!TypeManager.IsDelegateType (delegateType))
61                                 return null;
62
63                         ParameterData d_params = TypeManager.GetDelegateParameters (delegateType);
64
65                         if (explicit_parameters) {
66                                 if (!VerifyExplicitParameters (delegateType, d_params, ec.IsInProbingMode))
67                                         return null;
68
69                                 return Parameters;
70                         }
71
72                         //
73                         // If L has an implicitly typed parameter list we make implicit parameters explicit
74                         // Set each parameter of L is given the type of the corresponding parameter in D
75                         //
76                         if (!VerifyParameterCompatibility (delegateType, d_params, ec.IsInProbingMode))
77                                 return null;
78
79                         if (Parameters.Types == null)
80                                 Parameters.Types = new Type [Parameters.Count];
81
82                         for (int i = 0; i < d_params.Count; i++) {
83                                 // D has no ref or out parameters
84                                 if ((d_params.ParameterModifier (i) & Parameter.Modifier.ISBYREF) != 0)
85                                         return null;
86
87                                 Type d_param = d_params.Types [i];
88
89 #if MS_COMPATIBLE
90                                 // Blablabla, because reflection does not work with dynamic types
91                                 if (d_param.IsGenericParameter)
92                                         d_param = delegateType.GetGenericArguments () [d_param.GenericParameterPosition];
93 #endif
94                                 // When inferred context exists all generics parameters have type replacements
95                                 if (tic != null) {
96                                         d_param = tic.InflateGenericArgument (d_param);
97                                 }
98
99                                 Parameters.Types [i] = Parameters.FixedParameters[i].ParameterType = d_param;
100                         }
101                         return Parameters;
102                 }
103
104                 public override Expression DoResolve (EmitContext ec)
105                 {
106                         //
107                         // Only explicit parameters can be resolved at this point
108                         //
109                         if (explicit_parameters) {
110                                 if (!Parameters.Resolve (ec))
111                                         return null;
112                         }
113
114                         eclass = ExprClass.Value;
115                         type = TypeManager.anonymous_method_type;                                               
116                         return this;
117                 }
118
119                 protected override AnonymousMethodBody CompatibleMethodFactory (Type returnType, Type delegateType, Parameters p, ToplevelBlock b)
120                 {
121                         return new LambdaMethod (Host,
122                                 p, b, returnType,
123                                 delegateType, loc);
124                 }
125
126                 public override string GetSignatureForError ()
127                 {
128                         return "lambda expression";
129                 }
130         }
131
132         public class LambdaMethod : AnonymousMethodBody
133         {
134                 public LambdaMethod (TypeContainer host, Parameters parameters,
135                                         ToplevelBlock block, Type return_type, Type delegate_type,
136                                         Location loc)
137                         : base (host, parameters, block, return_type, delegate_type, loc)
138                 {
139                 }
140
141                 public override string ContainerType {
142                         get {
143                                 return "lambda expression";
144                         }
145                 }
146
147                 public override Expression CreateExpressionTree (EmitContext ec)
148                 {
149                         //
150                         // Remove IL method implementation when expression tree is requested
151                         //
152                         method.Parent.PartialContainer.RemoveMethod (method);
153
154                         Expression args = parameters.CreateExpressionTree (ec, loc);
155                         Expression expr = Block.CreateExpressionTree (ec);
156                         if (expr == null)
157                                 return null;
158
159                         ArrayList arguments = new ArrayList (2);
160                         arguments.Add (new Argument (expr));
161                         arguments.Add (new Argument (args));
162                         return CreateExpressionFactoryCall ("Lambda",
163                                 new TypeArguments (loc, new TypeExpression (type, loc)),
164                                 arguments);
165                 }
166         }
167
168         //
169         // This is a return statement that is prepended lambda expression bodies that happen
170         // to be expressions.  Depending on the return type of the delegate this will behave
171         // as either { expr (); return (); } or { return expr (); }
172         //
173         public class ContextualReturn : Return
174         {
175                 public ContextualReturn (Expression expr)
176                         : base (expr, expr.Location)
177                 {
178                 }
179
180                 public override Expression CreateExpressionTree (EmitContext ec)
181                 {
182                         return Expr.CreateExpressionTree (ec);
183                 }
184
185                 public override void Emit (EmitContext ec)
186                 {
187                         if (ec.ReturnType == TypeManager.void_type) {
188                                 ((ExpressionStatement) Expr).EmitStatement (ec);
189                                 ec.ig.Emit (OpCodes.Ret);
190                                 return;
191                         }
192
193                         base.Emit (ec);
194                 }
195
196                 protected override bool DoResolve (EmitContext ec)
197                 {       
198                         //
199                         // When delegate returns void, only expression statements can be used
200                         //
201                         if (ec.ReturnType == TypeManager.void_type) {
202                                 Expr = Expr.Resolve (ec);
203                                 if (Expr == null)
204                                         return false;
205                                 
206                                 if (Expr is ExpressionStatement)
207                                         return true;
208                                 
209                                 Expr.Error_InvalidExpressionStatement ();
210                                 return false;
211                         }
212
213                         return base.DoResolve (ec);
214                 }
215         }
216 }