Add support for async signal handlers
[vala-lang.git] / vala / valalambdaexpression.vala
blob38310334b778372a71dee04112040e636cc556e1
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 /**
31 * The expression body of this lambda expression. Only one of
32 * expression_body or statement_body may be set.
34 public Expression expression_body { get; set; }
36 /**
37 * The statement body of this lambda expression. Only one of
38 * expression_body or statement_body may be set.
40 public Block statement_body { get; set; }
42 /**
43 * The generated method.
45 public Method method { get; set; }
47 private List<string> parameters = new ArrayList<string> ();
49 /**
50 * Creates a new lambda expression.
52 * @param expression_body expression body
53 * @param source_reference reference to source code
54 * @return newly created lambda expression
56 public LambdaExpression (Expression expression_body, SourceReference source_reference) {
57 this.source_reference = source_reference;
58 this.expression_body = expression_body;
61 /**
62 * Creates a new lambda expression with statement body.
64 * @param statement_body statement body
65 * @param source_reference reference to source code
66 * @return newly created lambda expression
68 public LambdaExpression.with_statement_body (Block statement_body, SourceReference source_reference) {
69 this.statement_body = statement_body;
70 this.source_reference = source_reference;
73 /**
74 * Appends implicitly typed parameter.
76 * @param param parameter name
78 public void add_parameter (string param) {
79 parameters.add (param);
82 /**
83 * Returns copy of parameter list.
85 * @return parameter list
87 public List<string> get_parameters () {
88 return parameters;
91 public override void accept (CodeVisitor visitor) {
92 visitor.visit_lambda_expression (this);
94 visitor.visit_expression (this);
97 public override void accept_children (CodeVisitor visitor) {
98 if (method == null) {
99 if (expression_body != null) {
100 expression_body.accept (visitor);
101 visitor.visit_end_full_expression (expression_body);
102 } else if (statement_body != null) {
103 statement_body.accept (visitor);
105 } else {
106 method.accept (visitor);
110 public override bool is_pure () {
111 return false;
114 string get_lambda_name (CodeContext context) {
115 var result = "_lambda%d_".printf (context.analyzer.next_lambda_id);
117 context.analyzer.next_lambda_id++;
119 return result;
122 public override bool check (CodeContext context) {
123 if (checked) {
124 return !error;
127 checked = true;
129 if (!(target_type is DelegateType)) {
130 error = true;
131 Report.error (source_reference, "lambda expression not allowed in this context");
132 return false;
135 var cb = (Delegate) ((DelegateType) target_type).delegate_symbol;
136 var return_type = cb.return_type.get_actual_type (target_type, null, this);
137 method = new Method (get_lambda_name (context), return_type, source_reference);
138 // track usage for flow analyzer
139 method.used = true;
140 method.check_deprecated (source_reference);
142 if (!cb.has_target || !context.analyzer.is_in_instance_method ()) {
143 method.binding = MemberBinding.STATIC;
144 } else {
145 var sym = context.analyzer.current_symbol;
146 while (method.this_parameter == null) {
147 if (sym is Property) {
148 var prop = (Property) sym;
149 method.this_parameter = prop.this_parameter;
150 } else if (sym is Constructor) {
151 var c = (Constructor) sym;
152 method.this_parameter = c.this_parameter;
153 } else if (sym is Destructor) {
154 var d = (Destructor) sym;
155 method.this_parameter = d.this_parameter;
156 } else if (sym is Method) {
157 var m = (Method) sym;
158 method.this_parameter = m.this_parameter;
161 sym = sym.parent_symbol;
164 method.owner = context.analyzer.current_symbol.scope;
166 if (!(method.return_type is VoidType) && CodeContext.get ().profile == Profile.DOVA) {
167 method.result_var = new LocalVariable (method.return_type.copy (), "result", null, source_reference);
168 method.result_var.is_result = true;
171 var lambda_params = get_parameters ();
172 Iterator<string> lambda_param_it = lambda_params.iterator ();
174 if (cb.sender_type != null && lambda_params.size == cb.get_parameters ().size + 1) {
175 // lambda expression has sender parameter
176 lambda_param_it.next ();
178 string lambda_param = lambda_param_it.get ();
179 var param = new Parameter (lambda_param, cb.sender_type);
180 method.add_parameter (param);
183 foreach (Parameter cb_param in cb.get_parameters ()) {
184 if (!lambda_param_it.next ()) {
185 /* lambda expressions are allowed to have less parameters */
186 break;
189 string lambda_param = lambda_param_it.get ();
190 var param_type = cb_param.variable_type.get_actual_type (target_type, null, this);
191 var param = new Parameter (lambda_param, param_type);
192 method.add_parameter (param);
195 if (lambda_param_it.next ()) {
196 /* lambda expressions may not expect more parameters */
197 error = true;
198 Report.error (source_reference, "lambda expression: too many parameters");
199 return false;
202 foreach (var error_type in cb.get_error_types ()) {
203 method.add_error_type (error_type.copy ());
206 if (expression_body != null) {
207 var block = new Block (source_reference);
208 block.scope.parent_scope = method.scope;
210 if (method.return_type.data_type != null) {
211 if (context.profile == Profile.DOVA) {
212 block.add_statement (new ExpressionStatement (new Assignment (new MemberAccess.simple ("result", source_reference), expression_body, AssignmentOperator.SIMPLE, source_reference), source_reference));
213 block.add_statement (new ReturnStatement (null, source_reference));
214 } else {
215 block.add_statement (new ReturnStatement (expression_body, source_reference));
217 } else {
218 block.add_statement (new ExpressionStatement (expression_body, source_reference));
221 method.body = block;
222 } else {
223 method.body = statement_body;
225 method.body.owner = method.scope;
227 // support use of generics in closures
228 var m = context.analyzer.find_parent_method (context.analyzer.current_symbol);
229 if (m != null) {
230 foreach (var type_param in m.get_type_parameters ()) {
231 method.add_type_parameter (new TypeParameter (type_param.name, type_param.source_reference));
233 method.closure = true;
234 m.body.captured = true;
238 /* lambda expressions should be usable like MemberAccess of a method */
239 symbol_reference = method;
241 method.check (context);
243 value_type = new MethodType (method);
244 value_type.value_owned = target_type.value_owned;
246 return !error;
249 public override void emit (CodeGenerator codegen) {
250 codegen.visit_lambda_expression (this);
252 codegen.visit_expression (this);
255 public override void get_used_variables (Collection<LocalVariable> collection) {
256 // require captured variables to be initialized
257 if (method.closure) {
258 method.get_captured_variables (collection);