2007-11-14 Marek Safar <marek.safar@gmail.com>
[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 // Licensed under the terms of the GNU GPL
8 //
9 // (C) 2007 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 (AnonymousMethodExpression parent,
28                                          GenericMethod generic, TypeContainer host,
29                                          Parameters parameters, Block container,
30                                          Location loc)
31                         : base (parent, generic, host, parameters, container, loc)
32                 {
33                         if (parameters.Count > 0)
34                                 explicit_parameters = !(parameters.FixedParameters [0] is ImplicitLambdaParameter);
35                 }
36
37                 public override bool HasExplicitParameters {
38                         get {
39                                 return explicit_parameters;
40                         }
41                 }
42
43                 protected override Parameters ResolveParameters (EmitContext ec, TypeInferenceContext tic, Type delegateType)
44                 {
45                         if (!TypeManager.IsDelegateType (delegateType))
46                                 return null;
47
48                         ParameterData d_params = TypeManager.GetDelegateParameters (delegateType);
49
50                         if (explicit_parameters) {
51                                 if (!VerifyExplicitParameters (delegateType, d_params, ec.IsInProbingMode))
52                                         return null;
53
54                                 return Parameters;
55                         }
56
57                         //
58                         // If L has an implicitly typed parameter list we make implicit parameters explicit
59                         // Set each parameter of L is given the type of the corresponding parameter in D
60                         //
61                         if (!VerifyParameterCompatibility (delegateType, d_params, ec.IsInProbingMode))
62                                 return null;
63
64                         if (Parameters.Types == null)
65                                 Parameters.Types = new Type [Parameters.Count];
66
67                         for (int i = 0; i < d_params.Count; i++) {
68                                 // D has no ref or out parameters
69                                 if ((d_params.ParameterModifier (i) & Parameter.Modifier.ISBYREF) != 0)
70                                         return null;
71
72                                 Type d_param = d_params.Types [i];
73
74 #if MS_COMPATIBLE
75                                 // Blablabla, because reflection does not work with dynamic types
76                                 if (d_param.IsGenericParameter)
77                                         d_param = delegateType.GetGenericArguments () [d_param.GenericParameterPosition];
78 #endif
79                                 // When inferred context exists all generics parameters have type replacements
80                                 if (tic != null) {
81                                         d_param = tic.InflateGenericArgument (d_param);
82                                 }
83
84                                 Parameters.Types [i] = Parameters.FixedParameters[i].ParameterType = d_param;
85                         }
86                         return Parameters;
87                 }
88
89                 public override Expression DoResolve (EmitContext ec)
90                 {
91                         //
92                         // Only explicit parameters can be resolved at this point
93                         //
94                         if (explicit_parameters) {
95                                 if (!Parameters.Resolve (ec))
96                                         return null;
97                         }
98
99                         eclass = ExprClass.Value;
100                         type = TypeManager.anonymous_method_type;                                               
101                         return this;
102                 }
103
104                 protected override AnonymousMethod CompatibleMethodFactory (Type returnType, Type delegateType, Parameters p, ToplevelBlock b)
105                 {
106                         return new LambdaMethod (RootScope, Host,
107                                 GenericMethod, p, Container, b, returnType,
108                                 delegateType, loc);
109                 }
110
111                 public override string GetSignatureForError ()
112                 {
113                         return "lambda expression";
114                 }
115         }
116
117         public class LambdaMethod : AnonymousMethod
118         {
119                 public LambdaMethod (RootScopeInfo root_scope,
120                                         DeclSpace host, GenericMethod generic,
121                                         Parameters parameters, Block container,
122                                         ToplevelBlock block, Type return_type, Type delegate_type,
123                                         Location loc)
124                         : base (root_scope, host, generic, parameters, container, block,
125                                 return_type, delegate_type, loc)
126                 {
127                 }
128
129                 public override string ContainerType {
130                         get {
131                                 return "lambda expression";
132                         }
133                 }
134         }
135
136         //
137         // This is a return statement that is prepended lambda expression bodies that happen
138         // to be expressions.  Depending on the return type of the delegate this will behave
139         // as either { expr (); return (); } or { return expr (); }
140
141         public class ContextualReturn : Return
142         {
143                 bool statement_return;
144
145                 public ContextualReturn (Expression expr)
146                         : base (expr, expr.Location)
147                 {
148                 }
149
150                 public override void Emit (EmitContext ec)
151                 {
152                         if (statement_return)
153                                 ((ExpressionStatement)Expr).EmitStatement (ec);
154                         else
155                                 base.Emit (ec);
156                 }
157
158                 public override bool Resolve (EmitContext ec)
159                 {
160                         if (ec.ReturnType == TypeManager.void_type) {
161                                 statement_return = true;
162                                 return Expr.Resolve (ec) != null;
163                         }
164
165                         return base.Resolve (ec);
166                 }
167         }
168 }