summaryrefslogtreecommitdiff
path: root/vala/valadelegatetype.vala
blob: 8b51f324cc0cf70ef16156b027e76095ed82825b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/* valadelegatetype.vala
 *
 * Copyright (C) 2007-2012  Jürg Billeter
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
 */

using GLib;

/**
 * The type of an instance of a delegate.
 */
public class Vala.DelegateType : CallableType {
	public weak Delegate delegate_symbol {
		get {
			return (Delegate) symbol;
		}
	}

	public bool is_called_once { get; set; }

	DelegateTargetField? target_field;
	DelegateDestroyField? destroy_field;

	public DelegateType (Delegate delegate_symbol, SourceReference? source_reference = null) {
		base (delegate_symbol, source_reference);
		this.is_called_once = (delegate_symbol.get_attribute_string ("CCode", "scope") == "async");
	}

	public override Symbol? get_member (string member_name) {
		if (member_name == "target") {
			return get_target_field ();
		} else if (member_name == "destroy") {
			return get_destroy_field ();
		}
		return null;
	}

	unowned DelegateTargetField get_target_field () {
		if (target_field == null) {
			target_field = new DelegateTargetField (source_reference);
			target_field.access = SymbolAccessibility.PUBLIC;
		}
		return target_field;
	}

	unowned DelegateDestroyField get_destroy_field () {
		if (destroy_field == null) {
			destroy_field = new DelegateDestroyField (source_reference);
			destroy_field.access = SymbolAccessibility.PUBLIC;
		}
		return destroy_field;
	}

	public override DataType copy () {
		var result = new DelegateType (delegate_symbol, source_reference);
		result.value_owned = value_owned;
		result.nullable = nullable;

		foreach (DataType arg in get_type_arguments ()) {
			result.add_type_argument (arg.copy ());
		}

		result.is_called_once = is_called_once;

		return result;
	}

	public override bool equals (DataType type2) {
		return compatible (type2);
	}

	public override bool is_accessible (Symbol sym) {
		return delegate_symbol.is_accessible (sym);
	}

	public override bool check (CodeContext context) {
		if (is_called_once && !value_owned) {
			Report.warning (source_reference, "delegates with scope=\"async\" must be owned");
		}

		if (!delegate_symbol.check (context)) {
			error = true;
			return false;
		}

		// check whether there is the expected amount of type-arguments
		if (!check_type_arguments (context, true)) {
			error = true;
			return false;
		}

		return true;
	}

	public override bool compatible (DataType target_type) {
		unowned DelegateType? dt_target = target_type as DelegateType;
		if (dt_target == null) {
			return false;
		}

		// trivial case
		if (delegate_symbol == dt_target.delegate_symbol) {
			return true;
		}

		if (delegate_symbol.has_target != dt_target.delegate_symbol.has_target) {
			return false;
		}

		// target-delegate is allowed to ensure stricter return type (stronger postcondition)
		if (!get_return_type ().stricter (dt_target.get_return_type ().get_actual_type (dt_target, null, this))) {
			return false;
		}

		var parameters = get_parameters ();
		Iterator<Parameter> params_it = parameters.iterator ();

		if (dt_target.delegate_symbol.parent_symbol is Signal && dt_target.delegate_symbol.sender_type != null && parameters.size == dt_target.get_parameters ().size + 1) {
			// target-delegate has sender parameter
			params_it.next ();

			// target-delegate is allowed to accept arguments of looser types (weaker precondition)
			var p = params_it.get ();
			if (!dt_target.delegate_symbol.sender_type.stricter (p.variable_type)) {
				return false;
			}
		}

		foreach (Parameter param in dt_target.get_parameters ()) {
			if (!params_it.next ()) {
				return false;
			}

			var p = params_it.get ();
			if (p != null && (param.ellipsis || param.params_array)) {
				if (param.ellipsis != p.ellipsis || param.params_array != p.params_array) {
					return false;
				}
				break;
			}

			// target-delegate is allowed to accept arguments of looser types (weaker precondition)
			if (!param.variable_type.get_actual_type (this, null, this).stricter (p.variable_type)) {
				return false;
			}
		}

		/* target-delegate may not expect more arguments */
		if (params_it.next ()) {
			return false;
		}

		// target-delegate may throw less but not more errors than the delegate
		var error_types = new ArrayList<DataType> ();
		delegate_symbol.get_error_types (error_types);
		foreach (DataType error_type in error_types) {
			bool match = false;
			var delegate_error_types = new ArrayList<DataType> ();
			dt_target.delegate_symbol.get_error_types (delegate_error_types);
			foreach (DataType delegate_error_type in delegate_error_types) {
				if (error_type.compatible (delegate_error_type)) {
					match = true;
					break;
				}
			}

			if (!match) {
				return false;
			}
		}

		return true;
	}

	public override bool is_disposable () {
		return delegate_symbol.has_target && value_owned && !is_called_once;
	}
}