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
20 * Jürg Billeter <j@bitron.ch>
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
{
31 * Specifies the element type.
33 public DataType? type_reference
{
34 get { return _data_type
; }
37 if (_data_type
!= null) {
38 _data_type
.parent_node
= this
;
44 * Specifies the element variable name.
46 public string variable_name
{ get; set; }
49 * Specifies the container.
51 public Expression collection
{
57 _collection
.parent_node
= this
;
62 * Specifies the loop body.
70 _body
.parent_node
= this
;
74 public bool use_iterator
{ get; private set; }
77 * Specifies the declarator for the generated element variable.
79 public LocalVariable element_variable
{ get; set; }
82 * Specifies the declarator for the generated collection variable.
84 public LocalVariable collection_variable
{ get; set; }
87 * Specifies the declarator for the generated iterator variable.
89 public LocalVariable iterator_variable
{ get; set; }
91 private Expression _collection
;
94 private DataType _data_type
;
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
;
110 this
.type_reference
= type_reference
;
113 public override void accept (CodeVisitor visitor
) {
115 base.accept (visitor
);
119 visitor
.visit_foreach_statement (this
);
122 public override void accept_children (CodeVisitor visitor
) {
124 base.accept_children (visitor
);
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
) {
157 // analyze collection expression first, used for type inference
158 if (!collection
.check (analyzer
)) {
159 // ignore inner error
162 } else if (collection
.value_type
== null) {
163 Report
.error (collection
.source_reference
, "invalid collection expression");
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) {
178 Report
.error (collection
.source_reference
, "missing type argument for collection");
182 return check_without_iterator (analyzer
, collection_type
, collection_type
.get_type_arguments ().get (0));
184 return check_with_iterator (analyzer
, collection_type
);
188 bool check_with_iterator (SemanticAnalyzer analyzer
, DataType collection_type
) {
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
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 ()));
242 // analyze element type
243 if (type_reference
== null) {
245 type_reference
= element_type
.copy ();
246 } else if (!element_type
.compatible (type_reference
)) {
248 Report
.error (source_reference
, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type
.to_string (), type_reference
.to_string ()));
250 } else if (element_type
.is_disposable () && element_type
.value_owned
&& !type_reference
.value_owned
) {
252 Report
.error (source_reference
, "Foreach: Invalid assignment from owned expression to unowned variable");
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
));
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) {
274 type_reference
= element_type
.copy ();
275 } else if (!element_type
.compatible (type_reference
)) {
277 Report
.error (source_reference
, "Foreach: Cannot convert from `%s' to `%s'".printf (element_type
.to_string (), type_reference
.to_string ()));
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;
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 ());
314 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
315 collection
.add (element_variable
);