Use strict non-null types with --enable-experimental-non-null
[vala-lang.git] / vala / valamemberaccess.vala
blobb7fab0b264c93fe8e41769e9d72ca25aaee59022
1 /* valamemberaccess.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;
25 /**
26 * Represents an access to a type member in the source code.
28 public class Vala.MemberAccess : Expression {
29 /**
30 * The parent of the member.
32 public Expression? inner {
33 get {
34 return _inner;
36 set {
37 _inner = value;
38 if (_inner != null) {
39 _inner.parent_node = this;
44 /**
45 * The name of the member.
47 public string member_name { get; set; }
49 /**
50 * Pointer member access.
52 public bool pointer_member_access { get; set; }
54 /**
55 * Represents access to an instance member without an actual instance,
56 * e.g. `MyClass.an_instance_method`.
58 public bool prototype_access { get; set; }
60 /**
61 * Specifies whether the member is used for object creation.
63 public bool creation_member { get; set; }
65 /**
66 * Qualified access to global symbol.
68 public bool qualified { get; set; }
70 private Expression? _inner;
71 private List<DataType> type_argument_list = new ArrayList<DataType> ();
73 /**
74 * Creates a new member access expression.
76 * @param inner parent of the member
77 * @param member_name member name
78 * @param source_reference reference to source code
79 * @return newly created member access expression
81 public MemberAccess (Expression? inner, string member_name, SourceReference? source_reference = null) {
82 this.inner = inner;
83 this.member_name = member_name;
84 this.source_reference = source_reference;
87 public MemberAccess.simple (string member_name, SourceReference? source_reference = null) {
88 this.member_name = member_name;
89 this.source_reference = source_reference;
92 public MemberAccess.pointer (Expression inner, string member_name, SourceReference? source_reference = null) {
93 this.inner = inner;
94 this.member_name = member_name;
95 this.source_reference = source_reference;
96 pointer_member_access = true;
99 /**
100 * Appends the specified type as generic type argument.
102 * @param arg a type reference
104 public void add_type_argument (DataType arg) {
105 type_argument_list.add (arg);
106 arg.parent_node = this;
110 * Returns a copy of the list of generic type arguments.
112 * @return type argument list
114 public List<DataType> get_type_arguments () {
115 return new ReadOnlyList<DataType> (type_argument_list);
118 public override void accept (CodeVisitor visitor) {
119 visitor.visit_member_access (this);
121 visitor.visit_expression (this);
124 public override void accept_children (CodeVisitor visitor) {
125 if (inner != null) {
126 inner.accept (visitor);
129 foreach (DataType type_arg in type_argument_list) {
130 type_arg.accept (visitor);
134 public override string to_string () {
135 if (symbol_reference.is_instance_member ()) {
136 if (inner == null) {
137 return member_name;
138 } else {
139 return "%s.%s".printf (inner.to_string (), member_name);
141 } else {
142 // ensure to always use fully-qualified name
143 // to refer to static members
144 return symbol_reference.get_full_name ();
148 public override void replace_expression (Expression old_node, Expression new_node) {
149 if (inner == old_node) {
150 inner = new_node;
154 public override bool is_pure () {
155 // accessing property could have side-effects
156 return (inner == null || inner.is_pure ()) && !(symbol_reference is Property);
159 public override void replace_type (DataType old_type, DataType new_type) {
160 for (int i = 0; i < type_argument_list.size; i++) {
161 if (type_argument_list[i] == old_type) {
162 type_argument_list[i] = new_type;
163 return;
168 public override bool is_constant () {
169 if (symbol_reference is Constant || symbol_reference is EnumValue) {
170 return true;
171 } else {
172 return false;
176 public override bool is_non_null () {
177 var c = symbol_reference as Constant;
178 if (c != null) {
179 return !c.type_reference.nullable;
180 } else {
181 return false;
185 public override bool check (SemanticAnalyzer analyzer) {
186 if (checked) {
187 return !error;
190 checked = true;
192 if (inner != null) {
193 inner.check (analyzer);
196 foreach (DataType type_arg in type_argument_list) {
197 type_arg.check (analyzer);
200 Symbol base_symbol = null;
201 FormalParameter this_parameter = null;
202 bool may_access_instance_members = false;
203 bool may_access_klass_members = false;
205 symbol_reference = null;
207 if (qualified) {
208 base_symbol = analyzer.root_symbol;
209 symbol_reference = analyzer.root_symbol.scope.lookup (member_name);
210 } else if (inner == null) {
211 if (member_name == "this") {
212 if (!analyzer.is_in_instance_method ()) {
213 error = true;
214 Report.error (source_reference, "This access invalid outside of instance methods");
215 return false;
219 base_symbol = analyzer.current_symbol;
221 var sym = analyzer.current_symbol;
222 while (sym != null && symbol_reference == null) {
223 if (this_parameter == null) {
224 if (sym is CreationMethod) {
225 var cm = (CreationMethod) sym;
226 this_parameter = cm.this_parameter;
227 may_access_instance_members = true;
228 may_access_klass_members = true;
229 } else if (sym is Property) {
230 var prop = (Property) sym;
231 this_parameter = prop.this_parameter;
232 may_access_instance_members = (prop.binding == MemberBinding.INSTANCE);
233 may_access_klass_members = (prop.binding != MemberBinding.STATIC);
234 } else if (sym is Constructor) {
235 var c = (Constructor) sym;
236 this_parameter = c.this_parameter;
237 may_access_instance_members = (c.binding == MemberBinding.INSTANCE);
238 may_access_klass_members = true;
239 } else if (sym is Destructor) {
240 var d = (Destructor) sym;
241 this_parameter = d.this_parameter;
242 may_access_instance_members = (d.binding == MemberBinding.INSTANCE);
243 may_access_klass_members = true;
244 } else if (sym is Method) {
245 var m = (Method) sym;
246 this_parameter = m.this_parameter;
247 may_access_instance_members = (m.binding == MemberBinding.INSTANCE);
248 may_access_klass_members = (m.binding != MemberBinding.STATIC);
252 symbol_reference = analyzer.symbol_lookup_inherited (sym, member_name);
253 sym = sym.parent_symbol;
256 if (symbol_reference == null && source_reference != null) {
257 foreach (UsingDirective ns in source_reference.using_directives) {
258 var local_sym = ns.namespace_symbol.scope.lookup (member_name);
259 if (local_sym != null) {
260 if (symbol_reference != null && symbol_reference != local_sym) {
261 error = true;
262 Report.error (source_reference, "`%s' is an ambiguous reference between `%s' and `%s'".printf (member_name, symbol_reference.get_full_name (), local_sym.get_full_name ()));
263 return false;
265 symbol_reference = local_sym;
269 } else {
270 if (inner.error) {
271 /* if there was an error in the inner expression, skip this check */
272 error = true;
273 return false;
276 if (pointer_member_access) {
277 var pointer_type = inner.value_type as PointerType;
278 if (pointer_type != null && pointer_type.base_type is ValueType) {
279 // transform foo->bar to (*foo).bar
280 inner = new PointerIndirection (inner, source_reference);
281 inner.check (analyzer);
282 pointer_member_access = false;
286 if (inner is MemberAccess) {
287 var ma = (MemberAccess) inner;
288 if (ma.prototype_access) {
289 error = true;
290 Report.error (source_reference, "Access to instance member `%s' denied".printf (inner.symbol_reference.get_full_name ()));
291 return false;
295 if (inner is MemberAccess || inner is BaseAccess) {
296 base_symbol = inner.symbol_reference;
298 if (symbol_reference == null && (base_symbol is Namespace || base_symbol is TypeSymbol)) {
299 symbol_reference = base_symbol.scope.lookup (member_name);
300 if (inner is BaseAccess) {
301 // inner expression is base access
302 // access to instance members of the base type possible
303 may_access_instance_members = true;
304 may_access_klass_members = true;
309 if (symbol_reference == null && inner.value_type != null) {
310 if (pointer_member_access) {
311 symbol_reference = inner.value_type.get_pointer_member (member_name);
312 } else {
313 if (inner.value_type.data_type != null) {
314 base_symbol = inner.value_type.data_type;
316 symbol_reference = inner.value_type.get_member (member_name);
318 if (symbol_reference != null) {
319 // inner expression is variable, field, or parameter
320 // access to instance members of the corresponding type possible
321 may_access_instance_members = true;
322 may_access_klass_members = true;
326 if (symbol_reference == null && inner.value_type != null && inner.value_type.is_dynamic) {
327 // allow late bound members for dynamic types
328 var dynamic_object_type = (ObjectType) inner.value_type;
329 if (parent_node is MethodCall) {
330 var invoc = (MethodCall) parent_node;
331 if (invoc.call == this) {
332 // dynamic method
333 DataType ret_type;
334 if (invoc.target_type != null) {
335 ret_type = invoc.target_type.copy ();
336 ret_type.value_owned = true;
337 } else if (invoc.parent_node is ExpressionStatement) {
338 ret_type = new VoidType ();
339 } else {
340 // expect dynamic object of the same type
341 ret_type = inner.value_type.copy ();
343 var m = new DynamicMethod (inner.value_type, member_name, ret_type, source_reference);
344 m.invocation = invoc;
345 var err = new ErrorType (null, null);
346 err.dynamic_error = true;
347 m.add_error_type (err);
348 m.access = SymbolAccessibility.PUBLIC;
349 m.add_parameter (new FormalParameter.with_ellipsis ());
350 dynamic_object_type.type_symbol.scope.add (null, m);
351 symbol_reference = m;
353 } else if (parent_node is Assignment) {
354 var a = (Assignment) parent_node;
355 if (a.left == this
356 && (a.operator == AssignmentOperator.ADD
357 || a.operator == AssignmentOperator.SUB)) {
358 // dynamic signal
359 var s = new DynamicSignal (inner.value_type, member_name, new VoidType (), source_reference);
360 s.handler = a.right;
361 s.access = SymbolAccessibility.PUBLIC;
362 dynamic_object_type.type_symbol.scope.add (null, s);
363 symbol_reference = s;
364 } else if (a.left == this) {
365 // dynamic property assignment
366 var prop = new DynamicProperty (inner.value_type, member_name, source_reference);
367 prop.access = SymbolAccessibility.PUBLIC;
368 prop.set_accessor = new PropertyAccessor (false, true, false, null, null, prop.source_reference);
369 prop.set_accessor.access = SymbolAccessibility.PUBLIC;
370 prop.owner = inner.value_type.data_type.scope;
371 dynamic_object_type.type_symbol.scope.add (null, prop);
372 symbol_reference = prop;
375 if (symbol_reference == null) {
376 // dynamic property read access
377 var prop = new DynamicProperty (inner.value_type, member_name, source_reference);
378 if (target_type != null) {
379 prop.property_type = target_type;
380 } else {
381 // expect dynamic object of the same type
382 prop.property_type = inner.value_type.copy ();
384 prop.access = SymbolAccessibility.PUBLIC;
385 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, prop.source_reference);
386 prop.get_accessor.access = SymbolAccessibility.PUBLIC;
387 prop.owner = inner.value_type.data_type.scope;
388 dynamic_object_type.type_symbol.scope.add (null, prop);
389 symbol_reference = prop;
391 if (symbol_reference != null) {
392 may_access_instance_members = true;
393 may_access_klass_members = true;
398 if (symbol_reference == null) {
399 error = true;
401 string base_type_name = "(null)";
402 if (inner != null && inner.value_type != null) {
403 base_type_name = inner.value_type.to_string ();
404 } else if (base_symbol != null) {
405 base_type_name = base_symbol.get_full_name ();
408 Report.error (source_reference, "The name `%s' does not exist in the context of `%s'".printf (member_name, base_type_name));
409 return false;
412 var member = symbol_reference;
413 var access = SymbolAccessibility.PUBLIC;
414 bool instance = false;
415 bool klass = false;
417 if (!member.check (analyzer)) {
418 return false;
421 if (member is LocalVariable) {
422 var local = (LocalVariable) member;
423 var block = (Block) local.parent_symbol;
424 if (analyzer.find_parent_method (block) != analyzer.current_method) {
425 // mark all methods between current method and the captured
426 // block as closures (to support nested closures)
427 Symbol sym = analyzer.current_method;
428 while (sym != block) {
429 var method = sym as Method;
430 if (method != null) {
431 method.closure = true;
432 // consider captured variables as used
433 // as we require captured variables to be initialized
434 method.add_captured_variable (local);
436 sym = sym.parent_symbol;
439 local.captured = true;
440 block.captured = true;
442 } else if (member is FormalParameter) {
443 var param = (FormalParameter) member;
444 var m = param.parent_symbol as Method;
445 if (m != null && m != analyzer.current_method && param != m.this_parameter) {
446 // mark all methods between current method and the captured
447 // parameter as closures (to support nested closures)
448 Symbol sym = analyzer.current_method;
449 while (sym != m) {
450 var method = sym as Method;
451 if (method != null) {
452 method.closure = true;
454 sym = sym.parent_symbol;
457 param.captured = true;
458 m.body.captured = true;
460 if (param.direction != ParameterDirection.IN) {
461 error = true;
462 Report.error (source_reference, "Cannot capture reference or output parameter `%s'".printf (param.get_full_name ()));
465 } else if (member is Field) {
466 var f = (Field) member;
467 access = f.access;
468 instance = (f.binding == MemberBinding.INSTANCE);
469 klass = (f.binding == MemberBinding.CLASS);
470 } else if (member is Method) {
471 var m = (Method) member;
472 if (m.is_async_callback) {
473 // ensure to use right callback method for virtual/abstract async methods
474 m = analyzer.current_method.get_callback_method ();
475 symbol_reference = m;
476 member = symbol_reference;
477 } else if (m.base_method != null) {
478 // refer to base method to inherit default arguments
479 m = m.base_method;
481 if (m.signal_reference != null) {
482 // method is class/default handler for a signal
483 // let signal deal with member access
484 symbol_reference = m.signal_reference;
485 } else {
486 symbol_reference = m;
489 member = symbol_reference;
490 } else if (m.base_interface_method != null) {
491 // refer to base method to inherit default arguments
492 m = m.base_interface_method;
493 symbol_reference = m;
494 member = symbol_reference;
496 access = m.access;
497 if (!(m is CreationMethod)) {
498 instance = (m.binding == MemberBinding.INSTANCE);
500 klass = (m.binding == MemberBinding.CLASS);
501 } else if (member is Property) {
502 var prop = (Property) member;
503 if (!prop.check (analyzer)) {
504 error = true;
505 return false;
507 if (prop.base_property != null) {
508 // refer to base property
509 prop = prop.base_property;
510 symbol_reference = prop;
511 member = symbol_reference;
512 } else if (prop.base_interface_property != null) {
513 // refer to base property
514 prop = prop.base_interface_property;
515 symbol_reference = prop;
516 member = symbol_reference;
518 access = prop.access;
519 if (lvalue) {
520 if (prop.set_accessor == null) {
521 error = true;
522 Report.error (source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
523 return false;
525 if (prop.access == SymbolAccessibility.PUBLIC) {
526 access = prop.set_accessor.access;
527 } else if (prop.access == SymbolAccessibility.PROTECTED
528 && prop.set_accessor.access != SymbolAccessibility.PUBLIC) {
529 access = prop.set_accessor.access;
531 } else {
532 if (prop.get_accessor == null) {
533 error = true;
534 Report.error (source_reference, "Property `%s' is write-only".printf (prop.get_full_name ()));
535 return false;
537 if (prop.access == SymbolAccessibility.PUBLIC) {
538 access = prop.get_accessor.access;
539 } else if (prop.access == SymbolAccessibility.PROTECTED
540 && prop.get_accessor.access != SymbolAccessibility.PUBLIC) {
541 access = prop.get_accessor.access;
544 instance = (prop.binding == MemberBinding.INSTANCE);
545 } else if (member is Signal) {
546 instance = true;
549 member.used = true;
551 if (access == SymbolAccessibility.PRIVATE) {
552 var target_type = member.parent_symbol;
554 bool in_target_type = false;
555 for (Symbol this_symbol = analyzer.current_symbol; this_symbol != null; this_symbol = this_symbol.parent_symbol) {
556 if (target_type == this_symbol) {
557 in_target_type = true;
558 break;
562 if (!in_target_type) {
563 error = true;
564 Report.error (source_reference, "Access to private member `%s' denied".printf (member.get_full_name ()));
565 return false;
568 if ((instance && !may_access_instance_members) ||
569 (klass && !may_access_klass_members)) {
570 prototype_access = true;
572 if (symbol_reference is Method) {
573 // also set static type for prototype access
574 // required when using instance methods as delegates in constants
575 // TODO replace by MethodPrototype
576 value_type = analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
577 } else if (symbol_reference is Field) {
578 value_type = new FieldPrototype ((Field) symbol_reference);
579 } else {
580 value_type = new InvalidType ();
583 if (target_type != null) {
584 value_type.value_owned = target_type.value_owned;
586 } else {
587 // implicit this access
588 if (instance && inner == null) {
589 inner = new MemberAccess (null, "this", source_reference);
590 inner.value_type = this_parameter.parameter_type.copy ();
591 inner.symbol_reference = this_parameter;
594 formal_value_type = analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
595 if (inner != null && formal_value_type != null) {
596 value_type = formal_value_type.get_actual_type (inner.value_type, null, this);
597 } else {
598 value_type = formal_value_type;
601 if (symbol_reference is Method) {
602 var m = (Method) symbol_reference;
604 if (target_type != null) {
605 value_type.value_owned = target_type.value_owned;
608 Method base_method;
609 if (m.base_method != null) {
610 base_method = m.base_method;
611 } else if (m.base_interface_method != null) {
612 base_method = m.base_interface_method;
613 } else {
614 base_method = m;
617 if (instance && base_method.parent_symbol is TypeSymbol) {
618 inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) base_method.parent_symbol);
620 } else if (symbol_reference is Property) {
621 var prop = (Property) symbol_reference;
623 Property base_property;
624 if (prop.base_property != null) {
625 base_property = prop.base_property;
626 } else if (prop.base_interface_property != null) {
627 base_property = prop.base_interface_property;
628 } else {
629 base_property = prop;
632 if (instance && base_property.parent_symbol != null) {
633 inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) base_property.parent_symbol);
635 } else if ((symbol_reference is Field
636 || symbol_reference is Signal)
637 && instance && symbol_reference.parent_symbol != null) {
638 inner.target_type = analyzer.get_data_type_for_symbol ((TypeSymbol) symbol_reference.parent_symbol);
642 return !error;
645 public override void get_defined_variables (Collection<LocalVariable> collection) {
646 if (inner != null) {
647 inner.get_defined_variables (collection);
651 public override void get_used_variables (Collection<LocalVariable> collection) {
652 if (inner != null) {
653 inner.get_used_variables (collection);
655 var local = symbol_reference as LocalVariable;
656 if (local != null) {
657 collection.add (local);