Warn when using result variable with incompatible type to prepare possible
[vala-lang.git] / vala / valastruct.vala
blob62bb24913f019b355a5d54077a2b0ede85ff5d65
1 /* valastruct.vala
3 * Copyright (C) 2006-2009 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 GLib;
24 using Gee;
26 /**
27 * Represents a struct declaration in the source code.
29 public class Vala.Struct : TypeSymbol {
30 private Gee.List<TypeParameter> type_parameters = new ArrayList<TypeParameter> ();
31 private Gee.List<Constant> constants = new ArrayList<Constant> ();
32 private Gee.List<Field> fields = new ArrayList<Field> ();
33 private Gee.List<Method> methods = new ArrayList<Method> ();
35 private Gee.List<DataType> base_types = new ArrayList<DataType> ();
37 private string cname;
38 private string const_cname;
39 private string type_id;
40 private string lower_case_cprefix;
41 private string lower_case_csuffix;
42 private bool boolean_type;
43 private bool integer_type;
44 private bool floating_type;
45 private int rank;
46 private string marshaller_type_name;
47 private string get_value_function;
48 private string set_value_function;
49 private string default_value = null;
50 private string? type_signature;
51 private string copy_function;
52 private string destroy_function;
54 /**
55 * Specifies the default construction method.
57 public Method default_construction_method { get; set; }
59 /**
60 * Specifies whether this struct has a registered GType.
62 public bool has_type_id { get; set; default = true; }
64 /**
65 * Creates a new struct.
67 * @param name type name
68 * @param source_reference reference to source code
69 * @return newly created struct
71 public Struct (string name, SourceReference? source_reference = null) {
72 base (name, source_reference);
75 /**
76 * Appends the specified parameter to the list of type parameters.
78 * @param p a type parameter
80 public void add_type_parameter (TypeParameter p) {
81 type_parameters.add (p);
82 p.type = this;
83 scope.add (p.name, p);
86 /**
87 * Returns a copy of the type parameter list.
89 * @return list of type parameters
91 public Gee.List<TypeParameter> get_type_parameters () {
92 return new ReadOnlyList<TypeParameter> (type_parameters);
95 /**
96 * Adds the specified constant as a member to this struct.
98 * @param c a constant
100 public void add_constant (Constant c) {
101 constants.add (c);
102 scope.add (c.name, c);
106 * Adds the specified field as a member to this struct.
108 * @param f a field
110 public void add_field (Field f) {
111 fields.add (f);
112 scope.add (f.name, f);
116 * Returns a copy of the list of fields.
118 * @return list of fields
120 public Gee.List<Field> get_fields () {
121 return new ReadOnlyList<Field> (fields);
125 * Returns a copy of the list of constants.
127 * @return list of constants
129 public Gee.List<Constant> get_constants () {
130 return new ReadOnlyList<Constant> (constants);
134 * Adds the specified method as a member to this struct.
136 * @param m a method
138 public void add_method (Method m) {
139 return_if_fail (m != null);
141 if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
142 m.this_parameter = new FormalParameter ("this", new StructValueType (this));
143 m.scope.add (m.this_parameter.name, m.this_parameter);
145 if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) {
146 m.result_var = new LocalVariable (m.return_type.copy (), "result");
147 m.scope.add (m.result_var.name, m.result_var);
149 if (m is CreationMethod) {
150 if (m.name == null) {
151 default_construction_method = m;
152 m.name = "new";
155 var cm = (CreationMethod) m;
156 if (cm.type_name != null && cm.type_name != name) {
157 // type_name is null for constructors generated by GIdlParser
158 Report.error (m.source_reference, "missing return type in method `%s.%s´".printf (get_full_name (), cm.type_name));
159 m.error = true;
160 return;
164 methods.add (m);
165 scope.add (m.name, m);
169 * Returns a copy of the list of methods.
171 * @return list of methods
173 public Gee.List<Method> get_methods () {
174 return new ReadOnlyList<Method> (methods);
177 public override void accept (CodeVisitor visitor) {
178 visitor.visit_struct (this);
181 public override void accept_children (CodeVisitor visitor) {
182 foreach (DataType type in base_types) {
183 type.accept (visitor);
186 foreach (TypeParameter p in type_parameters) {
187 p.accept (visitor);
190 foreach (Field f in fields) {
191 f.accept (visitor);
194 foreach (Constant c in constants) {
195 c.accept (visitor);
198 foreach (Method m in methods) {
199 m.accept (visitor);
203 public override string get_cname (bool const_type = false) {
204 if (const_type && const_cname != null) {
205 return const_cname;
208 if (cname == null) {
209 var attr = get_attribute ("CCode");
210 if (attr != null) {
211 cname = attr.get_string ("cname");
213 if (cname == null) {
214 cname = get_default_cname ();
217 return cname;
221 * Returns the default name of this struct as it is used in C code.
223 * @return the name to be used in C code by default
225 public string get_default_cname () {
226 return "%s%s".printf (parent_symbol.get_cprefix (), name);
229 private void set_const_cname (string cname) {
230 this.const_cname = cname;
233 public override string get_lower_case_cprefix () {
234 if (lower_case_cprefix == null) {
235 lower_case_cprefix = "%s_".printf (get_lower_case_cname (null));
237 return lower_case_cprefix;
240 private string get_lower_case_csuffix () {
241 if (lower_case_csuffix == null) {
242 lower_case_csuffix = camel_case_to_lower_case (name);
244 return lower_case_csuffix;
247 public override string? get_lower_case_cname (string? infix) {
248 if (infix == null) {
249 infix = "";
251 return "%s%s%s".printf (parent_symbol.get_lower_case_cprefix (), infix, get_lower_case_csuffix ());
254 public override string? get_upper_case_cname (string? infix) {
255 return get_lower_case_cname (infix).up ();
258 public override string? get_type_signature () {
259 if (type_signature == null) {
260 var str = new StringBuilder ();
261 str.append_c ('(');
262 foreach (Field f in fields) {
263 if (f.binding == MemberBinding.INSTANCE) {
264 str.append (f.field_type.get_type_signature ());
267 str.append_c (')');
268 return str.str;
270 return type_signature;
274 * Returns whether this is a boolean type.
276 * @return true if this is a boolean type, false otherwise
278 public bool is_boolean_type () {
279 foreach (DataType type in base_types) {
280 var st = type.data_type as Struct;
281 if (st != null && st.is_boolean_type ()) {
282 return true;
285 return boolean_type;
289 * Returns whether this is an integer type.
291 * @return true if this is an integer type, false otherwise
293 public bool is_integer_type () {
294 foreach (DataType type in base_types) {
295 var st = type.data_type as Struct;
296 if (st != null && st.is_integer_type ()) {
297 return true;
300 return integer_type;
304 * Returns whether this is a floating point type.
306 * @return true if this is a floating point type, false otherwise
308 public bool is_floating_type () {
309 foreach (DataType type in base_types) {
310 var st = type.data_type as Struct;
311 if (st != null && st.is_floating_type ()) {
312 return true;
315 return floating_type;
319 * Returns the rank of this integer or floating point type.
321 * @return the rank if this is an integer or floating point type
323 public int get_rank () {
324 return rank;
327 private void process_ccode_attribute (Attribute a) {
328 if (a.has_argument ("const_cname")) {
329 set_const_cname (a.get_string ("const_cname"));
331 if (a.has_argument ("cprefix")) {
332 lower_case_cprefix = a.get_string ("cprefix");
334 if (a.has_argument ("cheader_filename")) {
335 var val = a.get_string ("cheader_filename");
336 foreach (string filename in val.split (",")) {
337 add_cheader_filename (filename);
340 if (a.has_argument ("has_type_id")) {
341 has_type_id = a.get_bool ("has_type_id");
343 if (a.has_argument ("type_id")) {
344 set_type_id (a.get_string ("type_id"));
346 if (a.has_argument ("marshaller_type_name")) {
347 set_marshaller_type_name (a.get_string ("marshaller_type_name"));
349 if (a.has_argument ("get_value_function")) {
350 set_get_value_function (a.get_string ("get_value_function"));
352 if (a.has_argument ("set_value_function")) {
353 set_set_value_function (a.get_string ("set_value_function"));
355 if (a.has_argument ("default_value")) {
356 set_default_value (a.get_string ("default_value"));
358 if (a.has_argument ("type_signature")) {
359 type_signature = a.get_string ("type_signature");
361 if (a.has_argument ("copy_function")) {
362 set_copy_function (a.get_string ("copy_function"));
364 if (a.has_argument ("destroy_function")) {
365 set_destroy_function (a.get_string ("destroy_function"));
369 private void process_boolean_type_attribute (Attribute a) {
370 boolean_type = true;
373 private void process_integer_type_attribute (Attribute a) {
374 integer_type = true;
375 if (a.has_argument ("rank")) {
376 rank = a.get_integer ("rank");
380 private void process_floating_type_attribute (Attribute a) {
381 floating_type = true;
382 if (a.has_argument ("rank")) {
383 rank = a.get_integer ("rank");
388 * Process all associated attributes.
390 public void process_attributes () {
391 foreach (Attribute a in attributes) {
392 if (a.name == "CCode") {
393 process_ccode_attribute (a);
394 } else if (a.name == "BooleanType") {
395 process_boolean_type_attribute (a);
396 } else if (a.name == "IntegerType") {
397 process_integer_type_attribute (a);
398 } else if (a.name == "FloatingType") {
399 process_floating_type_attribute (a);
404 public override string? get_type_id () {
405 if (type_id == null) {
406 if (!has_type_id) {
407 foreach (DataType type in base_types) {
408 var st = type.data_type as Struct;
409 if (st != null) {
410 return st.get_type_id ();;
413 if (is_simple_type ()) {
414 Report.error (source_reference, "The type `%s` doesn't declare a type id".printf (get_full_name ()));
415 } else {
416 return "G_TYPE_POINTER";
418 } else {
419 type_id = get_upper_case_cname ("TYPE_");
422 return type_id;
425 public void set_type_id (string? name) {
426 this.type_id = name;
429 public override string? get_marshaller_type_name () {
430 if (marshaller_type_name == null) {
431 foreach (DataType type in base_types) {
432 var st = type.data_type as Struct;
433 if (st != null) {
434 return st.get_marshaller_type_name ();
437 if (is_simple_type ()) {
438 Report.error (source_reference, "The type `%s` doesn't declare a marshaller type name".printf (get_full_name ()));
439 } else if (has_type_id) {
440 return "BOXED";
441 } else {
442 return "POINTER";
445 return marshaller_type_name;
448 private void set_marshaller_type_name (string? name) {
449 this.marshaller_type_name = name;
452 public override string? get_get_value_function () {
453 if (get_value_function == null) {
454 foreach (DataType type in base_types) {
455 var st = type.data_type as Struct;
456 if (st != null) {
457 return st.get_get_value_function ();
460 if (is_simple_type ()) {
461 Report.error (source_reference, "The value type `%s` doesn't declare a GValue get function".printf (get_full_name ()));
462 return null;
463 } else if (has_type_id) {
464 return "g_value_get_boxed";
465 } else {
466 return "g_value_get_pointer";
468 } else {
469 return get_value_function;
473 public override string? get_set_value_function () {
474 if (set_value_function == null) {
475 foreach (DataType type in base_types) {
476 var st = type.data_type as Struct;
477 if (st != null) {
478 return st.get_set_value_function ();
481 if (is_simple_type ()) {
482 Report.error (source_reference, "The value type `%s` doesn't declare a GValue set function".printf (get_full_name ()));
483 return null;
484 } else if (has_type_id) {
485 return "g_value_set_boxed";
486 } else {
487 return "g_value_set_pointer";
489 } else {
490 return set_value_function;
494 private void set_get_value_function (string? function) {
495 get_value_function = function;
498 private void set_set_value_function (string? function) {
499 set_value_function = function;
502 public override string? get_default_value () {
503 if (default_value != null) {
504 return default_value;
507 // inherit default value from base type
508 foreach (DataType type in base_types) {
509 var st = type.data_type as Struct;
510 if (st != null) {
511 return st.get_default_value ();
514 return null;
517 private void set_default_value (string? value) {
518 default_value = value;
522 * Adds the specified struct to the list of base types of this struct.
524 * @param type a class or interface reference
526 public void add_base_type (DataType type) {
527 base_types.add (type);
528 type.parent_node = this;
532 * Returns a copy of the base type list.
534 * @return list of base types
536 public Gee.List<DataType> get_base_types () {
537 return new ReadOnlyList<DataType> (base_types);
540 public override int get_type_parameter_index (string name) {
541 int i = 0;
543 foreach (TypeParameter p in type_parameters) {
544 if (p.name == name) {
545 return (i);
547 i++;
550 return -1;
554 * Returns whether this struct is a simple type, i.e. whether
555 * instances are passed by value.
557 public bool is_simple_type () {
558 foreach (DataType type in base_types) {
559 var st = type.data_type as Struct;
560 if (st != null && st.is_simple_type ()) {
561 return true;
564 return get_attribute ("SimpleType") != null;
568 * Marks this struct as simple type, i.e. instances will be passed by
569 * value.
571 public void set_simple_type (bool simple_type) {
572 attributes.append (new Attribute ("SimpleType"));
575 public override void replace_type (DataType old_type, DataType new_type) {
576 for (int i = 0; i < base_types.size; i++) {
577 if (base_types[i] == old_type) {
578 base_types[i] = new_type;
579 return;
584 public override bool is_subtype_of (TypeSymbol t) {
585 if (this == t) {
586 return true;
589 foreach (DataType base_type in base_types) {
590 if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
591 return true;
595 return false;
598 public override string? get_dup_function () {
599 // TODO use attribute check instead
600 if (external_package) {
601 return null;
602 } else {
603 return get_lower_case_cprefix () + "dup";
607 public override string? get_free_function () {
608 // TODO use attribute check instead
609 if (external_package) {
610 return null;
611 } else {
612 return get_lower_case_cprefix () + "free";
616 public string get_default_copy_function () {
617 return get_lower_case_cprefix () + "copy";
620 public override string? get_copy_function () {
621 if (copy_function == null) {
622 copy_function = get_default_copy_function ();
624 return copy_function;
627 public void set_copy_function (string name) {
628 this.copy_function = name;
631 public string get_default_destroy_function () {
632 return get_lower_case_cprefix () + "destroy";
635 public override string? get_destroy_function () {
636 if (destroy_function == null) {
637 destroy_function = get_default_destroy_function ();
639 return destroy_function;
642 public void set_destroy_function (string name) {
643 this.destroy_function = name;
646 public bool is_disposable () {
647 if (destroy_function != null) {
648 return true;
651 foreach (Field f in fields) {
652 if (f.binding == MemberBinding.INSTANCE
653 && f.field_type.is_disposable ()) {
654 return true;
658 return false;
661 public override bool check (SemanticAnalyzer analyzer) {
662 if (checked) {
663 return !error;
666 checked = true;
668 process_attributes ();
670 var old_source_file = analyzer.current_source_file;
671 var old_symbol = analyzer.current_symbol;
672 var old_struct = analyzer.current_struct;
674 if (source_reference != null) {
675 analyzer.current_source_file = source_reference.file;
677 analyzer.current_symbol = this;
678 analyzer.current_struct = this;
680 foreach (DataType type in base_types) {
681 type.check (analyzer);
683 if (!(type is StructValueType)) {
684 error = true;
685 Report.error (source_reference, "The base type `%s` of value type `%s` is not a struct".printf (type.data_type.to_string (), get_full_name ()));
686 return false;
690 foreach (TypeParameter p in type_parameters) {
691 p.check (analyzer);
694 foreach (Field f in fields) {
695 f.check (analyzer);
698 foreach (Constant c in constants) {
699 c.check (analyzer);
702 foreach (Method m in methods) {
703 m.check (analyzer);
706 if (!external && !external_package && get_base_types ().size == 0 && get_fields ().size == 0) {
707 Report.error (source_reference, "structs cannot be empty");
710 analyzer.current_source_file = old_source_file;
711 analyzer.current_symbol = old_symbol;
712 analyzer.current_struct = old_struct;
714 return !error;