1/* valamemberaccess.vala
2 *
3 * Copyright (C) 2006-2012  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 an access to a type member in the source code.
27 */
28public class Vala.MemberAccess : Expression {
29	/**
30	 * The parent of the member.
31	 */
32	public Expression? inner {
33		get {
34			return _inner;
35		}
36		set {
37			_inner = value;
38			if (_inner != null) {
39				_inner.parent_node = this;
40			}
41		}
42	}
43
44	/**
45	 * The name of the member.
46	 */
47	public string member_name { get; set; }
48
49	/**
50	 * Pointer member access.
51	 */
52	public bool pointer_member_access { get; set; }
53
54	/**
55	 * Represents access to an instance member without an actual instance,
56	 * e.g. `MyClass.an_instance_method`.
57	 */
58	public bool prototype_access { get; set; }
59
60	/**
61	 * Requires indirect access due to possible side-effects of parent expression.
62	 */
63	public bool tainted_access { get; set; }
64
65	/**
66	 * Specifies whether the member is used for object creation.
67	 */
68	public bool creation_member { get; set; }
69
70	/**
71	 * Qualified access to global symbol.
72	 */
73	public bool qualified { get; set; }
74
75	private Expression? _inner;
76	private List<DataType> type_argument_list = new ArrayList<DataType> ();
77
78	/**
79	 * Creates a new member access expression.
80	 *
81	 * @param inner            parent of the member
82	 * @param member_name      member name
83	 * @param source_reference reference to source code
84	 * @return                 newly created member access expression
85	 */
86	public MemberAccess (Expression? inner, string member_name, SourceReference? source_reference = null) {
87		this.inner = inner;
88		this.member_name = member_name;
89		this.source_reference = source_reference;
90	}
91
92	public MemberAccess.simple (string member_name, SourceReference? source_reference = null) {
93		this.inner = null;
94		this.member_name = member_name;
95		this.source_reference = source_reference;
96	}
97
98	public MemberAccess.pointer (Expression inner, string member_name, SourceReference? source_reference = null) {
99		this.inner = inner;
100		this.member_name = member_name;
101		this.source_reference = source_reference;
102		pointer_member_access = true;
103	}
104
105	/**
106	 * Appends the specified type as generic type argument.
107	 *
108	 * @param arg a type reference
109	 */
110	public void add_type_argument (DataType arg) {
111		type_argument_list.add (arg);
112		arg.parent_node = this;
113	}
114
115	/**
116	 * Returns the list of generic type arguments.
117	 *
118	 * @return type argument list
119	 */
120	public unowned List<DataType> get_type_arguments () {
121		return type_argument_list;
122	}
123
124	public override void accept (CodeVisitor visitor) {
125		visitor.visit_member_access (this);
126
127		visitor.visit_expression (this);
128	}
129
130	public override void accept_children (CodeVisitor visitor) {
131		if (inner != null) {
132			inner.accept (visitor);
133		}
134
135		foreach (DataType type_arg in type_argument_list) {
136			type_arg.accept (visitor);
137		}
138	}
139
140	public override string to_string () {
141		if (symbol_reference == null || symbol_reference.is_instance_member ()) {
142			if (inner == null) {
143				return member_name;
144			} else {
145				return "%s%s%s".printf (inner.to_string (), pointer_member_access ? "->" : ".", member_name);
146			}
147		} else {
148			// ensure to always use fully-qualified name
149			// to refer to static members
150			return symbol_reference.get_full_name ();
151		}
152	}
153
154	public override void replace_expression (Expression old_node, Expression new_node) {
155		if (inner == old_node) {
156			inner = new_node;
157		}
158	}
159
160	public override bool is_pure () {
161		// accessing property could have side-effects
162		return (inner == null || inner.is_pure ()) && !(symbol_reference is Property);
163	}
164
165	public override bool is_accessible (Symbol sym) {
166		return (inner == null || inner.is_accessible (sym)) && symbol_reference.is_accessible (sym);
167	}
168
169	public override void replace_type (DataType old_type, DataType new_type) {
170		for (int i = 0; i < type_argument_list.size; i++) {
171			if (type_argument_list[i] == old_type) {
172				type_argument_list[i] = new_type;
173				return;
174			}
175		}
176	}
177
178	public override bool is_constant () {
179		unowned Method? method = symbol_reference as Method;
180		if (symbol_reference is Constant) {
181			return true;
182		} else if (symbol_reference is ArrayLengthField && inner != null && inner.symbol_reference is Constant) {
183			// length of constant array
184			return true;
185		} else if (method != null &&
186		           (method.binding == MemberBinding.STATIC || prototype_access)) {
187			return true;
188		} else {
189			return false;
190		}
191	}
192
193	public override bool is_non_null () {
194		unowned Constant? c = symbol_reference as Constant;
195		if (c != null) {
196			return (c is EnumValue || !c.type_reference.nullable);
197		} else {
198			return false;
199		}
200	}
201
202	public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
203		if (inner != null) {
204			inner.get_error_types (collection, source_reference);
205		}
206	}
207
208	public override bool check (CodeContext context) {
209		if (checked) {
210			return !error;
211		}
212
213		checked = true;
214
215		if (inner != null) {
216			inner.check (context);
217		}
218
219		foreach (DataType type_arg in type_argument_list) {
220			type_arg.check (context);
221		}
222
223		unowned Symbol? base_symbol = null;
224		unowned Parameter? this_parameter = null;
225		bool may_access_instance_members = false;
226		bool may_access_klass_members = false;
227
228		symbol_reference = null;
229
230		if (qualified) {
231			base_symbol = context.root;
232			symbol_reference = base_symbol.scope.lookup (member_name);
233		} else if (inner == null) {
234			if (member_name == "this") {
235				if (!context.analyzer.is_in_instance_method ()) {
236					error = true;
237					Report.error (source_reference, "This access invalid outside of instance methods");
238					return false;
239				}
240			}
241
242			base_symbol = context.analyzer.current_symbol;
243
244			// track whether method has been found to make sure that access
245			// to instance member is denied from within static lambda expressions
246			bool method_found = false;
247
248			unowned Symbol? sym = context.analyzer.current_symbol;
249			while (sym != null && symbol_reference == null) {
250				if (!method_found) {
251					if (sym is CreationMethod) {
252						unowned CreationMethod cm = (CreationMethod) sym;
253						this_parameter = cm.this_parameter;
254						may_access_instance_members = true;
255						may_access_klass_members = true;
256						method_found = true;
257					} else if (sym is Property) {
258						unowned Property prop = (Property) sym;
259						this_parameter = prop.this_parameter;
260						may_access_instance_members = (prop.binding == MemberBinding.INSTANCE);
261						may_access_klass_members = (prop.binding != MemberBinding.STATIC);
262						method_found = true;
263					} else if (sym is Constructor) {
264						unowned Constructor c = (Constructor) sym;
265						this_parameter = c.this_parameter;
266						may_access_instance_members = (c.binding == MemberBinding.INSTANCE);
267						may_access_klass_members = true;
268						method_found = true;
269					} else if (sym is Destructor) {
270						unowned Destructor d = (Destructor) sym;
271						this_parameter = d.this_parameter;
272						may_access_instance_members = (d.binding == MemberBinding.INSTANCE);
273						may_access_klass_members = true;
274						method_found = true;
275					} else if (sym is Method) {
276						unowned Method m = (Method) sym;
277						this_parameter = m.this_parameter;
278						may_access_instance_members = (m.binding == MemberBinding.INSTANCE);
279						may_access_klass_members = (m.binding != MemberBinding.STATIC);
280						method_found = true;
281					}
282				}
283
284				symbol_reference = SemanticAnalyzer.symbol_lookup_inherited (sym, member_name);
285
286				if (symbol_reference == null && sym is TypeSymbol && may_access_instance_members) {
287					// used for generated to_string methods in enums
288					symbol_reference = this_parameter.variable_type.get_member (member_name);
289
290					if (symbol_reference != null && is_instance_symbol (symbol_reference)) {
291						// implicit this
292						inner = new MemberAccess (null, "this", source_reference);
293						inner.value_type = this_parameter.variable_type.copy ();
294						inner.value_type.value_owned = false;
295						inner.symbol_reference = this_parameter;
296
297						symbol_reference = inner.value_type.get_member (member_name);
298					}
299				}
300
301				if (symbol_reference == null) {
302					if (sym is TypeSymbol) {
303						// do not allow instance access to outer classes
304						this_parameter = null;
305						may_access_instance_members = false;
306						may_access_klass_members = false;
307					}
308				}
309
310				sym = sym.parent_symbol;
311			}
312
313			if (symbol_reference == null && source_reference != null) {
314				foreach (UsingDirective ns in source_reference.using_directives) {
315					if (ns.error) {
316						// ignore previous error
317						continue;
318					}
319					var local_sym = ns.namespace_symbol.scope.lookup (member_name);
320					if (local_sym != null) {
321						if (symbol_reference != null && symbol_reference != local_sym) {
322							error = true;
323							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 ()));
324							return false;
325						}
326
327						// Transform to fully qualified member access
328						unowned Symbol? inner_sym = local_sym.parent_symbol;
329						unowned MemberAccess? inner_ma = this;
330						while (inner_sym != null && inner_sym.name != null) {
331							inner_ma.inner = new MemberAccess (null, inner_sym.name, source_reference);
332							inner_ma = (MemberAccess) inner_ma.inner;
333							inner_sym = inner_sym.parent_symbol;
334						}
335						inner_ma.qualified = true;
336						inner.check (context);
337
338						symbol_reference = local_sym;
339					}
340				}
341			}
342		} else {
343			if (inner.error) {
344				/* if there was an error in the inner expression, skip this check */
345				error = true;
346				return false;
347			}
348
349			if (inner.value_type is PointerType) {
350				unowned PointerType? pointer_type = inner.value_type as PointerType;
351				if (pointer_type != null && pointer_type.base_type is ValueType) {
352					if (inner.formal_value_type is GenericType) {
353						inner = new CastExpression (inner, pointer_type.copy (), source_reference);
354					}
355					// transform foo->bar to (*foo).bar
356					inner = new PointerIndirection (inner, source_reference);
357					inner.check (context);
358					pointer_member_access = false;
359				}
360			}
361
362			if (inner is MemberAccess) {
363				unowned MemberAccess ma = (MemberAccess) inner;
364				if (ma.prototype_access) {
365					error = true;
366					Report.error (source_reference, "Access to instance member `%s' denied".printf (inner.symbol_reference.get_full_name ()));
367					return false;
368				}
369			}
370
371			if (inner is CastExpression && ((CastExpression) inner).is_silent_cast) {
372				Report.warning (source_reference, "Access to possible `null'. Perform a check or use an unsafe cast.");
373			}
374
375			if (inner is MemberAccess || inner is BaseAccess) {
376				base_symbol = inner.symbol_reference;
377
378				if (symbol_reference == null && (base_symbol is Namespace || base_symbol is TypeSymbol)) {
379					symbol_reference = base_symbol.scope.lookup (member_name);
380					if (inner is BaseAccess) {
381						// inner expression is base access
382						// access to instance members of the base type possible
383						may_access_instance_members = true;
384						may_access_klass_members = true;
385					}
386				}
387			}
388
389			if (inner is MemberAccess && inner.symbol_reference is TypeParameter) {
390				inner.value_type = new GenericType ((TypeParameter) inner.symbol_reference);
391				inner.value_type.source_reference = source_reference;
392			}
393
394			if (symbol_reference == null && inner.value_type != null) {
395				if (pointer_member_access) {
396					symbol_reference = inner.value_type.get_pointer_member (member_name);
397				} else {
398					if (inner.value_type.type_symbol != null) {
399						base_symbol = inner.value_type.type_symbol;
400					}
401					symbol_reference = inner.value_type.get_member (member_name);
402				}
403				if (symbol_reference != null) {
404					// inner expression is variable, field, or parameter
405					// access to instance members of the corresponding type possible
406					may_access_instance_members = true;
407					may_access_klass_members = true;
408				}
409			}
410
411			if (symbol_reference == null && inner.value_type != null && inner.value_type.is_dynamic) {
412				// allow late bound members for dynamic types
413				var dynamic_object_type = (ObjectType) inner.value_type;
414				if (parent_node is MethodCall) {
415					unowned MethodCall invoc = (MethodCall) parent_node;
416					if (invoc.call == this) {
417						// dynamic method
418						DataType ret_type;
419						if (invoc.target_type != null) {
420							ret_type = invoc.target_type.copy ();
421							ret_type.value_owned = true;
422						} else if (invoc.parent_node is ExpressionStatement) {
423							ret_type = new VoidType ();
424						} else {
425							// expect dynamic object of the same type
426							ret_type = inner.value_type.copy ();
427						}
428						var m = new DynamicMethod (inner.value_type, member_name, ret_type, source_reference);
429						m.invocation = invoc;
430						var err = new ErrorType (null, null);
431						err.dynamic_error = true;
432						m.add_error_type (err);
433						m.access = SymbolAccessibility.PUBLIC;
434						m.add_parameter (new Parameter.with_ellipsis ());
435						m.this_parameter = new Parameter ("this", dynamic_object_type.copy (), m.source_reference);
436						dynamic_object_type.type_symbol.scope.add (null, m);
437						symbol_reference = m;
438					}
439				} else if (parent_node is Assignment) {
440					unowned Assignment a = (Assignment) parent_node;
441					if (a.left == this) {
442						// dynamic property assignment
443						var prop = new DynamicProperty (inner.value_type, member_name, source_reference);
444						prop.access = SymbolAccessibility.PUBLIC;
445						prop.set_accessor = new PropertyAccessor (false, true, false, null, null, prop.source_reference);
446						prop.owner = inner.value_type.type_symbol.scope;
447						dynamic_object_type.type_symbol.scope.add (null, prop);
448						symbol_reference = prop;
449					}
450				} else if (parent_node is MemberAccess && inner is MemberAccess && parent_node.parent_node is MethodCall) {
451					unowned MemberAccess ma = (MemberAccess) parent_node;
452					if (ma.member_name == "connect" || ma.member_name == "connect_after") {
453						// dynamic signal
454						var s = new DynamicSignal (inner.value_type, member_name, new VoidType (), source_reference);
455						var mcall = (MethodCall) parent_node.parent_node;
456						// the first argument is the handler
457						if (mcall.get_argument_list().size > 0) {
458							s.handler = mcall.get_argument_list()[0];
459							unowned MemberAccess? arg = s.handler as MemberAccess;
460							if (arg == null || !arg.check (context) || !(arg.symbol_reference is Method)) {
461								error = true;
462								Report.error (s.handler.source_reference, "Invalid handler for `%s'".printf (s.get_full_name ()));
463							}
464						}
465						s.access = SymbolAccessibility.PUBLIC;
466						dynamic_object_type.type_symbol.scope.add (null, s);
467						symbol_reference = s;
468					}
469				}
470				if (symbol_reference == null) {
471					// dynamic property read access
472					var prop = new DynamicProperty (inner.value_type, member_name, source_reference);
473					if (target_type != null) {
474						prop.property_type = target_type;
475					} else {
476						// expect dynamic object of the same type
477						prop.property_type = inner.value_type.copy ();
478					}
479					prop.access = SymbolAccessibility.PUBLIC;
480					prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, prop.source_reference);
481					prop.owner = inner.value_type.type_symbol.scope;
482					dynamic_object_type.type_symbol.scope.add (null, prop);
483					symbol_reference = prop;
484				}
485				if (symbol_reference != null) {
486					may_access_instance_members = true;
487					may_access_klass_members = true;
488				}
489			}
490
491			if (symbol_reference is ArrayResizeMethod && inner.symbol_reference is Variable) {
492				// require the real type with its original value_owned attritubte
493				var inner_type = context.analyzer.get_value_type_for_symbol (inner.symbol_reference, true) as ArrayType;
494				if (inner_type != null && inner_type.inline_allocated) {
495					Report.error (source_reference, "`resize' is not supported for arrays with fixed length");
496					error = true;
497				} else if (inner_type != null && !inner_type.value_owned) {
498					Report.error (source_reference, "`resize' is not allowed for unowned array references");
499					error = true;
500				}
501			}
502		}
503
504		// enum-type inference
505		if (inner == null && symbol_reference == null && target_type != null && target_type.type_symbol is Enum) {
506			unowned Enum enum_type = (Enum) target_type.type_symbol;
507			foreach (var val in enum_type.get_values ()) {
508				if (member_name == val.name) {
509					symbol_reference = val;
510					break;
511				}
512			}
513		}
514
515		if (symbol_reference == null) {
516			error = true;
517
518			string base_type_name = "(null)";
519			unowned Symbol? base_type = null;
520			if (inner != null && inner.value_type != null) {
521				base_type_name = inner.value_type.to_string ();
522				base_type = inner.value_type.type_symbol;
523			} else if (base_symbol != null) {
524				base_type_name = base_symbol.get_full_name ();
525				base_type = base_symbol;
526			}
527
528			string? base_type_package = "";
529			if (base_type != null && base_type.external_package) {
530				base_type_package = base_symbol.source_reference.file.package_name;
531				if (base_type_package != null) {
532					base_type_package = " (%s)".printf (base_type_package);
533				}
534			}
535
536			Report.error (source_reference, "The name `%s' does not exist in the context of `%s'%s".printf (member_name, base_type_name, base_type_package));
537			value_type = new InvalidType ();
538			return false;
539		} else if (symbol_reference.error) {
540			//ignore previous error
541			error = true;
542			return false;
543		}
544
545		if (symbol_reference is Signal) {
546			unowned Signal sig = (Signal) symbol_reference;
547			unowned CodeNode? ma = this;
548			while (ma.parent_node is MemberAccess) {
549				ma = ma.parent_node;
550			}
551			unowned CodeNode? parent = ma.parent_node;
552			if (parent != null && !(parent is ElementAccess) && !(((MemberAccess) ma).inner is BaseAccess)
553			    && (!(parent is MethodCall) || ((MethodCall) parent).get_argument_list ().contains (this))) {
554				if (sig.get_attribute ("HasEmitter") != null) {
555					if (!sig.check (context)) {
556						return false;
557					}
558					symbol_reference = sig.emitter;
559				} else {
560					error = true;
561					Report.error (source_reference, "Signal `%s' requires emitter in this context".printf (symbol_reference.get_full_name ()));
562					return false;
563				}
564			}
565		}
566
567		unowned Symbol? member = symbol_reference;
568		var access = SymbolAccessibility.PUBLIC;
569		bool instance = false;
570		bool klass = false;
571		bool generics = false;
572
573		if (!member.check (context)) {
574			return false;
575		}
576
577		if (member is LocalVariable) {
578			unowned LocalVariable local = (LocalVariable) member;
579			unowned Block? block = local.parent_symbol as Block;
580			if (block != null && SemanticAnalyzer.find_parent_method_or_property_accessor (block) != context.analyzer.current_method_or_property_accessor) {
581				// mark all methods between current method and the captured
582				// block as closures (to support nested closures)
583				unowned Symbol? sym = context.analyzer.current_method_or_property_accessor;
584				while (sym != block) {
585					unowned Method? method = sym as Method;
586					if (method != null) {
587						method.closure = true;
588						// consider captured variables as used
589						// as we require captured variables to be initialized
590						method.add_captured_variable (local);
591					}
592					sym = sym.parent_symbol;
593				}
594
595				local.captured = true;
596				block.captured = true;
597
598				if (local.variable_type.type_symbol == context.analyzer.va_list_type.type_symbol) {
599					error = true;
600					Report.error (source_reference, "Capturing `va_list' variable `%s' is not allowed".printf (local.get_full_name ()));
601				}
602			}
603		} else if (member is Parameter) {
604			unowned Parameter param = (Parameter) member;
605			unowned Method? m = param.parent_symbol as Method;
606			if (m != null && m != context.analyzer.current_method_or_property_accessor && param != m.this_parameter) {
607				// mark all methods between current method and the captured
608				// parameter as closures (to support nested closures)
609				unowned Symbol? sym = context.analyzer.current_method_or_property_accessor;
610				while (sym != m) {
611					unowned Method? method = sym as Method;
612					if (method != null) {
613						method.closure = true;
614					}
615					sym = sym.parent_symbol;
616				}
617
618				param.captured = true;
619				m.body.captured = true;
620
621				if (param.direction != ParameterDirection.IN) {
622					error = true;
623					Report.error (source_reference, "Cannot capture reference or output parameter `%s'".printf (param.get_full_name ()));
624				}
625				if (param.variable_type.type_symbol == context.analyzer.va_list_type.type_symbol) {
626					error = true;
627					Report.error (source_reference, "Capturing `va_list' parameter `%s' is not allowed".printf (param.get_full_name ()));
628				}
629			} else {
630				unowned PropertyAccessor? acc = param.parent_symbol.parent_symbol as PropertyAccessor;
631				if (acc != null && acc != context.analyzer.current_method_or_property_accessor && param != acc.prop.this_parameter) {
632					// mark all methods between current method and the captured
633					// parameter as closures (to support nested closures)
634					unowned Symbol? sym = context.analyzer.current_method_or_property_accessor;
635					while (sym != m) {
636						unowned Method? method = sym as Method;
637						if (method != null) {
638							method.closure = true;
639						}
640						sym = sym.parent_symbol;
641					}
642
643					param.captured = true;
644					acc.body.captured = true;
645				}
646			}
647		} else if (member is Field) {
648			unowned Field f = (Field) member;
649			access = f.access;
650			instance = (f.binding == MemberBinding.INSTANCE);
651			klass = (f.binding == MemberBinding.CLASS);
652
653			// do not allow access to fields of generic types
654			// if instance type does not specify type arguments
655			if (f.variable_type is GenericType) {
656				generics = true;
657			}
658		} else if (member is Constant) {
659			unowned Constant c = (Constant) member;
660			access = c.access;
661
662			unowned Block? block = c.parent_symbol as Block;
663			if (block != null && SemanticAnalyzer.find_parent_method_or_property_accessor (block) != context.analyzer.current_method_or_property_accessor) {
664				error = true;
665				Report.error (source_reference, "internal error: accessing local constants of outer methods is not supported yet");
666				return false;
667			}
668		} else if (member is Method) {
669			unowned Method m = (Method) member;
670			if (m.is_async_callback) {
671				// ensure to use right callback method for virtual/abstract async methods
672				// and also for lambda expressions within async methods
673				unowned Method? async_method = context.analyzer.current_async_method;
674
675				bool is_valid_access = false;
676				if (async_method != null) {
677					if (m == async_method.get_callback_method ()) {
678						is_valid_access = true;
679					} else if (async_method.base_method != null && m == async_method.base_method.get_callback_method ()) {
680						is_valid_access = true;
681					} else if (async_method.base_interface_method != null && m == async_method.base_interface_method.get_callback_method ()) {
682						is_valid_access = true;
683					}
684				}
685				if (!is_valid_access) {
686					error = true;
687					Report.error (source_reference, "Access to async callback `%s' not allowed in this context".printf (m.get_full_name ()));
688					return false;
689				}
690
691				if (async_method != context.analyzer.current_method) {
692					unowned Symbol? sym = context.analyzer.current_method;
693					while (sym != async_method) {
694						unowned Method? method = sym as Method;
695						if (method != null) {
696							method.closure = true;
697						}
698						sym = sym.parent_symbol;
699					}
700					async_method.body.captured = true;
701				}
702
703				m = async_method.get_callback_method ();
704				symbol_reference = m;
705				member = symbol_reference;
706			} else if (m.base_method != null) {
707				// refer to base method to inherit default arguments
708				m = m.base_method;
709
710				if (m.signal_reference != null) {
711					// method is class/default handler for a signal
712					// let signal deal with member access
713					symbol_reference = m.signal_reference;
714				} else {
715					symbol_reference = m;
716				}
717
718				member = symbol_reference;
719			} else if (m.base_interface_method != null) {
720				// refer to base method to inherit default arguments
721				m = m.base_interface_method;
722
723				if (m.signal_reference != null) {
724					// method is class/default handler for a signal
725					// let signal deal with member access
726					symbol_reference = m.signal_reference;
727				} else {
728					symbol_reference = m;
729				}
730
731				member = symbol_reference;
732			}
733			access = m.access;
734			if (!(m is CreationMethod)) {
735				instance = (m.binding == MemberBinding.INSTANCE);
736			}
737			klass = (m.binding == MemberBinding.CLASS);
738
739			// do not allow access to methods using generic type parameters
740			// if instance type does not specify type arguments
741			foreach (var param in m.get_parameters ()) {
742				unowned GenericType? generic_type = param.variable_type as GenericType;
743				if (generic_type != null && generic_type.type_parameter.parent_symbol is TypeSymbol) {
744					generics = true;
745					break;
746				}
747			}
748			unowned GenericType? generic_type = m.return_type as GenericType;
749			if (generic_type != null && generic_type.type_parameter.parent_symbol is TypeSymbol) {
750				generics = true;
751			}
752		} else if (member is Property) {
753			unowned Property prop = (Property) member;
754			if (!prop.check (context)) {
755				error = true;
756				return false;
757			}
758			if (prop.base_property != null) {
759				// refer to base property
760				prop = prop.base_property;
761				symbol_reference = prop;
762				member = symbol_reference;
763			} else if (prop.base_interface_property != null) {
764				// refer to base property
765				prop = prop.base_interface_property;
766				symbol_reference = prop;
767				member = symbol_reference;
768			}
769			access = prop.access;
770			if (lvalue) {
771				if (prop.set_accessor == null) {
772					error = true;
773					Report.error (source_reference, "Property `%s' is read-only".printf (prop.get_full_name ()));
774					return false;
775				} else if (!prop.set_accessor.writable && prop.set_accessor.construction) {
776					if (context.analyzer.find_current_method () is CreationMethod) {
777						error = true;
778						Report.error (source_reference, "Cannot assign to construct-only properties, use Object (property: value) constructor chain up");
779						return false;
780					} else if (context.analyzer.is_in_constructor ()) {
781						if (!context.analyzer.current_type_symbol.is_subtype_of ((TypeSymbol) prop.parent_symbol)) {
782							error = true;
783							Report.error (source_reference, "Cannot assign to construct-only property `%s' in `construct' of `%s'".printf (prop.get_full_name (), context.analyzer.current_type_symbol.get_full_name ()));
784							return false;
785						}
786					} else {
787						error = true;
788						Report.error (source_reference, "Cannot assign to construct-only property in this context");
789						return false;
790					}
791				}
792				if (prop.access == SymbolAccessibility.PUBLIC) {
793					access = prop.set_accessor.access;
794				} else if (prop.access == SymbolAccessibility.PROTECTED
795				           && prop.set_accessor.access != SymbolAccessibility.PUBLIC) {
796					access = prop.set_accessor.access;
797				}
798			} else {
799				if (prop.get_accessor == null) {
800					error = true;
801					Report.error (source_reference, "Property `%s' is write-only".printf (prop.get_full_name ()));
802					return false;
803				}
804				if (prop.access == SymbolAccessibility.PUBLIC) {
805					access = prop.get_accessor.access;
806				} else if (prop.access == SymbolAccessibility.PROTECTED
807				           && prop.get_accessor.access != SymbolAccessibility.PUBLIC) {
808					access = prop.get_accessor.access;
809				}
810			}
811			instance = (prop.binding == MemberBinding.INSTANCE);
812
813			// do not allow access to properties of generic types
814			// if instance type does not specify type arguments
815			if (prop.property_type is GenericType) {
816				generics = true;
817			}
818		} else if (member is Signal) {
819			instance = true;
820			access = member.access;
821		} else if (!creation_member && member is ErrorCode) {
822			symbol_reference = ((ErrorCode) member).code;
823			member = symbol_reference;
824		}
825
826		// recursive usage of itself doesn't count as used
827		unowned CodeNode? parent = this;
828		while (parent != member) {
829			parent = parent.parent_node;
830			if (parent == null || parent == member) {
831				break;
832			}
833		}
834		if (parent != member) {
835			member.used = true;
836		}
837		member.version.check (source_reference);
838
839		if (access == SymbolAccessibility.PROTECTED && member.parent_symbol is TypeSymbol) {
840			unowned TypeSymbol target_type = (TypeSymbol) member.parent_symbol;
841
842			bool in_subtype = false;
843			for (Symbol this_symbol = context.analyzer.current_symbol; this_symbol != null; this_symbol = this_symbol.parent_symbol) {
844				if (this_symbol == target_type) {
845					// required for interfaces with non-abstract methods
846					// accessing protected interface members
847					in_subtype = true;
848					break;
849				}
850
851				unowned Class? cl = this_symbol as Class;
852				if (cl != null && cl.is_subtype_of (target_type)) {
853					in_subtype = true;
854					break;
855				}
856			}
857
858			if (!in_subtype) {
859				error = true;
860				Report.error (source_reference, "Access to protected member `%s' denied".printf (member.get_full_name ()));
861				return false;
862			}
863		} else if (access == SymbolAccessibility.PRIVATE) {
864			unowned Symbol? target_type = member.parent_symbol;
865
866			bool in_target_type = false;
867			for (Symbol this_symbol = context.analyzer.current_symbol; this_symbol != null; this_symbol = this_symbol.parent_symbol) {
868				if (target_type == this_symbol) {
869					in_target_type = true;
870					break;
871				}
872			}
873
874			if (!in_target_type) {
875				error = true;
876				Report.error (source_reference, "Access to private member `%s' denied".printf (member.get_full_name ()));
877				return false;
878			}
879		}
880
881		if (generics && inner != null) {
882			unowned DataType instance_type = inner.value_type;
883			unowned PointerType? pointer_type = inner.value_type as PointerType;
884			if (pointer_type != null) {
885				instance_type = pointer_type.base_type;
886			}
887
888			// instance type might be a subtype of the parent symbol of the member
889			// that subtype might not be generic, so do not report an error in that case
890			unowned ObjectType? object_type = instance_type as ObjectType;
891			if (object_type != null && object_type.object_type_symbol.has_type_parameters ()
892			    && !instance_type.has_type_arguments ()) {
893				error = true;
894				Report.error (inner.source_reference, "missing generic type arguments");
895				return false;
896			}
897		}
898
899		if ((instance && !may_access_instance_members) ||
900		    (klass && !may_access_klass_members)) {
901			prototype_access = true;
902
903			if (symbol_reference is Method) {
904				// also set static type for prototype access
905				// required when using instance methods as delegates in constants
906				// TODO replace by MethodPrototype
907				value_type = context.analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
908			} else if (symbol_reference is Field) {
909				value_type = new FieldPrototype ((Field) symbol_reference);
910			} else if (symbol_reference is Property) {
911				value_type = new PropertyPrototype ((Property) symbol_reference);
912			} else {
913				error = true;
914				value_type = new InvalidType ();
915			}
916
917			if (target_type != null) {
918				value_type.value_owned = target_type.value_owned;
919			}
920		} else {
921			// implicit this access
922			if (instance && inner == null) {
923				inner = new MemberAccess (null, "this", source_reference);
924				inner.value_type = this_parameter.variable_type.copy ();
925				inner.value_type.value_owned = false;
926				inner.symbol_reference = this_parameter;
927			} else {
928				check_lvalue_access ();
929			}
930
931			if (!instance && !klass && !(symbol_reference is CreationMethod) && may_access_instance_members && inner != null) {
932				if (inner.symbol_reference is Method) {
933					// do not warn when calling .begin or .end on static async method
934				} else {
935					Report.warning (source_reference, "Access to static member `%s' with an instance reference".printf (symbol_reference.get_full_name ()));
936
937					// Transform to static member access
938					unowned Symbol? inner_sym = symbol_reference.parent_symbol;
939					unowned MemberAccess? inner_ma = this;
940					while (inner_sym != null && inner_sym.name != null) {
941						inner_ma.inner = new MemberAccess (null, inner_sym.name, source_reference);
942						inner_ma = (MemberAccess) inner_ma.inner;
943						inner_sym = inner_sym.parent_symbol;
944					}
945					inner_ma.qualified = true;
946					inner.check (context);
947				}
948			}
949
950			if (context.experimental_non_null && instance && inner.value_type.nullable &&
951			    !(inner.value_type is PointerType) && !(inner.value_type is GenericType) &&
952				!(inner.value_type is ArrayType)) {
953				Report.error (source_reference, "Access to instance member `%s' from nullable reference denied".printf (symbol_reference.get_full_name ()));
954			}
955
956			unowned Method? m = symbol_reference as Method;
957			unowned MemberAccess? inner_ma = inner as MemberAccess;
958			if (m != null && m.binding == MemberBinding.STATIC && m.parent_symbol is ObjectTypeSymbol &&
959			    inner != null && inner.value_type == null && inner_ma.type_argument_list.size > 0) {
960				// support static methods in generic classes
961				inner.value_type = new ObjectType ((ObjectTypeSymbol) m.parent_symbol);
962
963				foreach (var type_argument in inner_ma.type_argument_list) {
964					inner.value_type.add_type_argument (type_argument);
965				}
966			}
967
968			formal_value_type = context.analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
969			if (inner != null && formal_value_type != null) {
970				value_type = formal_value_type.get_actual_type (inner.value_type, null, this);
971			} else {
972				value_type = formal_value_type;
973			}
974
975			if (symbol_reference is Method) {
976				unowned Method method = (Method) symbol_reference;
977				if (target_type != null) {
978					value_type.value_owned = target_type.value_owned;
979				}
980				if (instance && method.parent_symbol is TypeSymbol) {
981					inner.target_type = SemanticAnalyzer.get_data_type_for_symbol (method.parent_symbol);
982					inner.target_type.value_owned = method.this_parameter.variable_type.value_owned;
983				}
984			} else if (symbol_reference is Property
985			    && instance && symbol_reference.parent_symbol != null) {
986				inner.target_type = SemanticAnalyzer.get_data_type_for_symbol (symbol_reference.parent_symbol);
987			} else if ((symbol_reference is Field || symbol_reference is Signal)
988			    && instance && symbol_reference.parent_symbol != null) {
989				var parent_type = SemanticAnalyzer.get_data_type_for_symbol (symbol_reference.parent_symbol);
990				inner.target_type = parent_type.get_actual_type (inner.value_type, null, this);
991			}
992		}
993
994		if (value_type != null) {
995			value_type.check (context);
996		}
997
998		// Provide some extra information for the code generator
999		if (!tainted_access) {
1000			tainted_access = is_tainted ();
1001		}
1002
1003		return !error;
1004	}
1005
1006	static bool is_instance_symbol (Symbol symbol) {
1007		if (symbol is Field && ((Field) symbol).binding == MemberBinding.INSTANCE) {
1008			return true;
1009		} else if (symbol is Method && !(symbol is CreationMethod) && ((Method) symbol).binding == MemberBinding.INSTANCE) {
1010			return true;
1011		} else if (symbol is Property && ((Property) symbol).binding == MemberBinding.INSTANCE) {
1012			return true;
1013		} else if (symbol is Signal) {
1014			return true;
1015		} else {
1016			return false;
1017		}
1018	}
1019
1020	public void check_lvalue_access () {
1021		if (inner == null) {
1022			return;
1023		}
1024		var instance = symbol_reference is Field && ((Field) symbol_reference).binding == MemberBinding.INSTANCE;
1025		if (!instance) {
1026			instance = symbol_reference is Method && ((Method) symbol_reference).binding == MemberBinding.INSTANCE;
1027		}
1028		if (!instance) {
1029			instance = symbol_reference is Property && ((Property) symbol_reference).binding == MemberBinding.INSTANCE;
1030		}
1031
1032		var this_access = inner.symbol_reference is Parameter && inner.symbol_reference.name == "this";
1033		var struct_or_array = (inner.value_type is StructValueType && !inner.value_type.nullable) || inner.value_type is ArrayType;
1034
1035		unowned MemberAccess? ma = inner as MemberAccess;
1036		if (ma == null && struct_or_array && inner is PointerIndirection) {
1037			// (*struct)->method()
1038			ma = ((PointerIndirection) inner).inner as MemberAccess;
1039		}
1040
1041		if (instance && struct_or_array && (symbol_reference is Method || lvalue) && ((ma != null && ma.symbol_reference is Variable) || inner is ElementAccess) && !this_access) {
1042			inner.lvalue = true;
1043			if (ma != null) {
1044				ma.lvalue = true;
1045				ma.check_lvalue_access ();
1046			}
1047		}
1048
1049		if (symbol_reference is Method && ((Method) symbol_reference).get_attribute ("DestroysInstance") != null) {
1050			unowned Class? cl = ((Method) symbol_reference).parent_symbol as Class;
1051			if (cl != null && cl.is_compact && ma != null) {
1052				ma.lvalue = true;
1053				ma.check_lvalue_access ();
1054			}
1055		}
1056	}
1057
1058	public override void emit (CodeGenerator codegen) {
1059		if (inner != null) {
1060			inner.emit (codegen);
1061		}
1062
1063		codegen.visit_member_access (this);
1064
1065		codegen.visit_expression (this);
1066	}
1067
1068	public override void get_defined_variables (Collection<Variable> collection) {
1069		if (inner != null) {
1070			inner.get_defined_variables (collection);
1071		}
1072	}
1073
1074	public override void get_used_variables (Collection<Variable> collection) {
1075		if (inner != null) {
1076			inner.get_used_variables (collection);
1077		}
1078		unowned LocalVariable? local = symbol_reference as LocalVariable;
1079		unowned Parameter? param = symbol_reference as Parameter;
1080		if (local != null) {
1081			collection.add (local);
1082		} else if (param != null && param.direction == ParameterDirection.OUT) {
1083			collection.add (param);
1084		}
1085	}
1086
1087	bool is_tainted () {
1088		unowned CodeNode node = this;
1089		if (node.parent_node is MemberAccess) {
1090			return false;
1091		}
1092
1093		while (node.parent_node is Expression) {
1094			node = node.parent_node;
1095			if (node is Assignment || node is MethodCall || node is ObjectCreationExpression) {
1096				break;
1097			}
1098		}
1099
1100		bool found = false;
1101		var traverse = new TraverseVisitor ((n) => {
1102			if (n is PostfixExpression) {
1103				found = true;
1104				return TraverseStatus.STOP;
1105			} else if (n is UnaryExpression) {
1106				unowned UnaryExpression e = (UnaryExpression) n;
1107				if (e.operator == UnaryOperator.INCREMENT || e.operator == UnaryOperator.DECREMENT) {
1108					found = true;
1109					return TraverseStatus.STOP;
1110				}
1111			}
1112			return TraverseStatus.CONTINUE;
1113		});
1114		node.accept (traverse);
1115
1116		return found;
1117	}
1118}
1119