Add support for async signal handlers
[vala-lang.git] / vala / valaforeachstatement.vala
blob2ce466d9557f2e8aa8333c89fe485e44e5b5ef6c
1 /* valaforeachstatement.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>
24 /**
25 * Represents a foreach statement in the source code. Foreach statements iterate
26 * over the elements of a collection.
28 public class Vala.ForeachStatement : Block {
29 /**
30 * Specifies the element type.
32 public DataType? type_reference {
33 get { return _data_type; }
34 set {
35 _data_type = value;
36 if (_data_type != null) {
37 _data_type.parent_node = this;
42 /**
43 * Specifies the element variable name.
45 public string variable_name { get; set; }
47 /**
48 * Specifies the container.
50 public Expression collection {
51 get {
52 return _collection;
54 set {
55 _collection = value;
56 _collection.parent_node = this;
60 /**
61 * Specifies the loop body.
63 public Block body {
64 get {
65 return _body;
67 set {
68 _body = value;
69 _body.parent_node = this;
73 public bool use_iterator { get; private set; }
75 /**
76 * Specifies the declarator for the generated element variable.
78 public LocalVariable element_variable { get; set; }
80 /**
81 * Specifies the declarator for the generated collection variable.
83 public LocalVariable collection_variable { get; set; }
85 /**
86 * Specifies the declarator for the generated iterator variable.
88 public LocalVariable iterator_variable { get; set; }
90 private Expression _collection;
91 private Block _body;
93 private DataType _data_type;
95 /**
96 * Creates a new foreach statement.
98 * @param type element type
99 * @param id element variable name
100 * @param col loop body
101 * @param source reference to source code
102 * @return newly created foreach statement
104 public ForeachStatement (DataType? type_reference, string variable_name, Expression collection, Block body, SourceReference source_reference) {
105 base (source_reference);
106 this.variable_name = variable_name;
107 this.collection = collection;
108 this.body = body;
109 this.type_reference = type_reference;
112 public override void accept (CodeVisitor visitor) {
113 if (use_iterator) {
114 base.accept (visitor);
115 return;
118 visitor.visit_foreach_statement (this);
121 public override void accept_children (CodeVisitor visitor) {
122 if (use_iterator) {
123 base.accept_children (visitor);
124 return;
127 collection.accept (visitor);
128 visitor.visit_end_full_expression (collection);
130 if (type_reference != null) {
131 type_reference.accept (visitor);
134 body.accept (visitor);
137 public override void replace_expression (Expression old_node, Expression new_node) {
138 if (collection == old_node) {
139 collection = new_node;
143 public override void replace_type (DataType old_type, DataType new_type) {
144 if (type_reference == old_type) {
145 type_reference = new_type;
149 public override bool check (CodeContext context) {
150 if (checked) {
151 return !error;
154 checked = true;
156 // analyze collection expression first, used for type inference
157 if (!collection.check (context)) {
158 // ignore inner error
159 error = true;
160 return false;
161 } else if (collection.value_type == null) {
162 Report.error (collection.source_reference, "invalid collection expression");
163 error = true;
164 return false;
167 var collection_type = collection.value_type.copy ();
168 collection.target_type = collection_type.copy ();
170 if (context.profile != Profile.DOVA && collection_type.is_array ()) {
171 var array_type = (ArrayType) collection_type;
173 // can't use inline-allocated array for temporary variable
174 array_type.inline_allocated = false;
176 return check_without_iterator (context, collection_type, array_type.element_type);
177 } else if (context.profile == Profile.GOBJECT && (collection_type.compatible (context.analyzer.glist_type) || collection_type.compatible (context.analyzer.gslist_type))) {
178 if (collection_type.get_type_arguments ().size != 1) {
179 error = true;
180 Report.error (collection.source_reference, "missing type argument for collection");
181 return false;
184 return check_without_iterator (context, collection_type, collection_type.get_type_arguments ().get (0));
185 } else if (context.profile == Profile.GOBJECT && collection_type.compatible (context.analyzer.gvaluearray_type)) {
186 return check_without_iterator (context, collection_type, context.analyzer.gvalue_type);
187 } else {
188 return check_with_iterator (context, collection_type);
192 bool check_with_index (CodeContext context, DataType collection_type) {
193 var get_method = collection_type.get_member ("get") as Method;
194 if (get_method == null) {
195 return false;
197 if (get_method.get_parameters ().size != 1) {
198 return false;
200 var size_property = collection_type.get_member ("size") as Property;
201 if (size_property == null) {
202 return false;
205 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_list".printf (variable_name), collection, source_reference), source_reference));
206 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_size".printf (variable_name), new MemberAccess (new MemberAccess.simple ("_%s_list".printf (variable_name), source_reference), "size", source_reference), source_reference), source_reference));
207 add_statement (new DeclarationStatement (new LocalVariable (null, "_%s_index".printf (variable_name), new UnaryExpression (UnaryOperator.MINUS, new IntegerLiteral ("1", source_reference), source_reference), source_reference), source_reference));
208 var next = new UnaryExpression (UnaryOperator.INCREMENT, new MemberAccess.simple ("_%s_index".printf (variable_name), source_reference), source_reference);
209 var conditional = new BinaryExpression (BinaryOperator.LESS_THAN, next, new MemberAccess.simple ("_%s_size".printf (variable_name), source_reference), source_reference);
210 var loop = new WhileStatement (conditional, body, source_reference);
211 add_statement (loop);
213 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_list".printf (variable_name), source_reference), "get", source_reference), source_reference);
214 get_call.add_argument (new MemberAccess.simple ("_%s_index".printf (variable_name), source_reference));
215 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
217 checked = false;
218 return base.check (context);
221 bool check_with_iterator (CodeContext context, DataType collection_type) {
222 use_iterator = true;
224 if (check_with_index (context, collection_type)) {
225 return true;
228 var iterator_method = collection_type.get_member ("iterator") as Method;
229 if (iterator_method == null) {
230 Report.error (collection.source_reference, "`%s' does not have an `iterator' method".printf (collection_type.to_string ()));
231 error = true;
232 return false;
234 if (iterator_method.get_parameters ().size != 0) {
235 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (iterator_method.get_full_name ()));
236 error = true;
237 return false;
239 var iterator_type = iterator_method.return_type.get_actual_type (collection_type, null, this);
240 if (iterator_type is VoidType) {
241 Report.error (collection.source_reference, "`%s' must return an iterator".printf (iterator_method.get_full_name ()));
242 error = true;
243 return false;
246 var iterator_call = new MethodCall (new MemberAccess (collection, "iterator", source_reference), source_reference);
247 add_statement (new DeclarationStatement (new LocalVariable (iterator_type, "_%s_it".printf (variable_name), iterator_call, source_reference), source_reference));
249 var next_value_method = iterator_type.get_member ("next_value") as Method;
250 var next_method = iterator_type.get_member ("next") as Method;
251 if (next_value_method != null) {
252 if (next_value_method.get_parameters ().size != 0) {
253 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (next_value_method.get_full_name ()));
254 error = true;
255 return false;
257 var element_type = next_value_method.return_type.get_actual_type (iterator_type, null, this);
258 if (!element_type.nullable) {
259 Report.error (collection.source_reference, "return type of `%s' must be nullable".printf (next_value_method.get_full_name ()));
260 error = true;
261 return false;
264 if (!analyze_element_type (element_type)) {
265 return false;
268 add_statement (new DeclarationStatement (new LocalVariable (type_reference, variable_name, null, source_reference), source_reference));
270 var next_value_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next_value", source_reference), source_reference);
271 var assignment = new Assignment (new MemberAccess (null, variable_name, source_reference), next_value_call, AssignmentOperator.SIMPLE, source_reference);
272 var conditional = new BinaryExpression (BinaryOperator.INEQUALITY, assignment, new NullLiteral (source_reference), source_reference);
273 var loop = new WhileStatement (conditional, body, source_reference);
274 add_statement (loop);
275 } else if (next_method != null) {
276 if (next_method.get_parameters ().size != 0) {
277 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (next_method.get_full_name ()));
278 error = true;
279 return false;
281 if (!next_method.return_type.compatible (context.analyzer.bool_type)) {
282 Report.error (collection.source_reference, "`%s' must return a boolean value".printf (next_method.get_full_name ()));
283 error = true;
284 return false;
286 var get_method = iterator_type.get_member ("get") as Method;
287 if (get_method == null) {
288 Report.error (collection.source_reference, "`%s' does not have a `get' method".printf (iterator_type.to_string ()));
289 error = true;
290 return false;
292 if (get_method.get_parameters ().size != 0) {
293 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (get_method.get_full_name ()));
294 error = true;
295 return false;
297 var element_type = get_method.return_type.get_actual_type (iterator_type, null, this);
298 if (element_type is VoidType) {
299 Report.error (collection.source_reference, "`%s' must return an element".printf (get_method.get_full_name ()));
300 error = true;
301 return false;
304 if (!analyze_element_type (element_type)) {
305 return false;
308 var next_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next", source_reference), source_reference);
309 var loop = new WhileStatement (next_call, body, source_reference);
310 add_statement (loop);
312 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "get", source_reference), source_reference);
313 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
314 } else {
315 Report.error (collection.source_reference, "`%s' does not have a `next_value' or `next' method".printf (iterator_type.to_string ()));
316 error = true;
317 return false;
320 checked = false;
321 return base.check (context);
324 bool analyze_element_type (DataType element_type) {
325 // analyze element type
326 if (type_reference == null) {
327 // var type
328 type_reference = element_type.copy ();
329 } else if (!element_type.compatible (type_reference)) {
330 error = true;
331 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
332 return false;
333 } else if (element_type.is_disposable () && element_type.value_owned && !type_reference.value_owned) {
334 error = true;
335 Report.error (source_reference, "Foreach: Invalid assignment from owned expression to unowned variable");
336 return false;
339 return true;
342 bool check_without_iterator (CodeContext context, DataType collection_type, DataType element_type) {
343 // analyze element type
344 if (type_reference == null) {
345 // var type
346 type_reference = element_type.copy ();
347 } else if (!element_type.compatible (type_reference)) {
348 error = true;
349 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
350 return false;
353 element_variable = new LocalVariable (type_reference, variable_name, null, source_reference);
355 body.scope.add (variable_name, element_variable);
357 body.add_local_variable (element_variable);
358 element_variable.active = true;
359 element_variable.checked = true;
361 // analyze body
362 owner = context.analyzer.current_symbol.scope;
363 context.analyzer.current_symbol = this;
365 // call add_local_variable to check for shadowed variable
366 add_local_variable (element_variable);
367 remove_local_variable (element_variable);
369 body.check (context);
371 foreach (LocalVariable local in get_local_variables ()) {
372 local.active = false;
375 context.analyzer.current_symbol = context.analyzer.current_symbol.parent_symbol;
377 collection_variable = new LocalVariable (collection_type, "%s_collection".printf (variable_name));
379 add_local_variable (collection_variable);
380 collection_variable.active = true;
382 add_error_types (collection.get_error_types ());
383 add_error_types (body.get_error_types ());
385 return !error;
388 public override void emit (CodeGenerator codegen) {
389 if (use_iterator) {
390 base.emit (codegen);
391 return;
394 collection.emit (codegen);
395 codegen.visit_end_full_expression (collection);
397 element_variable.active = true;
398 collection_variable.active = true;
399 if (iterator_variable != null) {
400 iterator_variable.active = true;
403 codegen.visit_foreach_statement (this);
406 public override void get_defined_variables (Collection<LocalVariable> collection) {
407 collection.add (element_variable);