1/* valaconstant.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
23using GLib;
24
25/**
26 * Represents a type member with a constant value.
27 */
28public class Vala.Constant : Symbol {
29	/**
30	 * The data type of this constant.
31	 */
32	public DataType type_reference {
33		get { return _data_type; }
34		set {
35			_data_type = value;
36			_data_type.parent_node = this;
37		}
38	}
39
40	/**
41	 * The value of this constant.
42	 */
43	public Expression? value {
44		get { return _value; }
45		set {
46			_value = value;
47			if (_value != null) {
48				_value.parent_node = this;
49			}
50		}
51	}
52
53	private DataType _data_type;
54
55	private Expression _value;
56
57	/**
58	 * Creates a new constant.
59	 *
60	 * @param name             constant name
61	 * @param type_reference   constant type
62	 * @param value            constant value
63	 * @param source_reference reference to source code
64	 * @return                 newly created constant
65	 */
66	public Constant (string name, DataType? type_reference, Expression? value, SourceReference? source_reference = null, Comment? comment = null) {
67		base (name, source_reference, comment);
68		if (type_reference != null) {
69			this.type_reference = type_reference;
70		}
71		this.value = value;
72	}
73
74	public override void accept (CodeVisitor visitor) {
75		visitor.visit_constant (this);
76	}
77
78	public override void accept_children (CodeVisitor visitor) {
79		type_reference.accept (visitor);
80
81		if (value != null) {
82			value.accept (visitor);
83		}
84	}
85
86	public override void replace_expression (Expression old_node, Expression new_node) {
87		if (value == old_node) {
88			value = new_node;
89		}
90	}
91
92	public override void replace_type (DataType old_type, DataType new_type) {
93		if (type_reference == old_type) {
94			type_reference = new_type;
95		}
96	}
97
98	public override bool check (CodeContext context) {
99		if (checked) {
100			return !error;
101		}
102
103		checked = true;
104
105		var old_source_file = context.analyzer.current_source_file;
106		var old_symbol = context.analyzer.current_symbol;
107
108		if (source_reference != null) {
109			context.analyzer.current_source_file = source_reference.file;
110		}
111		if (!(parent_symbol is Block)) {
112			// non-local constant
113			context.analyzer.current_symbol = this;
114		}
115
116		type_reference.check (context);
117
118		if (!check_const_type (type_reference, context)) {
119			error = true;
120			Report.error (source_reference, "`%s' not supported as type for constants".printf (type_reference.to_string ()));
121			return false;
122		}
123
124		if (!external) {
125			if (value == null) {
126				// constants from fast-vapi files are special
127				if (source_type != SourceFileType.FAST) {
128					error = true;
129					Report.error (source_reference, "A const field requires a value to be provided");
130				}
131			} else {
132				value.target_type = type_reference;
133
134				if (!value.check (context) || type_reference.error) {
135					error = true;
136					return false;
137				}
138
139				if (!value.value_type.compatible (type_reference)) {
140					error = true;
141					Report.error (source_reference, "Cannot convert from `%s' to `%s'".printf (value.value_type.to_string (), type_reference.to_string ()));
142					return false;
143				}
144
145				// support translated string constants for efficiency / convenience
146				// even though the expression is not a compile-time constant
147				unowned MethodCall? call = value as MethodCall;
148				if (call != null) {
149					unowned MethodType? method_type = call.call.value_type as MethodType;
150					if (method_type != null && method_type.method_symbol.get_full_name () == "GLib._") {
151						// first argument is string
152						var literal = call.get_argument_list ().get (0) as StringLiteral;
153						if (literal != null) {
154							value = literal;
155							literal.translate = true;
156						}
157					}
158				}
159
160				if (!value.is_constant ()) {
161					error = true;
162					Report.error (value.source_reference, "Value must be constant");
163					return false;
164				}
165			}
166		} else {
167			if (value != null) {
168				error = true;
169				Report.error (source_reference, "External constants cannot use values");
170			}
171		}
172
173		if (!external_package && !hides && get_hidden_member () != null) {
174			Report.warning (source_reference, "%s hides inherited constant `%s'. Use the `new' keyword if hiding was intentional".printf (get_full_name (), get_hidden_member ().get_full_name ()));
175		}
176
177		context.analyzer.current_source_file = old_source_file;
178		context.analyzer.current_symbol = old_symbol;
179
180		active = true;
181
182		return !error;
183	}
184
185	bool check_const_type (DataType type, CodeContext context) {
186		if (type is ValueType) {
187			return true;
188		} else if (type is VoidType || type is PointerType) {
189			return false;
190		} else if (type is ArrayType) {
191			unowned ArrayType array_type = (ArrayType) type;
192			return check_const_type (array_type.element_type, context);
193		} else if (type.type_symbol != null) {
194			return type.type_symbol.is_subtype_of (context.analyzer.string_type.type_symbol);
195		} else {
196			return false;
197		}
198	}
199}
200