Warn when using result variable with incompatible type to prepare possible
[vala-lang.git] / vala / valaforeachstatement.vala
blobf69f38fed0a6b19b97aed54f47cbf50b7eff017a
1 /* valaforeachstatement.vala
3 * Copyright (C) 2006-2008 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 Gee;
25 /**
26 * Represents a foreach statement in the source code. Foreach statements iterate
27 * over the elements of a collection.
29 public class Vala.ForeachStatement : Block {
30 /**
31 * Specifies the element type.
33 public DataType? type_reference {
34 get { return _data_type; }
35 set {
36 _data_type = value;
37 if (_data_type != null) {
38 _data_type.parent_node = this;
43 /**
44 * Specifies the element variable name.
46 public string variable_name { get; set; }
48 /**
49 * Specifies the container.
51 public Expression collection {
52 get {
53 return _collection;
55 set {
56 _collection = value;
57 _collection.parent_node = this;
61 /**
62 * Specifies the loop body.
64 public Block body {
65 get {
66 return _body;
68 set {
69 _body = value;
70 _body.parent_node = this;
74 public bool use_iterator { get; private set; }
76 /**
77 * Specifies the declarator for the generated element variable.
79 public LocalVariable element_variable { get; set; }
81 /**
82 * Specifies the declarator for the generated collection variable.
84 public LocalVariable collection_variable { get; set; }
86 /**
87 * Specifies the declarator for the generated iterator variable.
89 public LocalVariable iterator_variable { get; set; }
91 private Expression _collection;
92 private Block _body;
94 private DataType _data_type;
96 /**
97 * Creates a new foreach statement.
99 * @param type element type
100 * @param id element variable name
101 * @param col loop body
102 * @param source reference to source code
103 * @return newly created foreach statement
105 public ForeachStatement (DataType? type_reference, string variable_name, Expression collection, Block body, SourceReference source_reference) {
106 base (source_reference);
107 this.variable_name = variable_name;
108 this.collection = collection;
109 this.body = body;
110 this.type_reference = type_reference;
113 public override void accept (CodeVisitor visitor) {
114 if (use_iterator) {
115 base.accept (visitor);
116 return;
119 visitor.visit_foreach_statement (this);
122 public override void accept_children (CodeVisitor visitor) {
123 if (use_iterator) {
124 base.accept_children (visitor);
125 return;
128 collection.accept (visitor);
129 visitor.visit_end_full_expression (collection);
131 if (type_reference != null) {
132 type_reference.accept (visitor);
135 body.accept (visitor);
138 public override void replace_expression (Expression old_node, Expression new_node) {
139 if (collection == old_node) {
140 collection = new_node;
144 public override void replace_type (DataType old_type, DataType new_type) {
145 if (type_reference == old_type) {
146 type_reference = new_type;
150 public override bool check (SemanticAnalyzer analyzer) {
151 if (checked) {
152 return !error;
155 checked = true;
157 // analyze collection expression first, used for type inference
158 if (!collection.check (analyzer)) {
159 // ignore inner error
160 error = true;
161 return false;
162 } else if (collection.value_type == null) {
163 Report.error (collection.source_reference, "invalid collection expression");
164 error = true;
165 return false;
168 var collection_type = collection.value_type.copy ();
169 collection.target_type = collection_type.copy ();
171 if (collection_type.is_array ()) {
172 var array_type = (ArrayType) collection_type;
174 return check_without_iterator (analyzer, collection_type, array_type.element_type);
175 } else if (collection_type.compatible (analyzer.glist_type) || collection_type.compatible (analyzer.gslist_type)) {
176 if (collection_type.get_type_arguments ().size != 1) {
177 error = true;
178 Report.error (collection.source_reference, "missing type argument for collection");
179 return false;
182 return check_without_iterator (analyzer, collection_type, collection_type.get_type_arguments ().get (0));
183 } else {
184 return check_with_iterator (analyzer, collection_type);
188 bool check_with_iterator (SemanticAnalyzer analyzer, DataType collection_type) {
189 use_iterator = true;
191 var iterator_method = collection_type.get_member ("iterator") as Method;
192 if (iterator_method == null) {
193 Report.error (collection.source_reference, "`%s' does not have an `iterator' method".printf (collection_type.to_string ()));
194 error = true;
195 return false;
197 if (iterator_method.get_parameters ().size != 0) {
198 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (iterator_method.get_full_name ()));
199 error = true;
200 return false;
202 var iterator_type = iterator_method.return_type.get_actual_type (collection_type, this);
203 if (iterator_type is VoidType) {
204 Report.error (collection.source_reference, "`%s' must return an iterator".printf (iterator_method.get_full_name ()));
205 error = true;
206 return false;
208 var next_method = iterator_type.get_member ("next") as Method;
209 if (next_method == null) {
210 Report.error (collection.source_reference, "`%s' does not have a `next' method".printf (iterator_type.to_string ()));
211 error = true;
212 return false;
214 if (next_method.get_parameters ().size != 0) {
215 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (next_method.get_full_name ()));
216 error = true;
217 return false;
219 if (!next_method.return_type.compatible (analyzer.bool_type)) {
220 Report.error (collection.source_reference, "`%s' must return a boolean value".printf (next_method.get_full_name ()));
221 error = true;
222 return false;
224 var get_method = iterator_type.get_member ("get") as Method;
225 if (get_method == null) {
226 Report.error (collection.source_reference, "`%s' does not have a `get' method".printf (iterator_type.to_string ()));
227 error = true;
228 return false;
230 if (get_method.get_parameters ().size != 0) {
231 Report.error (collection.source_reference, "`%s' must not have any parameters".printf (get_method.get_full_name ()));
232 error = true;
233 return false;
235 var element_type = get_method.return_type.get_actual_type (iterator_type, this);
236 if (element_type is VoidType) {
237 Report.error (collection.source_reference, "`%s' must return an element".printf (get_method.get_full_name ()));
238 error = true;
239 return false;
242 // analyze element type
243 if (type_reference == null) {
244 // var type
245 type_reference = element_type.copy ();
246 } else if (!element_type.compatible (type_reference)) {
247 error = true;
248 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
249 return false;
250 } else if (element_type.is_disposable () && element_type.value_owned && !type_reference.value_owned) {
251 error = true;
252 Report.error (source_reference, "Foreach: Invalid assignment from owned expression to unowned variable");
253 return false;
256 var iterator_call = new MethodCall (new MemberAccess (collection, "iterator"));
257 add_statement (new DeclarationStatement (new LocalVariable (iterator_type, "_%s_it".printf (variable_name), iterator_call, source_reference), source_reference));
259 var next_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "next", source_reference), source_reference);
260 var loop = new WhileStatement (next_call, body, source_reference);
261 add_statement (loop);
263 var get_call = new MethodCall (new MemberAccess (new MemberAccess.simple ("_%s_it".printf (variable_name), source_reference), "get", source_reference), source_reference);
264 body.insert_statement (0, new DeclarationStatement (new LocalVariable (type_reference, variable_name, get_call, source_reference), source_reference));
266 checked = false;
267 return base.check (analyzer);
270 bool check_without_iterator (SemanticAnalyzer analyzer, DataType collection_type, DataType element_type) {
271 // analyze element type
272 if (type_reference == null) {
273 // var type
274 type_reference = element_type.copy ();
275 } else if (!element_type.compatible (type_reference)) {
276 error = true;
277 Report.error (source_reference, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type.to_string (), type_reference.to_string ()));
278 return false;
281 analyzer.current_source_file.add_type_dependency (type_reference, SourceFileDependencyType.SOURCE);
283 element_variable = new LocalVariable (type_reference, variable_name);
285 body.scope.add (variable_name, element_variable);
287 body.add_local_variable (element_variable);
288 element_variable.active = true;
289 element_variable.checked = true;
291 // analyze body
292 owner = analyzer.current_symbol.scope;
293 analyzer.current_symbol = this;
295 body.check (analyzer);
297 foreach (LocalVariable local in get_local_variables ()) {
298 local.active = false;
301 analyzer.current_symbol = analyzer.current_symbol.parent_symbol;
303 collection_variable = new LocalVariable (collection_type, "%s_collection".printf (variable_name));
305 add_local_variable (collection_variable);
306 collection_variable.active = true;
308 add_error_types (collection.get_error_types ());
309 add_error_types (body.get_error_types ());
311 return !error;
314 public override void get_defined_variables (Collection<LocalVariable> collection) {
315 collection.add (element_variable);