Line data Source code
1 : /* valaexpression.vala
2 : *
3 : * Copyright (C) 2006-2010 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 : using GLib;
24 :
25 : /**
26 : * Base class for all code nodes that might be used as an expression.
27 : */
28 8903884 : public abstract class Vala.Expression : CodeNode {
29 : /**
30 : * The static type of the value of this expression.
31 : *
32 : * The semantic analyzer computes this value.
33 : */
34 38739911 : public DataType value_type { get; set; }
35 :
36 10717896 : public DataType? formal_value_type { get; set; }
37 :
38 : /*
39 : * The static type this expression is expected to have.
40 : *
41 : * The semantic analyzer computes this value, lambda expressions use it.
42 : */
43 14094680 : public DataType target_type { get; set; }
44 :
45 6260190 : public DataType? formal_target_type { get; set; }
46 :
47 : /**
48 : * The symbol this expression refers to.
49 : */
50 63457513 : public weak Symbol symbol_reference { get; set; }
51 :
52 : /**
53 : * Specifies that this expression is used as lvalue, i.e. the
54 : * left hand side of an assignment.
55 : */
56 2338737 : public bool lvalue { get; set; }
57 :
58 7005504 : public TargetValue? target_value { get; set; }
59 :
60 : /**
61 : * Returns whether this expression is constant, i.e. whether this
62 : * expression only consists of literals and other constants.
63 : */
64 32366 : public virtual bool is_constant () {
65 11 : return false;
66 : }
67 :
68 : /**
69 : * Returns whether this expression is pure, i.e. whether this expression
70 : * is free of side-effects.
71 : */
72 0 : public abstract bool is_pure ();
73 :
74 : /**
75 : * Returns whether this expression is guaranteed to be non-null.
76 : */
77 148504 : public virtual bool is_non_null () {
78 28591 : return false;
79 : }
80 :
81 : /**
82 : * Check whether symbol_references in this expression are at least
83 : * as accessible as the specified symbol.
84 : */
85 2129592 : public virtual bool is_accessible (Symbol sym) {
86 : return true;
87 : }
88 :
89 : /**
90 : * Check whether this expression is always true.
91 : */
92 109310 : public bool is_always_true () {
93 109310 : unowned BooleanLiteral? literal = this as BooleanLiteral;
94 50 : return (literal != null && literal.value);
95 : }
96 :
97 : /**
98 : * Check whether this expression is always false.
99 : */
100 109274 : public bool is_always_false () {
101 109274 : unowned BooleanLiteral? literal = this as BooleanLiteral;
102 19 : return (literal != null && !literal.value);
103 : }
104 :
105 : public Statement? parent_statement {
106 134155 : get {
107 134155 : if (parent_node is MemberInitializer) {
108 2 : return ((Expression) parent_node.parent_node).parent_statement;
109 134153 : } else if (parent_node is LocalVariable) {
110 3173 : return (Statement) parent_node.parent_node;
111 130980 : } else if (parent_node is Statement) {
112 90052 : return (Statement) parent_node;
113 40928 : } else if (parent_node is Expression) {
114 40928 : return ((Expression) parent_node).parent_statement;
115 : } else {
116 0 : return null;
117 : }
118 : }
119 : }
120 :
121 93225 : public void insert_statement (Block block, Statement stmt) {
122 93225 : block.insert_before (parent_statement, stmt);
123 : }
124 :
125 25 : public override bool check (CodeContext context) {
126 : //Add null checks to a null-safe expression
127 :
128 25 : unowned MethodCall? call = this as MethodCall;
129 7 : unowned Expression access = call != null ? call.call : this;
130 25 : unowned MemberAccess? member_access = access as MemberAccess;
131 25 : unowned ElementAccess? elem_access = access as ElementAccess;
132 25 : unowned SliceExpression? slice_expr = access as SliceExpression;
133 :
134 25 : unowned Expression? inner = null;
135 25 : if (member_access != null && member_access.null_safe_access) {
136 20 : inner = member_access.inner;
137 5 : } else if (elem_access != null && elem_access.null_safe_access) {
138 3 : inner = elem_access.container;
139 2 : } else if (slice_expr != null && slice_expr.null_safe_access) {
140 2 : inner = slice_expr.container;
141 : } else {
142 : // Nothing to do here
143 0 : return true;
144 : }
145 :
146 : // get the type of the inner expression
147 25 : if (!inner.check (context)) {
148 0 : error = true;
149 0 : return false;
150 : }
151 :
152 : // the inner expression may have been replaced by the check, reload it
153 25 : if (member_access != null) {
154 20 : inner = member_access.inner;
155 5 : } else if (elem_access != null) {
156 3 : inner = elem_access.container;
157 2 : } else if (slice_expr != null) {
158 2 : inner = slice_expr.container;
159 : }
160 :
161 25 : if (inner.value_type == null) {
162 0 : Report.error (inner.source_reference, "invalid inner expression");
163 0 : return false;
164 : }
165 :
166 : // declare the inner expression as a local variable to check for null
167 25 : var inner_type = inner.value_type.copy ();
168 25 : if (context.experimental_non_null && !inner_type.nullable) {
169 0 : Report.warning (inner.source_reference, "inner expression is never null");
170 : // make it nullable, otherwise the null check will not compile in non-null mode
171 0 : inner_type.nullable = true;
172 : }
173 25 : var inner_local = new LocalVariable (inner_type, get_temp_name (), inner, inner.source_reference);
174 :
175 25 : var inner_decl = new DeclarationStatement (inner_local, inner.source_reference);
176 25 : insert_statement (context.analyzer.insert_block, inner_decl);
177 :
178 25 : if (!inner_decl.check (context)) {
179 0 : return false;
180 : }
181 :
182 : // create an equivalent non null-safe expression
183 25 : Expression? non_null_expr = null;
184 :
185 25 : Expression inner_access = new MemberAccess.simple (inner_local.name, source_reference);
186 :
187 25 : if (context.experimental_non_null) {
188 0 : inner_access = new CastExpression.non_null (inner_access, source_reference);
189 : }
190 :
191 25 : if (member_access != null) {
192 20 : non_null_expr = new MemberAccess (inner_access, member_access.member_name, source_reference);
193 5 : } else if (elem_access != null) {
194 3 : var non_null_elem_access = new ElementAccess (inner_access, source_reference);
195 9 : foreach (Expression index in elem_access.get_indices ()) {
196 3 : non_null_elem_access.append_index (index);
197 : }
198 6 : non_null_expr = non_null_elem_access;
199 2 : } else if (slice_expr != null) {
200 2 : non_null_expr = new SliceExpression (inner_access, slice_expr.start, slice_expr.stop, source_reference);
201 : }
202 :
203 25 : if ((member_access != null || elem_access != null)
204 23 : && access.parent_node is ReferenceTransferExpression) {
205 : // preserve ownership transfer
206 2 : non_null_expr = new ReferenceTransferExpression (non_null_expr, source_reference);
207 : }
208 :
209 25 : if (!non_null_expr.check (context)) {
210 0 : return false;
211 : }
212 :
213 25 : if (non_null_expr.value_type == null) {
214 0 : Report.error (source_reference, "invalid null-safe expression");
215 0 : error = true;
216 0 : return false;
217 : }
218 :
219 : DataType result_type;
220 :
221 32 : if (call != null) {
222 : // if the expression is a method call, create an equivalent non-conditional method call
223 7 : var non_null_call = new MethodCall (non_null_expr, source_reference);
224 9 : foreach (Expression arg in call.get_argument_list ()) {
225 1 : non_null_call.add_argument (arg);
226 : }
227 7 : result_type = non_null_expr.value_type.get_return_type ().copy ();
228 7 : non_null_expr = non_null_call;
229 : } else {
230 18 : result_type = non_null_expr.value_type.copy ();
231 : }
232 :
233 25 : if (result_type is VoidType) {
234 : // void result type, replace the parent expression statement by a conditional statement
235 1 : var non_null_stmt = new ExpressionStatement (non_null_expr, source_reference);
236 1 : var non_null_block = new Block (source_reference);
237 1 : non_null_block.add_statement (non_null_stmt);
238 :
239 1 : var non_null_safe = new BinaryExpression (BinaryOperator.INEQUALITY, new MemberAccess.simple (inner_local.name, source_reference), new NullLiteral (source_reference), source_reference);
240 1 : var non_null_ifstmt = new IfStatement (non_null_safe, non_null_block, null, source_reference);
241 :
242 1 : unowned ExpressionStatement? parent_stmt = parent_node as ExpressionStatement;
243 1 : unowned Block? parent_block = parent_stmt != null ? parent_stmt.parent_node as Block : null;
244 :
245 1 : if (parent_stmt == null || parent_block == null) {
246 0 : Report.error (source_reference, "void method call not allowed here");
247 0 : error = true;
248 0 : return false;
249 : }
250 :
251 1 : context.analyzer.replaced_nodes.add (parent_stmt);
252 1 : parent_block.replace_statement (parent_stmt, non_null_ifstmt);
253 1 : return non_null_ifstmt.check (context);
254 : } else {
255 : // non-void result type, replace the expression by an access to a local variable
256 24 : if (!result_type.nullable) {
257 20 : if (result_type is ValueType) {
258 : // the value must be owned, otherwise the local variable may receive a stale pointer to the stack
259 15 : result_type.value_owned = true;
260 : }
261 20 : result_type.nullable = true;
262 : }
263 24 : var result_local = new LocalVariable (result_type, get_temp_name (), new NullLiteral (source_reference), source_reference);
264 :
265 24 : var result_decl = new DeclarationStatement (result_local, source_reference);
266 24 : insert_statement (context.analyzer.insert_block, result_decl);
267 :
268 24 : if (!result_decl.check (context)) {
269 0 : return false;
270 : }
271 :
272 : // assign the non-conditional member access if the inner expression is not null
273 24 : var non_null_safe = new BinaryExpression (BinaryOperator.INEQUALITY, new MemberAccess.simple (inner_local.name, source_reference), new NullLiteral (source_reference), source_reference);
274 24 : var non_null_stmt = new ExpressionStatement (new Assignment (new MemberAccess.simple (result_local.name, source_reference), non_null_expr, AssignmentOperator.SIMPLE, source_reference), source_reference);
275 24 : var non_null_block = new Block (source_reference);
276 24 : non_null_block.add_statement (non_null_stmt);
277 24 : var non_null_ifstmt = new IfStatement (non_null_safe, non_null_block, null, source_reference);
278 24 : insert_statement (context.analyzer.insert_block, non_null_ifstmt);
279 :
280 24 : if (!non_null_ifstmt.check (context)) {
281 0 : return false;
282 : }
283 :
284 24 : var result_access = SemanticAnalyzer.create_temp_access (result_local, target_type);
285 24 : context.analyzer.replaced_nodes.add (this);
286 24 : parent_node.replace_expression (this, result_access);
287 :
288 24 : if (lvalue) {
289 3 : if (non_null_expr is ReferenceTransferExpression) {
290 : // ownership can be transferred transitively
291 2 : result_access.lvalue = true;
292 : } else {
293 1 : Report.error (source_reference, "null-safe expression not supported as lvalue");
294 1 : error = true;
295 1 : return false;
296 : }
297 : }
298 :
299 23 : return result_access.check (context);
300 : }
301 : }
302 : }
|