naga/proc/overloads/
mod.rs

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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/*! Overload resolution for builtin functions.

This module defines the [`OverloadSet`] trait, which provides methods the
validator and typifier can use to check the types to builtin functions,
determine their result types, and produce diagnostics that explain why a given
application is not allowed and suggest fixes.

You can call [`MathFunction::overloads`] to obtain an `impl OverloadSet`
representing the given `MathFunction`'s overloads.

[`MathFunction::overloads`]: crate::ir::MathFunction::overloads

*/

mod constructor_set;
mod regular;
mod scalar_set;

mod any_overload_set;
mod list;
mod mathfunction;
mod one_bits_iter;
mod rule;
mod utils;

pub use rule::{Conclusion, MissingSpecialType, Rule};

use crate::ir;
use crate::proc::TypeResolution;

use alloc::vec::Vec;
use core::fmt;

#[expect(rustdoc::private_intra_doc_links)]
/// A trait for types representing of a set of Naga IR type rules.
///
/// Given an expression like `max(x, y)`, there are multiple type rules that
/// could apply, depending on the types of `x` and `y`, like:
///
/// - `max(i32, i32) -> i32`
/// - `max(vec4<f32>, vec4<f32>) -> vec4<f32>`
///
/// and so on. Borrowing WGSL's terminology, Naga calls the full set of type
/// rules that might apply to a given expression its "overload candidates", or
/// "overloads" for short.
///
/// This trait is meant to be implemented by types that represent a set of
/// overload candidates. For example, [`MathFunction::overloads`] returns an
/// `impl OverloadSet` describing the overloads for the given Naga IR math
/// function. Naga's typifier, validator, and WGSL front end use this trait for
/// their work.
///
/// [`MathFunction::overloads`]: ir::MathFunction::overloads
///
/// # Automatic conversions
///
/// In principle, overload sets are easy: you simply list all the overloads the
/// function supports, and then when you're presented with a call to typecheck,
/// you just see if the actual argument types presented in the source code match
/// some overload from the list.
///
/// However, Naga supports languages like WGSL, which apply certain [automatic
/// conversions] if necessary to make a call fit some overload's requirements.
/// This means that the set of calls that are effectively allowed by a given set
/// of overloads can be quite large, since any combination of automatic
/// conversions might be applied.
///
/// For example, if `x` is a `u32`, and `100` is an abstract integer, then even
/// though `max` has no overload like `max(u32, AbstractInt) -> ...`, the
/// expression `max(x, 100)` is still allowed, because AbstractInt automatically
/// converts to `u32`.
///
/// [automatic conversions]: https://gpuweb.github.io/gpuweb/wgsl/#feasible-automatic-conversion
///
/// # How to use `OverloadSet`
///
/// The general process of using an `OverloadSet` is as follows:
///
/// - Obtain an `OverloadSet` for a given operation (say, by calling
///   [`MathFunction::overloads`]). This set is fixed by Naga IR's semantics.
///
/// - Call its [`arg`] method, supplying the type of the argument passed to the
///   operation at a certain index. This returns a new `OverloadSet` containing
///   only those overloads that could accept the given type for the given
///   argument. This includes overloads that only match if automatic conversions
///   are applied.
///
/// - If, at some point, the overload set becomes empty, then the set of
///   arguments is not allowed for this operation, and the program is invalid.
///   The `OverloadSet` trait provides an [`is_empty`] method.
///
/// - After all arguments have been supplied, if the overload set is still
///   non-empty, you can call its [`most_preferred`] method to find out which
///   overload has the least cost in terms of automatic conversions.
///
/// - If the call is rejected, you can use `OverloadSet` to help produce
///   diagnostic messages that explain exactly what went wrong. `OverloadSet`
///   implementations are meant to be cheap to [`Clone`], so it is fine to keep
///   the original overload set value around, and re-run the selection process,
///   attempting to supply the rejected argument at each step to see exactly
///   which preceding argument ruled it out. The [`overload_list`] and
///   [`allowed_args`] methods are helpful for this.
///
/// [`arg`]: OverloadSet::arg
/// [`is_empty`]: OverloadSet::is_empty
/// [`most_preferred`]: OverloadSet::most_preferred
/// [`overload_list`]: OverloadSet::overload_list
/// [`allowed_args`]: OverloadSet::allowed_args
///
/// # Concrete implementations
///
/// This module contains two private implementations of `OverloadSet`:
///
/// - The [`List`] type is a straightforward list of overloads. It is general,
///   but verbose to use. The [`utils`] module exports functions that construct
///   `List` overload sets for the functions that need this.
///
/// - The [`Regular`] type is a compact, efficient representation for the kinds
///   of overload sets commonly seen for Naga IR mathematical functions.
///   However, in return for its simplicity, it is not as flexible as [`List`].
///   This module use the [`regular!`] macro as a legible notation for `Regular`
///   sets.
///
/// [`List`]: list::List
/// [`Regular`]: regular::Regular
/// [`regular!`]: regular::regular
pub trait OverloadSet: Clone {
    /// Return true if `self` is the empty set of overloads.
    fn is_empty(&self) -> bool;

    /// Return the smallest number of arguments in any type rule in the set.
    ///
    /// # Panics
    ///
    /// Panics if `self` is empty.
    fn min_arguments(&self) -> usize;

    /// Return the largest number of arguments in any type rule in the set.
    ///
    /// # Panics
    ///
    /// Panics if `self` is empty.
    fn max_arguments(&self) -> usize;

    /// Find the overloads that could accept a given argument.
    ///
    /// Return a new overload set containing those members of `self` that could
    /// accept a value of type `ty` for their `i`'th argument, once
    /// feasible automatic conversions have been applied.
    fn arg(&self, i: usize, ty: &ir::TypeInner, types: &crate::UniqueArena<ir::Type>) -> Self;

    /// Limit `self` to overloads whose arguments are all concrete types.
    ///
    /// Naga's overload resolution is based on WGSL's [overload resolution
    /// algorithm][ora], which includes the following step:
    ///
    /// > Eliminate any candidate where one of its subexpressions resolves to
    /// > an abstract type after feasible automatic conversions, but another of
    /// > the candidate’s subexpressions is not a const-expression.
    /// >
    /// > Note: As a consequence, if any subexpression in the phrase is not a
    /// > const-expression, then all subexpressions in the phrase must have a
    /// > concrete type.
    ///
    /// Essentially, if any one of the arguments is not a constant expression,
    /// then the operation is going to be evaluated at runtime, so all its
    /// arguments must be converted to a concrete type. If you determine that an
    /// argument is non-constant, you can use this trait method to toss out any
    /// overloads that would accept abstract types.
    ///
    /// In almost all cases, this operation has no effect. Only constant
    /// expressions can have abstract types, so if any argument is not a
    /// constant expression, it must have a concrete type. Although many
    /// operations in Naga IR have overloads for both abstract types and
    /// concrete types, few operations have overloads that accept a mix of
    /// abstract and concrete types. Thus, a single concrete argument will
    /// usually have eliminated all overloads that accept abstract types anyway.
    /// (The exceptions are oddities like `Expression::Select`, where the
    /// `condition` operand could be a runtime expression even as the `accept`
    /// and `reject` operands have abstract types.)
    ///
    /// Note that it is *not* correct to just [concretize] all arguments once
    /// you've noticed that some argument is non-constant. The type to which
    /// each argument is converted depends on the overloads available, not just
    /// the argument's own type.
    ///
    /// [ora]: https://gpuweb.github.io/gpuweb/wgsl/#overload-resolution-section
    /// [concretize]: https://gpuweb.github.io/gpuweb/wgsl/#concretization
    fn concrete_only(self, types: &crate::UniqueArena<ir::Type>) -> Self;

    /// Return the most preferred candidate.
    ///
    /// Rank the candidates in `self` as described in WGSL's [overload
    /// resolution algorithm][ora], and return a singleton set containing the
    /// most preferred candidate.
    ///
    /// # Simplifications versus WGSL
    ///
    /// Naga's process for selecting the most preferred candidate is simpler
    /// than WGSL's:
    ///
    /// - WGSL allows for the possibility of ambiguous calls, where multiple
    ///   overload candidates exist, no one candidate is clearly better than all
    ///   the others. For example, if a function has the two overloads `(i32,
    ///   f32) -> bool` and `(f32, i32) -> bool`, and the arguments are both
    ///   AbstractInt, neither overload is preferred over the other. Ambiguous
    ///   calls are errors.
    ///
    ///   However, Naga IR includes no operations whose overload sets allow such
    ///   situations to arise, so there is always a most preferred candidate.
    ///   Thus, this method infallibly returns a `Rule`, and has no way to
    ///   indicate ambiguity.
    ///
    /// - WGSL says that the most preferred candidate depends on the conversion
    ///   rank for each argument, as determined by the types of the arguments
    ///   being passed.
    ///
    ///   However, the overloads of every operation in Naga IR can be ranked
    ///   even without knowing the argument types. So this method does not
    ///   require the argument types as a parameter.
    ///
    /// # Panics
    ///
    /// Panics if `self` is empty, or if no argument types have been supplied.
    ///
    /// [ora]: https://gpuweb.github.io/gpuweb/wgsl/#overload-resolution-section
    fn most_preferred(&self) -> Rule;

    /// Return a type rule for each of the overloads in `self`.
    fn overload_list(&self, gctx: &crate::proc::GlobalCtx<'_>) -> Vec<Rule>;

    /// Return a list of the types allowed for argument `i`.
    fn allowed_args(&self, i: usize, gctx: &crate::proc::GlobalCtx<'_>) -> Vec<TypeResolution>;

    /// Return an object that can be formatted with [`core::fmt::Debug`].
    fn for_debug(&self, types: &crate::UniqueArena<ir::Type>) -> impl fmt::Debug;
}