Warn when using result variable with incompatible type to prepare possible
[vala-lang.git] / vala / valaunaryexpression.vala
blobe51777b03d44d5de13935224e78780c1b4d36173
1 /* valaunaryexpression.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 an expression with one operand in the source code.
28 * Supports +, -, !, ~, ref, out.
30 public class Vala.UnaryExpression : Expression {
31 /**
32 * The unary operator.
34 public UnaryOperator operator { get; set; }
36 /**
37 * The operand.
39 public Expression inner {
40 get {
41 return _inner;
43 set {
44 _inner = value;
45 _inner.parent_node = this;
49 private Expression _inner;
51 /**
52 * Creates a new unary expression.
54 * @param op unary operator
55 * @param inner operand
56 * @param source reference to source code
57 * @return newly created binary expression
59 public UnaryExpression (UnaryOperator op, Expression _inner, SourceReference source) {
60 operator = op;
61 inner = _inner;
62 source_reference = source;
65 public override void accept (CodeVisitor visitor) {
66 visitor.visit_unary_expression (this);
68 visitor.visit_expression (this);
71 public override void accept_children (CodeVisitor visitor) {
72 inner.accept (visitor);
75 public override void replace_expression (Expression old_node, Expression new_node) {
76 if (inner == old_node) {
77 inner = new_node;
81 private string get_operator_string () {
82 switch (_operator) {
83 case UnaryOperator.PLUS: return "+";
84 case UnaryOperator.MINUS: return "-";
85 case UnaryOperator.LOGICAL_NEGATION: return "!";
86 case UnaryOperator.BITWISE_COMPLEMENT: return "~";
87 case UnaryOperator.INCREMENT: return "++";
88 case UnaryOperator.DECREMENT: return "--";
89 case UnaryOperator.REF: return "ref ";
90 case UnaryOperator.OUT: return "out ";
93 assert_not_reached ();
96 public override string to_string () {
97 return get_operator_string () + _inner.to_string ();
100 public override bool is_pure () {
101 if (operator == UnaryOperator.INCREMENT || operator == UnaryOperator.DECREMENT) {
102 return false;
105 return inner.is_pure ();
108 bool is_numeric_type (DataType type) {
109 if (!(type.data_type is Struct)) {
110 return false;
113 var st = (Struct) type.data_type;
114 return st.is_integer_type () || st.is_floating_type ();
117 bool is_integer_type (DataType type) {
118 if (!(type.data_type is Struct)) {
119 return false;
122 var st = (Struct) type.data_type;
123 return st.is_integer_type ();
126 MemberAccess? find_member_access (Expression expr) {
127 if (expr is ParenthesizedExpression) {
128 var pe = (ParenthesizedExpression) expr;
129 return find_member_access (pe.inner);
132 if (expr is MemberAccess) {
133 return (MemberAccess) expr;
136 return null;
139 public override bool check (SemanticAnalyzer analyzer) {
140 if (checked) {
141 return !error;
144 checked = true;
146 if (operator == UnaryOperator.REF || operator == UnaryOperator.OUT) {
147 inner.lvalue = true;
148 inner.target_type = target_type;
151 if (!inner.check (analyzer)) {
152 /* if there was an error in the inner expression, skip type check */
153 error = true;
154 return false;
157 if (inner.value_type is FieldPrototype) {
158 error = true;
159 Report.error (inner.source_reference, "Access to instance member `%s' denied".printf (inner.symbol_reference.get_full_name ()));
160 return false;
163 if (operator == UnaryOperator.PLUS || operator == UnaryOperator.MINUS) {
164 // integer or floating point type
165 if (!is_numeric_type (inner.value_type)) {
166 error = true;
167 Report.error (source_reference, "Operator not supported for `%s'".printf (inner.value_type.to_string ()));
168 return false;
171 value_type = inner.value_type;
172 } else if (operator == UnaryOperator.LOGICAL_NEGATION) {
173 // boolean type
174 if (!inner.value_type.compatible (analyzer.bool_type)) {
175 error = true;
176 Report.error (source_reference, "Operator not supported for `%s'".printf (inner.value_type.to_string ()));
177 return false;
180 value_type = inner.value_type;
181 } else if (operator == UnaryOperator.BITWISE_COMPLEMENT) {
182 // integer type
183 if (!is_integer_type (inner.value_type)) {
184 error = true;
185 Report.error (source_reference, "Operator not supported for `%s'".printf (inner.value_type.to_string ()));
186 return false;
189 value_type = inner.value_type;
190 } else if (operator == UnaryOperator.INCREMENT ||
191 operator == UnaryOperator.DECREMENT) {
192 // integer type
193 if (!is_integer_type (inner.value_type)) {
194 error = true;
195 Report.error (source_reference, "Operator not supported for `%s'".printf (inner.value_type.to_string ()));
196 return false;
199 var ma = find_member_access (inner);
200 if (ma == null) {
201 error = true;
202 Report.error (source_reference, "Prefix operators not supported for this expression");
203 return false;
206 var old_value = new MemberAccess (ma.inner, ma.member_name, inner.source_reference);
207 var bin = new BinaryExpression (operator == UnaryOperator.INCREMENT ? BinaryOperator.PLUS : BinaryOperator.MINUS, old_value, new IntegerLiteral ("1"), source_reference);
209 var assignment = new Assignment (ma, bin, AssignmentOperator.SIMPLE, source_reference);
210 var parenthexp = new ParenthesizedExpression (assignment, source_reference);
211 parenthexp.target_type = target_type;
212 analyzer.replaced_nodes.add (this);
213 parent_node.replace_expression (this, parenthexp);
214 parenthexp.check (analyzer);
215 return true;
216 } else if (operator == UnaryOperator.REF || operator == UnaryOperator.OUT) {
217 if (inner.symbol_reference is Field || inner.symbol_reference is FormalParameter || inner.symbol_reference is LocalVariable) {
218 // ref and out can only be used with fields, parameters, and local variables
219 lvalue = true;
220 value_type = inner.value_type;
221 } else {
222 error = true;
223 Report.error (source_reference, "ref and out method arguments can only be used with fields, parameters, and local variables");
224 return false;
226 } else {
227 error = true;
228 Report.error (source_reference, "internal error: unsupported unary operator");
229 return false;
232 return !error;
235 public override void get_defined_variables (Collection<LocalVariable> collection) {
236 inner.get_defined_variables (collection);
237 if (operator == UnaryOperator.OUT || operator == UnaryOperator.REF) {
238 var local = inner.symbol_reference as LocalVariable;
239 if (local != null) {
240 collection.add (local);
245 public override void get_used_variables (Collection<LocalVariable> collection) {
246 if (operator != UnaryOperator.OUT) {
247 inner.get_used_variables (collection);
252 public enum Vala.UnaryOperator {
253 NONE,
254 PLUS,
255 MINUS,
256 LOGICAL_NEGATION,
257 BITWISE_COMPLEMENT,
258 INCREMENT,
259 DECREMENT,
260 REF,