Release 0.41.92
[vala-gnome.git] / vala / valalambdaexpression.vala
blob3223e747a7e846b3dc2a307d9d2d54a757f2c555
1 /* valalambdaexpression.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
25 /**
26 * Represents a lambda expression in the source code. Lambda expressions are
27 * anonymous methods with implicitly typed parameters.
29 public class Vala.LambdaExpression : Expression {
30 private static int next_lambda_id = 0;
32 /**
33 * The expression body of this lambda expression. Only one of
34 * expression_body or statement_body may be set.
36 public Expression expression_body { get; set; }
38 /**
39 * The statement body of this lambda expression. Only one of
40 * expression_body or statement_body may be set.
42 public Block statement_body { get; set; }
44 /**
45 * The generated method.
47 public Method method { get; set; }
49 private List<Parameter> parameters = new ArrayList<Parameter> ();
51 /**
52 * Creates a new lambda expression.
54 * @param expression_body expression body
55 * @param source_reference reference to source code
56 * @return newly created lambda expression
58 public LambdaExpression (Expression expression_body, SourceReference source_reference) {
59 this.source_reference = source_reference;
60 this.expression_body = expression_body;
63 /**
64 * Creates a new lambda expression with statement body.
66 * @param statement_body statement body
67 * @param source_reference reference to source code
68 * @return newly created lambda expression
70 public LambdaExpression.with_statement_body (Block statement_body, SourceReference source_reference) {
71 this.statement_body = statement_body;
72 this.source_reference = source_reference;
75 /**
76 * Appends implicitly typed parameter.
78 * @param param parameter name
80 public void add_parameter (Parameter param) {
81 parameters.add (param);
84 /**
85 * Returns copy of parameter list.
87 * @return parameter list
89 public List<Parameter> get_parameters () {
90 return parameters;
93 public override void accept (CodeVisitor visitor) {
94 visitor.visit_lambda_expression (this);
96 visitor.visit_expression (this);
99 public override void accept_children (CodeVisitor visitor) {
100 if (method == null) {
101 if (expression_body != null) {
102 expression_body.accept (visitor);
103 visitor.visit_end_full_expression (expression_body);
104 } else if (statement_body != null) {
105 statement_body.accept (visitor);
107 } else {
108 method.accept (visitor);
112 public override bool is_pure () {
113 return false;
116 public override bool check (CodeContext context) {
117 if (checked) {
118 return !error;
121 checked = true;
123 if (!(target_type is DelegateType)) {
124 error = true;
125 if (target_type != null) {
126 Report.error (source_reference, "Cannot convert lambda expression to `%s'".printf (target_type.to_string ()));
127 } else {
128 Report.error (source_reference, "lambda expression not allowed in this context");
130 return false;
133 var cb = (Delegate) ((DelegateType) target_type).delegate_symbol;
134 var return_type = cb.return_type.get_actual_type (target_type, null, this);
135 method = new Method ("_lambda%d_".printf (next_lambda_id++), return_type, source_reference);
136 // track usage for flow analyzer
137 method.used = true;
138 method.version.check (source_reference);
140 if (!cb.has_target || !context.analyzer.is_in_instance_method ()) {
141 method.binding = MemberBinding.STATIC;
142 } else {
143 var sym = context.analyzer.current_symbol;
144 while (method.this_parameter == null) {
145 if (sym is Property) {
146 var prop = (Property) sym;
147 method.this_parameter = prop.this_parameter;
148 } else if (sym is Constructor) {
149 var c = (Constructor) sym;
150 method.this_parameter = c.this_parameter;
151 } else if (sym is Destructor) {
152 var d = (Destructor) sym;
153 method.this_parameter = d.this_parameter;
154 } else if (sym is Method) {
155 var m = (Method) sym;
156 method.this_parameter = m.this_parameter;
159 sym = sym.parent_symbol;
162 method.owner = context.analyzer.current_symbol.scope;
164 var lambda_params = get_parameters ();
165 Iterator<Parameter> lambda_param_it = lambda_params.iterator ();
167 if (cb.sender_type != null && lambda_params.size == cb.get_parameters ().size + 1) {
168 // lambda expression has sender parameter
169 lambda_param_it.next ();
171 Parameter lambda_param = lambda_param_it.get ();
172 lambda_param.variable_type = cb.sender_type;
173 method.add_parameter (lambda_param);
176 foreach (Parameter cb_param in cb.get_parameters ()) {
177 if (!lambda_param_it.next ()) {
178 /* lambda expressions are allowed to have less parameters */
179 break;
182 Parameter lambda_param = lambda_param_it.get ();
184 if (lambda_param.direction != cb_param.direction) {
185 error = true;
186 Report.error (lambda_param.source_reference, "direction of parameter `%s' is incompatible with the target delegate".printf (lambda_param.name));
189 lambda_param.variable_type = cb_param.variable_type.get_actual_type (target_type, null, this);
190 method.add_parameter (lambda_param);
193 if (lambda_param_it.next ()) {
194 /* lambda expressions may not expect more parameters */
195 error = true;
196 Report.error (source_reference, "lambda expression: too many parameters");
197 return false;
200 foreach (var error_type in cb.get_error_types ()) {
201 method.add_error_type (error_type.copy ());
204 if (expression_body != null) {
205 var block = new Block (source_reference);
206 block.scope.parent_scope = method.scope;
208 if (method.return_type.data_type != null) {
209 block.add_statement (new ReturnStatement (expression_body, source_reference));
210 } else {
211 block.add_statement (new ExpressionStatement (expression_body, source_reference));
214 method.body = block;
215 } else {
216 method.body = statement_body;
218 method.body.owner = method.scope;
220 // support use of generics in closures
221 var m = context.analyzer.find_parent_method (context.analyzer.current_symbol);
222 if (m != null) {
223 foreach (var type_param in m.get_type_parameters ()) {
224 method.add_type_parameter (new TypeParameter (type_param.name, type_param.source_reference));
226 method.closure = true;
227 m.body.captured = true;
231 /* lambda expressions should be usable like MemberAccess of a method */
232 symbol_reference = method;
234 method.check (context);
236 value_type = new MethodType (method);
237 value_type.value_owned = target_type.value_owned;
239 return !error;
242 public override void emit (CodeGenerator codegen) {
243 codegen.visit_lambda_expression (this);
245 codegen.visit_expression (this);
248 public override void get_used_variables (Collection<Variable> collection) {
249 // require captured variables to be initialized
250 if (method.closure) {
251 method.get_captured_variables ((Collection<LocalVariable>) collection);