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