1/* valaarraycreationexpression.vala
2 *
3 * Copyright (C) 2006-2010  Jürg Billeter
4 * Copyright (C) 2006-2008  Raffaele Sandrini
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19 *
20 * Author:
21 * 	Raffaele Sandrini <raffaele@sandrini.ch>
22 * 	Jürg Billeter <j@bitron.ch>
23 */
24
25using GLib;
26
27/**
28 * Represents an array creation expression.
29 *
30 * {{{ new int[] { 1, 2, 3 } }}}
31 */
32public class Vala.ArrayCreationExpression : Expression {
33	/**
34	 * The type of the elements of the array.
35	 */
36	public DataType element_type {
37		get { return _element_type; }
38		set {
39			_element_type = value;
40			_element_type.parent_node = this;
41		}
42	}
43
44	/**
45	 * The length type.
46	 */
47	public DataType? length_type {
48		get { return _length_type; }
49		set {
50			_length_type = value;
51			if (_length_type != null) {
52				_length_type.parent_node = this;
53			}
54		}
55	}
56
57	/**
58	 * The rank of the array.
59	 */
60	public int rank { get; set; }
61
62	/**
63	 * The size for each dimension ascending from left to right.
64	 */
65	private List<Expression> sizes = new ArrayList<Expression> ();
66
67	/**
68	 * The root array initializer list.
69	 */
70	public InitializerList? initializer_list {
71		get { return _initializer_list; }
72		set {
73			_initializer_list = value;
74			if (_initializer_list != null) {
75				_initializer_list.parent_node = this;
76			}
77		}
78	}
79
80	private DataType _element_type;
81	private DataType _length_type;
82	private InitializerList? _initializer_list;
83
84	/**
85	 * Add a size expression.
86	 */
87	public void append_size (Expression size) {
88		sizes.add (size);
89		if (size != null) {
90			size.parent_node = this;
91		}
92	}
93
94	/**
95	 * Get the sizes for all dimensions ascending from left to right.
96	 */
97	public unowned List<Expression> get_sizes () {
98		return sizes;
99	}
100
101	public ArrayCreationExpression (DataType element_type, int rank, InitializerList? initializer_list, SourceReference? source_reference = null) {
102		this.element_type = element_type;
103		this.rank = rank;
104		this.initializer_list = initializer_list;
105		this.source_reference = source_reference;
106	}
107
108	public override void accept_children (CodeVisitor visitor) {
109		if (element_type != null) {
110			element_type.accept (visitor);
111		}
112
113		if (length_type != null) {
114			length_type.accept (visitor);
115		}
116
117		foreach (Expression e in sizes) {
118			e.accept (visitor);
119		}
120
121		if (initializer_list != null) {
122			initializer_list.accept (visitor);
123		}
124	}
125
126	public override void accept (CodeVisitor visitor) {
127		visitor.visit_array_creation_expression (this);
128
129		visitor.visit_expression (this);
130	}
131
132	public override bool is_pure () {
133		return false;
134	}
135
136	public override bool is_accessible (Symbol sym) {
137		if (element_type != null && !element_type.is_accessible (sym)) {
138			return false;
139		}
140
141		if (length_type != null && !length_type.is_accessible (sym)) {
142			return false;
143		}
144
145		foreach (Expression e in sizes) {
146			if (!e.is_accessible (sym)) {
147				return false;
148			}
149		}
150
151		if (initializer_list != null) {
152			return initializer_list.is_accessible (sym);
153		}
154
155		return true;
156	}
157
158	public override string to_string () {
159		var builder = new StringBuilder ("new ");
160		builder.append_printf ("%s[", element_type.to_string ());
161		bool first = true;
162		foreach (var size in sizes) {
163			if (first) {
164				builder.append (size.to_string ());
165				first = false;
166			} else {
167				builder.append_printf (", %s", size.to_string ());
168			}
169		}
170		builder.append_c (']');
171		if (initializer_list != null) {
172			builder.append (initializer_list.to_string ());
173		}
174		return builder.str;
175	}
176
177	public override void replace_expression (Expression old_node, Expression new_node) {
178		for (int i = 0; i < sizes.size; i++) {
179			if (sizes[i] == old_node) {
180				sizes[i] = new_node;
181				new_node.parent_node = this;
182				return;
183			}
184		}
185	}
186
187	public override void replace_type (DataType old_type, DataType new_type) {
188		if (element_type == old_type) {
189			element_type = new_type;
190		}
191		if (length_type == old_type) {
192			length_type = new_type;
193		}
194	}
195
196	private int create_sizes_from_initializer_list (CodeContext context, InitializerList il, int rank, List<Literal> sl) {
197		if (sl.size == (this.rank - rank)) {
198			// only add size if this is the first initializer list of the current dimension
199			var init = new IntegerLiteral (il.size.to_string (), il.source_reference);
200			init.check (context);
201			sl.add (init);
202		}
203
204		int subsize = -1;
205		foreach (Expression e in il.get_initializers ()) {
206			if (e is InitializerList && e.target_type is ArrayType) {
207				if (rank == 1) {
208					il.error = true;
209					e.error = true;
210					Report.error (e.source_reference, "Expected array element, got array initializer list");
211					return -1;
212				}
213				int size = create_sizes_from_initializer_list (context, (InitializerList) e, rank - 1, sl);
214				if (size == -1) {
215					return -1;
216				}
217				if (subsize >= 0 && subsize != size) {
218					il.error = true;
219					Report.error (il.source_reference, "Expected initializer list of size %d, got size %d".printf (subsize, size));
220					return -1;
221				} else {
222					subsize = size;
223				}
224			} else {
225				if (rank != 1) {
226					il.error = true;
227					e.error = true;
228					Report.error (e.source_reference, "Expected array initializer list, got array element");
229					return -1;
230				}
231			}
232		}
233		return il.size;
234	}
235
236	public override bool check (CodeContext context) {
237		if (checked) {
238			return !error;
239		}
240
241		checked = true;
242
243		List<Expression> sizes = get_sizes ();
244		var initlist = initializer_list;
245
246		if (element_type != null) {
247			element_type.check (context);
248
249			// check whether there is the expected amount of type-arguments
250			if (!element_type.check_type_arguments (context, true)) {
251				error = true;
252				return false;
253			}
254		}
255
256		if (length_type == null) {
257			// Make sure that "int" is still picked up as default
258			length_type = context.analyzer.int_type.copy ();
259		} else {
260			length_type.check (context);
261			if (!(length_type is IntegerType) || length_type.nullable) {
262				error = true;
263				Report.error (length_type.source_reference, "Expected integer type as length type of array");
264			}
265		}
266
267		foreach (Expression e in sizes) {
268			e.check (context);
269		}
270
271		var calc_sizes = new ArrayList<Literal> ();
272		if (initlist != null) {
273			initlist.target_type = new ArrayType (element_type, rank, source_reference);
274			((ArrayType) initlist.target_type).length_type = length_type.copy ();
275
276			if (!initlist.check (context)) {
277				error = true;
278			}
279
280			var ret = create_sizes_from_initializer_list (context, initlist, rank, calc_sizes);
281			if (ret == -1) {
282				error = true;
283			}
284
285			if (calc_sizes.size != rank) {
286				error = true;
287				var actual_type = new ArrayType (element_type, calc_sizes.size, source_reference);
288				((ArrayType) actual_type).length_type = length_type;
289				Report.error (initlist.source_reference, "Expected initializer for `%s' but got `%s'".printf (target_type.to_string (), actual_type.to_string ()));
290			}
291		}
292
293		if (sizes.size > 0) {
294			/* check for errors in the size list */
295			foreach (Expression e in sizes) {
296				if (e.value_type == null) {
297					/* return on previous error */
298					return false;
299				} else if (!(e.value_type is IntegerType || e.value_type is EnumValueType)) {
300					error = true;
301					Report.error (e.source_reference, "Expression of integer type expected");
302				}
303			}
304		} else {
305			if (initlist == null) {
306				error = true;
307				/* this is an internal error because it is already handled by the parser */
308				Report.error (source_reference, "internal error: initializer list expected");
309			} else {
310				foreach (Expression size in calc_sizes) {
311					append_size (size);
312				}
313			}
314		}
315
316		if (error) {
317			return false;
318		}
319
320		/* check for wrong elements inside the initializer */
321		if (initializer_list != null && initializer_list.value_type == null) {
322			return false;
323		}
324
325		/* try to construct the type of the array */
326		if (element_type == null) {
327			error = true;
328			Report.error (source_reference, "Cannot determine the element type of the created array");
329			return false;
330		}
331
332		value_type = new ArrayType (element_type, rank, source_reference);
333		((ArrayType) value_type).length_type = length_type.copy ();
334		if (formal_target_type is ArrayType) {
335			((ArrayType) value_type).fixed_length = ((ArrayType) formal_target_type).fixed_length;
336			((ArrayType) value_type).inline_allocated = ((ArrayType) formal_target_type).inline_allocated;
337		}
338		value_type.value_owned = true;
339
340		if (!value_type.check (context)) {
341			return false;
342		}
343
344		return !error;
345	}
346
347	public override void emit (CodeGenerator codegen) {
348		foreach (Expression e in sizes) {
349			e.emit (codegen);
350		}
351
352		if (initializer_list != null) {
353			initializer_list.emit (codegen);
354		}
355
356		codegen.visit_array_creation_expression (this);
357
358		codegen.visit_expression (this);
359	}
360
361	public override void get_used_variables (Collection<Variable> collection) {
362		foreach (Expression e in sizes) {
363			e.get_used_variables (collection);
364		}
365
366		if (initializer_list != null) {
367			initializer_list.get_used_variables (collection);
368		}
369	}
370}
371