9c804ceca575e6730e194e54a8bce5735a24cb81
[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 (Location loc)
28                         : base (loc)
29                 {
30                 }
31
32                 protected override Expression CreateExpressionTree (ResolveContext ec, TypeSpec delegate_type)
33                 {
34                         if (ec.IsInProbingMode)
35                                 return this;
36
37                         BlockContext bc = new BlockContext (ec.MemberContext, ec.ConstructorBlock, ec.BuiltinTypes.Void) {
38                                 CurrentAnonymousMethod = ec.CurrentAnonymousMethod
39                         };
40
41                         Expression args = Parameters.CreateExpressionTree (bc, loc);
42                         Expression expr = Block.CreateExpressionTree (ec);
43                         if (expr == null)
44                                 return null;
45
46                         Arguments arguments = new Arguments (2);
47                         arguments.Add (new Argument (expr));
48                         arguments.Add (new Argument (args));
49                         return CreateExpressionFactoryCall (ec, "Lambda",
50                                 new TypeArguments (new TypeExpression (delegate_type, loc)),
51                                 arguments);
52                 }
53
54                 public override bool HasExplicitParameters {
55                         get {
56                                 return Parameters.Count > 0 && !(Parameters.FixedParameters [0] is ImplicitLambdaParameter);
57                         }
58                 }
59
60                 protected override ParametersCompiled ResolveParameters (ResolveContext ec, TypeInferenceContext tic, TypeSpec delegateType)
61                 {
62                         if (!delegateType.IsDelegate)
63                                 return null;
64
65                         AParametersCollection d_params = Delegate.GetParameters (delegateType);
66
67                         if (HasExplicitParameters) {
68                                 if (!VerifyExplicitParameters (ec, tic, delegateType, d_params))
69                                         return null;
70
71                                 return Parameters;
72                         }
73
74                         //
75                         // If L has an implicitly typed parameter list we make implicit parameters explicit
76                         // Set each parameter of L is given the type of the corresponding parameter in D
77                         //
78                         if (!VerifyParameterCompatibility (ec, tic, delegateType, d_params, ec.IsInProbingMode))
79                                 return null;
80
81                         TypeSpec [] ptypes = new TypeSpec [Parameters.Count];
82                         for (int i = 0; i < d_params.Count; i++) {
83                                 // D has no ref or out parameters
84                                 if ((d_params.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != 0)
85                                         return null;
86
87                                 TypeSpec d_param = d_params.Types [i];
88
89                                 //
90                                 // When type inference context exists try to apply inferred type arguments
91                                 //
92                                 if (tic != null) {
93                                         d_param = tic.InflateGenericArgument (ec, d_param);
94                                 }
95
96                                 ptypes [i] = d_param;
97                                 ImplicitLambdaParameter ilp = (ImplicitLambdaParameter) Parameters.FixedParameters [i];
98                                 ilp.SetParameterType (d_param);
99                                 ilp.Resolve (null, i);
100                         }
101
102                         Parameters.Types = ptypes;
103                         return Parameters;
104                 }
105
106                 protected override AnonymousMethodBody CompatibleMethodFactory (TypeSpec returnType, TypeSpec delegateType, ParametersCompiled p, ParametersBlock b)
107                 {
108                         return new LambdaMethod (p, b, returnType, delegateType, loc);
109                 }
110
111                 protected override bool DoResolveParameters (ResolveContext rc)
112                 {
113                         //
114                         // Only explicit parameters can be resolved at this point
115                         //
116                         if (HasExplicitParameters) {
117                                 return Parameters.Resolve (rc);
118                         }
119
120                         return true;
121                 }
122
123                 public override string GetSignatureForError ()
124                 {
125                         return "lambda expression";
126                 }
127                 
128                 public override object Accept (StructuralVisitor visitor)
129                 {
130                         return visitor.Visit (this);
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.StartLocation)
186                 {
187                 }
188
189                 public override Expression CreateExpressionTree (ResolveContext ec)
190                 {
191                         return Expr.CreateExpressionTree (ec);
192                 }
193
194                 protected override void DoEmit (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.DoEmit (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                                         var reduced = Expr as IReducedExpressionStatement;
222                                         if (reduced != null) {
223                                                 statement = EmptyExpressionStatement.Instance;
224                                         } else {
225                                                 Expr.Error_InvalidExpressionStatement (ec);
226                                         }
227                                 }
228
229                                 return true;
230                         }
231
232                         return base.DoResolve (ec);
233                 }
234         }
235 }