1/* valacastexpression.vala
2 *
3 * Copyright (C) 2006-2011  Jürg Billeter
4 *
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.
9
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.
14
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
18 *
19 * Author:
20 * 	Jürg Billeter <j@bitron.ch>
21 */
22
23
24/**
25 * Represents a type cast in the source code.
26 */
27public class Vala.CastExpression : Expression {
28	/**
29	 * The expression to be cast.
30	 */
31	public Expression inner {
32		get {
33			return _inner;
34		}
35		set {
36			_inner = value;
37			_inner.parent_node = this;
38		}
39	}
40
41	/**
42	 * The target type.
43	 */
44	public DataType type_reference {
45		get { return _data_type; }
46		set {
47			_data_type = value;
48			_data_type.parent_node = this;
49		}
50	}
51
52	/**
53	 * Checked casts return NULL instead of raising an error.
54	 */
55	public bool is_silent_cast { get; private set; }
56
57	public bool is_non_null_cast { get; private set; }
58
59	private Expression _inner;
60
61	private DataType _data_type;
62
63	/**
64	 * Creates a new cast expression.
65	 *
66	 * @param inner           expression to be cast
67	 * @param type_reference  target type
68	 * @return                newly created cast expression
69	 */
70	public CastExpression (Expression inner, DataType type_reference, SourceReference? source_reference = null) {
71		this.type_reference = type_reference;
72		this.source_reference = source_reference;
73		this.is_silent_cast = false;
74		this.is_non_null_cast = false;
75		this.inner = inner;
76	}
77
78	public CastExpression.silent (Expression inner, DataType type_reference, SourceReference? source_reference = null) {
79		this.type_reference = type_reference;
80		this.source_reference = source_reference;
81		this.is_silent_cast = true;
82		this.is_non_null_cast = false;
83		this.inner = inner;
84	}
85
86	public CastExpression.non_null (Expression inner, SourceReference? source_reference = null) {
87		this.inner = inner;
88		this.is_non_null_cast = true;
89		this.source_reference = source_reference;
90	}
91
92	public override void accept (CodeVisitor visitor) {
93		visitor.visit_cast_expression (this);
94
95		visitor.visit_expression (this);
96	}
97
98	public override void accept_children (CodeVisitor visitor) {
99		inner.accept (visitor);
100		if (!is_non_null_cast) {
101			type_reference.accept (visitor);
102		}
103	}
104
105	public override string to_string () {
106		if (is_non_null_cast) {
107			return "(!) %s".printf (inner.to_string ());
108		} else if (is_silent_cast) {
109			return "%s as %s".printf (inner.to_string (), type_reference.to_string ());
110		} else {
111			return "(%s) %s".printf (type_reference.to_string (), inner.to_string ());
112		}
113	}
114
115	public override void replace_expression (Expression old_node, Expression new_node) {
116		if (inner == old_node) {
117			inner = new_node;
118		}
119	}
120
121	public override bool is_pure () {
122		return inner.is_pure ();
123	}
124
125	public override bool is_accessible (Symbol sym) {
126		return inner.is_accessible (sym);
127	}
128
129	public override void replace_type (DataType old_type, DataType new_type) {
130		if (type_reference == old_type) {
131			type_reference = new_type;
132		}
133	}
134
135	public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
136		inner.get_error_types (collection, source_reference);
137	}
138
139	public override bool check (CodeContext context) {
140		if (checked) {
141			return !error;
142		}
143
144		checked = true;
145
146		if (!inner.check (context)) {
147			error = true;
148			return false;
149		}
150
151		if (inner.value_type == null) {
152			Report.error (source_reference, "Invalid cast expression");
153			error = true;
154			return false;
155		}
156
157		if (is_non_null_cast) {
158			// (!) non-null cast
159			type_reference = inner.value_type.copy ();
160			type_reference.nullable = false;
161		}
162
163		type_reference.check (context);
164
165		// FIXME: check whether cast is allowed
166
167		if (type_reference is DelegateType && inner.value_type is MethodType) {
168			if (target_type != null) {
169				inner.value_type.value_owned = target_type.value_owned;
170			} else {
171				inner.value_type.value_owned = true;
172			}
173		}
174
175		// Implicit transformation of stack-allocated value to heap-allocated boxed-type
176		if (!(is_silent_cast || is_non_null_cast)
177		    && (type_reference is ValueType && type_reference.nullable)
178		    && inner.value_type.is_non_null_simple_type ()) {
179			var local = new LocalVariable (type_reference, get_temp_name (), null, inner.source_reference);
180			var decl = new DeclarationStatement (local, source_reference);
181
182			insert_statement (context.analyzer.insert_block, decl);
183
184			var temp_access = SemanticAnalyzer.create_temp_access (local, target_type);
185			temp_access.formal_target_type = formal_target_type;
186
187			// don't set initializer earlier as this changes parent_node and parent_statement
188			local.initializer = inner;
189			decl.check (context);
190
191			context.analyzer.replaced_nodes.add (this);
192			parent_node.replace_expression (this, temp_access);
193			return temp_access.check (context);
194		}
195
196		value_type = type_reference;
197		value_type.value_owned = inner.value_type.value_owned;
198		value_type.floating_reference = inner.value_type.floating_reference;
199
200		if (is_silent_cast) {
201			value_type.nullable = true;
202		}
203
204		if (context.profile == Profile.GOBJECT
205		    && is_gvariant (context, inner.value_type) && !is_gvariant (context, value_type)) {
206			// GVariant unboxing returns owned value
207			value_type.value_owned = true;
208			if (value_type.get_type_signature () == null) {
209				error = true;
210				Report.error (source_reference, "Casting of `GLib.Variant' to `%s' is not supported".printf (value_type.to_qualified_string ()));
211			}
212		}
213
214		if (context.profile == Profile.GOBJECT
215		    && is_gvalue (context, inner.value_type) && !is_gvalue (context, value_type)) {
216			// GValue unboxing returns unowned value
217			value_type.value_owned = false;
218			if (value_type.nullable && value_type.type_symbol != null && !value_type.type_symbol.is_reference_type ()) {
219				error = true;
220				Report.error (source_reference, "Casting of `GLib.Value' to `%s' is not supported".printf (value_type.to_qualified_string ()));
221			}
222		}
223
224		inner.target_type = inner.value_type.copy ();
225
226		return !error;
227	}
228
229	bool is_gvariant (CodeContext context, DataType type) {
230		return type.type_symbol != null && type.type_symbol.is_subtype_of (context.analyzer.gvariant_type.type_symbol);
231	}
232
233	bool is_gvalue (CodeContext context, DataType type) {
234		return type.type_symbol != null && type.type_symbol.is_subtype_of (context.analyzer.gvalue_type.type_symbol);
235	}
236
237	public override void emit (CodeGenerator codegen) {
238		inner.emit (codegen);
239
240		codegen.visit_cast_expression (this);
241
242		codegen.visit_expression (this);
243	}
244
245	public override void get_defined_variables (Collection<Variable> collection) {
246		inner.get_defined_variables (collection);
247	}
248
249	public override void get_used_variables (Collection<Variable> collection) {
250		inner.get_used_variables (collection);
251	}
252
253	public override bool is_constant () {
254		return inner.is_constant ();
255	}
256}
257