Skip to content
← Back

src/semantic/awaitable_resolver.ghul

1
namespace Semantic is
2
use Source.LOCATION
3
4
use Types.Type
5
6
// What one `await` reaches on its operand: the operand's
7
// `get_awaiter`, and on the awaiter that returns, `is_completed`
8
// and `get_result`. The result type of the `await` is whatever
9
// `get_result` returns, void included.
10
class AWAITABLE is
11
awaiter_type: Type
12
get_awaiter: Symbols.Function
13
is_completed: Symbols.Function
14
get_result: Symbols.Function
15
16
// The awaiter implements `ICriticalNotifyCompletion`, so the
17
// continuation is registered through the builder's
18
// `AwaitUnsafeOnCompleted`; otherwise through
19
// `AwaitOnCompleted`.
20
is_critical: bool
21
22
result_type: Type => get_result.return_type!
23
24
init(
25
awaiter_type: Type,
26
get_awaiter: Symbols.Function,
27
is_completed: Symbols.Function,
28
get_result: Symbols.Function,
29
is_critical: bool
30
) is
31
self.awaiter_type = awaiter_type
32
self.get_awaiter = get_awaiter
33
self.is_completed = is_completed
34
self.get_result = get_result
35
self.is_critical = is_critical
36
si
37
si
38
39
// Resolves the awaiter pattern against an operand type by member
40
// lookup, the way C# does: a parameterless `get_awaiter` whose
41
// result has a `bool` property `is_completed`, a parameterless
42
// `get_result`, and implements `INotifyCompletion`. Nothing here
43
// is specific to `Task`; a `ValueTask`, `Tasks.TASK.yield()` or
44
// a user-defined awaitable resolves the same way.
45
//
46
// `resolve` reports one error naming the first missing piece and
47
// returns null; the caller substitutes an error-typed value.
48
// `try_resolve` answers the same question silently, for a caller
49
// asking whether a value could be awaited rather than awaiting it.
50
class AWAITABLE_RESOLVER(
51
_innate_symbol_lookup: Lookups.InnateSymbolLookup,
52
_logger: Logging.Logger
53
) is
54
resolve(location: LOCATION, type: Type) -> AWAITABLE? =>
55
_resolve(location, type, true)
56
57
try_resolve(type: Type) -> AWAITABLE? =>
58
_resolve(LOCATION.internal, type, false)
59
60
_resolve(location: LOCATION, type: Type, report: bool) -> AWAITABLE? is
61
// An operand whose type failed to resolve has already been
62
// reported where the failure was.
63
if type.is_error then
64
return null
65
fi
66
67
let get_awaiter = _zero_argument_function(type, "get_awaiter")
68
69
if !get_awaiter? then
70
return _fail(report, location, "cannot await {type}: no get_awaiter method")
71
fi
72
73
let awaiter_type = get_awaiter.return_type
74
75
if !awaiter_type? \/ awaiter_type.is_void then
76
return _fail(report, location, "cannot await {type}: get_awaiter returns no value")
77
fi
78
79
let is_completed = _property_getter(awaiter_type, "is_completed")
80
81
if !is_completed? then
82
return _fail(report, location, "cannot await {type}: awaiter {awaiter_type} has no is_completed property")
83
fi
84
85
let is_completed_type = is_completed.return_type
86
87
if !is_completed_type? \/ !is_completed_type.is_equivalent_to(_innate_symbol_lookup.get_bool_type()) then
88
return _fail(report, location, "cannot await {type}: awaiter {awaiter_type} is_completed is not bool")
89
fi
90
91
let get_result = _zero_argument_function(awaiter_type, "get_result")
92
93
if !get_result? then
94
return _fail(report, location, "cannot await {type}: awaiter {awaiter_type} has no get_result method")
95
fi
96
97
if !get_result.return_type? then
98
return _fail(report, location, "cannot await {type}: awaiter {awaiter_type} get_result has no return type")
99
fi
100
101
let is_critical = _implements(awaiter_type, _innate_symbol_lookup.get_critical_notify_completion_type())
102
103
if !is_critical /\ !_implements(awaiter_type, _innate_symbol_lookup.get_notify_completion_type()) then
104
return _fail(report, location, "cannot await {type}: awaiter {awaiter_type} does not implement System.Runtime.CompilerServices.INotifyCompletion")
105
fi
106
107
return AWAITABLE(awaiter_type, get_awaiter, is_completed, get_result, is_critical)
108
si
109
110
_fail(report: bool, location: LOCATION, message: string) -> AWAITABLE? is
111
if report then
112
_logger.error(location, message)
113
fi
114
115
return null
116
si
117
118
_implements(type: Type, interface: Type?) -> bool =>
119
interface? /\ interface.is_assignable_from(type)
120
121
// Each member is called on a receiver, so only an instance
122
// member with no parameters qualifies; a static of the same
123
// name is not part of the pattern.
124
_zero_argument_function(type: Type, name: string) -> Symbols.Function? is
125
if let group: Symbols.FUNCTION_GROUP = type.find_member(name) then
126
for f in group.functions do
127
if f.is_instance /\ f.arguments.count == 0 then
128
return f
129
fi
130
od
131
fi
132
133
return null
134
si
135
136
_property_getter(type: Type, name: string) -> Symbols.Function? is
137
if let property: Symbols.Property = type.find_member(name) then
138
if let getter = property.read_function /\ getter.is_instance then
139
return getter
140
fi
141
fi
142
143
return null
144
si
145
si
146
si