05e42d3d3f98ad76bed4a0e0fcbb4c3c070ac560
[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 // Copyright 2011 Xamarin Inc
11 //
12
13 #if STATIC
14 using IKVM.Reflection.Emit;
15 #else
16 using System.Reflection.Emit;
17 #endif
18
19 namespace Mono.CSharp {
20         public class LambdaExpression : AnonymousMethodExpression
21         {
22                 //
23                 // The parameters can either be:
24                 //    A list of Parameters (explicitly typed parameters)
25                 //    An ImplicitLambdaParameter
26                 //
27                 public LambdaExpression (bool isAsync, Location loc)
28                         : base (isAsync, loc)
29                 {
30                 }
31
32                 public LambdaExpression (Location loc)
33                         : this (false, loc)
34                 {
35                 }
36
37                 protected override Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type)
38                 {
39                         if (ec.IsInProbingMode)
40                                 return this;
41
42                         BlockContext bc = new BlockContext (ec.MemberContext, ec.ConstructorBlock, ec.BuiltinTypes.Void) {
43                                 CurrentAnonymousMethod = ec.CurrentAnonymousMethod
44                         };
45
46                         Expression args = Parameters.CreateExpressionTree (bc, loc);
47                         Expression expr = Block.CreateExpressionTree (ec);
48                         if (expr == null)
49                                 return null;
50
51                         Arguments arguments = new Arguments (2);
52                         arguments.Add (new Argument (expr));
53                         arguments.Add (new Argument (args));
54                         return CreateExpressionFactoryCall (ec, "Lambda",
55                                 new TypeArguments (new TypeExpression (delegate_type, loc)),
56                                 arguments);
57                 }
58
59                 public override bool HasExplicitParameters {
60                         get {
61                                 return Parameters.Count > 0 && !(Parameters.FixedParameters [0] is ImplicitLambdaParameter);
62                         }
63                 }
64
65                 protected override ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegateType)
66                 {
67                         if (!delegateType.IsDelegate)
68                                 return null;
69
70                         AParametersCollection d_params = Delegate.GetParameters (delegateType);
71
72                         if (HasExplicitParameters) {
73                                 if (!VerifyExplicitParameters (ec, delegateType, d_params))
74                                         return null;
75
76                                 return Parameters;
77                         }
78
79                         //
80                         // If L has an implicitly typed parameter list we make implicit parameters explicit
81                         // Set each parameter of L is given the type of the corresponding parameter in D
82                         //
83                         if (!VerifyParameterCompatibility (ec, delegateType, d_params, ec.IsInProbingMode))
84                                 return null;
85
86                         TypeSpec [] ptypes = new TypeSpec [Parameters.Count];
87                         for (int i = 0; i < d_params.Count; i++) {
88                                 // D has no ref or out parameters
89                                 if ((d_params.FixedParameters [i].ModFlags & Parameter.Modifier.ISBYREF) != 0)
90                                         return null;
91
92                                 TypeSpec d_param = d_params.Types [i];
93
94                                 //
95                                 // When type inference context exists try to apply inferred type arguments
96                                 //
97                                 if (tic != null) {
98                                         d_param = tic.InflateGenericArgument (ec, d_param);
99                                 }
100
101                                 ptypes [i] = d_param;
102                                 ImplicitLambdaParameter ilp = (ImplicitLambdaParameter) Parameters.FixedParameters [i];
103                                 ilp.SetParameterType (d_param);
104                                 ilp.Resolve (null, i);
105                         }
106
107                         Parameters.Types = ptypes;
108                         return Parameters;
109                 }
110
111                 protected override AnonymousMethodBody CompatibleMethodFactory (TypeSpec returnType, TypeSpec delegateType, ParametersCompiled p, ParametersBlock b)
112                 {
113                         return new LambdaMethod (p, b, returnType, delegateType, loc);
114                 }
115
116                 protected override bool DoResolveParameters (ResolveContext rc)
117                 {
118                         //
119                         // Only explicit parameters can be resolved at this point
120                         //
121                         if (HasExplicitParameters) {
122                                 return Parameters.Resolve (rc);
123                         }
124
125                         return true;
126                 }
127
128                 public override string GetSignatureForError ()
129                 {
130                         return "lambda expression";
131                 }
132         }
133
134         class LambdaMethod : AnonymousMethodBody
135         {
136                 public LambdaMethod (ParametersCompiled parameters,
137                                         ParametersBlock block, TypeSpec return_type, TypeSpec delegate_type,
138                                         Location loc)
139                         : base (parameters, block, return_type, delegate_type, loc)
140                 {
141                 }
142
143                 #region Properties
144
145                 public override string ContainerType {
146                         get {
147                                 return "lambda expression";
148                         }
149                 }
150
151                 #endregion
152
153                 protected override void CloneTo (CloneContext clonectx, Expression target)
154                 {
155                         // TODO: nothing ??
156                 }
157
158                 public override Expression CreateExpressionTree (ResolveContext ec)
159                 {
160                         BlockContext bc = new BlockContext (ec.MemberContext, Block, ReturnType);
161                         Expression args = parameters.CreateExpressionTree (bc, loc);
162                         Expression expr = Block.CreateExpressionTree (ec);
163                         if (expr == null)
164                                 return null;
165
166                         Arguments arguments = new Arguments (2);
167                         arguments.Add (new Argument (expr));
168                         arguments.Add (new Argument (args));
169                         return CreateExpressionFactoryCall (ec, "Lambda",
170                                 new TypeArguments (new TypeExpression (type, loc)),
171                                 arguments);
172                 }
173         }
174
175         //
176         // This is a return statement that is prepended lambda expression bodies that happen
177         // to be expressions.  Depending on the return type of the delegate this will behave
178         // as either { expr (); return (); } or { return expr (); }
179         //
180         public class ContextualReturn : Return
181         {
182                 ExpressionStatement statement;
183
184                 public ContextualReturn (Expression expr)
185                         : base (expr, expr.Location)
186                 {
187                 }
188
189                 public override Expression CreateExpressionTree (ResolveContext ec)
190                 {
191                         return Expr.CreateExpressionTree (ec);
192                 }
193
194                 public override void Emit (EmitContext ec)
195                 {
196                         if (statement != null) {
197                                 statement.EmitStatement (ec);
198                                 if (unwind_protect)
199                                         ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
200                                 else {
201                                         ec.Emit (OpCodes.Ret);
202                                 }
203                                 return;
204                         }
205
206                         base.Emit (ec);
207                 }
208
209                 protected override bool DoResolve (BlockContext ec)
210                 {
211                         //
212                         // When delegate returns void, only expression statements can be used
213                         //
214                         if (ec.ReturnType.Kind == MemberKind.Void) {
215                                 Expr = Expr.Resolve (ec);
216                                 if (Expr == null)
217                                         return false;
218
219                                 statement = Expr as ExpressionStatement;
220                                 if (statement == null)
221                                         Expr.Error_InvalidExpressionStatement (ec);
222
223                                 return true;
224                         }
225
226                         return base.DoResolve (ec);
227                 }
228         }
229 }