Skip to content
← Back

src/syntax/trees/expressions/call.ghul

1
namespace Syntax.Trees.Expressions is
2
3
use Source
4
use Logging
5
6
use Ghul.Pipes
7
8
class CALL(location: LOCATION, function: Expression, arguments: LIST): Expression is
9
right_location: LOCATION => function.right_location
10
could_be_nested_function_definition: bool => function.is_unqualified_identifier
11
12
// Constraint pushed in by the parent context. For a constructor
13
// call (e.g. `Box()` against `let b: Box[int]`) the visitor uses
14
// it to bind the owner generic arguments when they can't be
15
// inferred from the supplied constructor arguments alone.
16
17
// Parallel to `arguments` when the call uses named-argument
18
// syntax (`f(width = 800, height = 600)`); null for a
19
// positional call. Set by `rewrite_named_arguments`, which
20
// also unwraps each argument to its value expression so the
21
// rest of the pipeline sees an ordinary positional list.
22
argument_names: Collections.List[Identifiers.Identifier]?
23
24
// True for a `|>` thread-first call. The subject has been inserted
25
// as the first element of `arguments`, so the call resolves and
26
// emits like any other; the flag only tells the formatter to
27
// render `arguments[0] |> function(rest)` and lets the constructor
28
// and named-argument checks reject the forms `|>` disallows.
29
is_thread_first: bool public
30
31
// True for a `~>` thread-first call. The subject was threaded
32
// in as with `|>`, but the call is lowered inside a presence
33
// test on it: an absent subject skips the call - arguments
34
// included - and yields the absent value widened to the
35
// optional shape of the call's result. Plain `|>` leaves
36
// this false.
37
propagates_absence: bool public
38
39
// For a `|>` call, the span running from the operator through the
40
// called name - what the completer treats as the position whose
41
// candidates are the functions the subject can be threaded into.
42
// Empty on every other call.
43
completion_target: LOCATION? public
44
45
super(location)
46
47
set_expected_type(expected_type: Semantic.Types.Type?, error_message: string?) is
48
compile_expressions_state.set_expected_type(expected_type, error_message)
49
si
50
51
// `f(a = 1, b = 2)` parses with each argument as a `VARIABLE`:
52
// the shared list parser treats `name = value` like a
53
// tuple-element precursor. Detect that shape, record the
54
// names, and unwrap each argument to its value expression so
55
// every later pass sees an ordinary positional list. A call
56
// must be all-named or all-positional.
57
rewrite_named_arguments(logger: Logger) is
58
let elements = arguments.expressions
59
60
if elements.count == 0 then
61
return
62
fi
63
64
let named_count = elements |> filter(e => _is_named_argument(e)) |> count()
65
66
if named_count == 0 then
67
return
68
fi
69
70
if named_count < elements.count then
71
logger.error(location, "cannot mix named and positional arguments")
72
73
for index in 0..elements.count do
74
if _is_named_argument(elements[index]) then
75
elements[index] = (cast VARIABLE?(elements[index])!).initializer!
76
fi
77
od
78
79
return
80
fi
81
82
let names = Collections.LIST[Identifiers.Identifier]()
83
84
for index in 0..elements.count do
85
let variable = cast VARIABLE?(elements[index])!
86
87
if let first = names |> find(seen => seen.name =~ variable.name.name) then
88
logger.error(variable.name.location, "named argument {variable.name.name} supplied more than once", first.location, "first supplied here")
89
fi
90
91
names.add(variable.name)
92
elements[index] = variable.initializer!
93
od
94
95
argument_names = names
96
si
97
98
// A named argument is `name = value`: a VARIABLE node carrying
99
// an initializer. A bare `name: type` (a VARIABLE with no
100
// initializer) is not a named argument - it only arises from
101
// parser error recovery, and is left for the normal error
102
// paths rather than reported here.
103
_is_named_argument(e: Expression) -> bool =>
104
isa VARIABLE(e) /\ (cast VARIABLE(e)).initializer?
105
106
accept(visitor: Visitor) is
107
visitor.visit(self)
108
si
109
110
walk(visitor: Visitor) is
111
// enter/leave_node wrap the whole visit so the ambient
112
// location covers Values built by `pre()` overrides that
113
// take control of the walk — `pre(CALL)` returns true and
114
// `visit_call` does its own resolver-driven walk, skipping
115
// the default recursion below.
116
visitor.enter_node(self)
117
try
118
if !visitor.pre(self) then
119
arguments.walk(visitor)
120
function.walk(visitor)
121
fi
122
123
accept(visitor)
124
finally
125
visitor.leave_node(self)
126
yrt
127
si
128
129
replace_child(old: Expression, replacement: Expression) is
130
if function == old then
131
function = replacement
132
fi
133
// Argument elements are replaced via arguments.replace_element
134
// by callers that have the index — no parent-driven lookup
135
// here (it would require a linear scan; the SPILL_AWAITS
136
// pass already has the index).
137
si
138
si
139
si