/src/wgpu/naga/src/front/wgsl/lower/conversion.rs
Line | Count | Source |
1 | | //! WGSL's automatic conversions for abstract types. |
2 | | |
3 | | use alloc::{boxed::Box, string::String, vec::Vec}; |
4 | | |
5 | | use crate::common::wgsl::{TryToWgsl, TypeContext}; |
6 | | use crate::front::wgsl::error::{ |
7 | | AutoConversionError, AutoConversionLeafScalarError, ConcretizationFailedError, |
8 | | }; |
9 | | use crate::front::wgsl::Result; |
10 | | use crate::{Handle, Span}; |
11 | | |
12 | | impl<'source> super::ExpressionContext<'source, '_, '_> { |
13 | | /// Try to use WGSL's automatic conversions to convert `expr` to `goal_ty`. |
14 | | /// |
15 | | /// If no conversions are necessary, return `expr` unchanged. |
16 | | /// |
17 | | /// If automatic conversions cannot convert `expr` to `goal_ty`, return an |
18 | | /// [`AutoConversion`] error. |
19 | | /// |
20 | | /// Although the Load Rule is one of the automatic conversions, this |
21 | | /// function assumes it has already been applied if appropriate, as |
22 | | /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`. |
23 | | /// |
24 | | /// [`AutoConversion`]: super::Error::AutoConversion |
25 | 0 | pub fn try_automatic_conversions( |
26 | 0 | &mut self, |
27 | 0 | expr: Handle<crate::Expression>, |
28 | 0 | goal_ty: &crate::proc::TypeResolution, |
29 | 0 | goal_span: Span, |
30 | 0 | ) -> Result<'source, Handle<crate::Expression>> { |
31 | 0 | let expr_span = self.get_expression_span(expr); |
32 | | // Keep the TypeResolution so we can get type names for |
33 | | // structs in error messages. |
34 | 0 | let expr_resolution = super::resolve!(self, expr); |
35 | 0 | let types = &self.module.types; |
36 | 0 | let expr_inner = expr_resolution.inner_with(types); |
37 | 0 | let goal_inner = goal_ty.inner_with(types); |
38 | | |
39 | | // We can only convert abstract types, so if `expr` is not abstract do not even |
40 | | // attempt conversion. This allows the validator to catch type errors correctly |
41 | | // rather than them being misreported as type conversion errors. |
42 | | // If the type is an array (of an array, etc) then we must check whether the |
43 | | // type of the innermost array's base type is abstract. |
44 | 0 | if !expr_inner.is_abstract(types) { |
45 | 0 | return Ok(expr); |
46 | 0 | } |
47 | | |
48 | | // If `expr` already has the requested type, we're done. |
49 | 0 | if self.module.compare_types(expr_resolution, goal_ty) { |
50 | 0 | return Ok(expr); |
51 | 0 | } |
52 | | |
53 | 0 | let (_expr_scalar, goal_scalar) = |
54 | 0 | match expr_inner.automatically_converts_to(goal_inner, types) { |
55 | 0 | Some(scalars) => scalars, |
56 | | None => { |
57 | 0 | let source_type = self.type_resolution_to_string(expr_resolution); |
58 | 0 | let dest_type = self.type_resolution_to_string(goal_ty); |
59 | | |
60 | 0 | return Err(Box::new(super::Error::AutoConversion(Box::new( |
61 | 0 | AutoConversionError { |
62 | 0 | dest_span: goal_span, |
63 | 0 | dest_type, |
64 | 0 | source_span: expr_span, |
65 | 0 | source_type, |
66 | 0 | }, |
67 | 0 | )))); |
68 | | } |
69 | | }; |
70 | | |
71 | 0 | self.convert_leaf_scalar(expr, expr_span, goal_scalar) |
72 | 0 | } |
73 | | |
74 | | /// Try to convert `expr`'s leaf scalar to `goal_scalar` using automatic conversions. |
75 | | /// |
76 | | /// If no conversions are necessary, return `expr` unchanged. |
77 | | /// |
78 | | /// If automatic conversions cannot convert `expr` to `goal_scalar`, return |
79 | | /// an [`AutoConversionLeafScalar`] error. |
80 | | /// |
81 | | /// Although the Load Rule is one of the automatic conversions, this |
82 | | /// function assumes it has already been applied if appropriate, as |
83 | | /// indicated by the fact that the Rust type of `expr` is not `Typed<_>`. |
84 | | /// |
85 | | /// [`AutoConversionLeafScalar`]: super::Error::AutoConversionLeafScalar |
86 | 51 | pub fn try_automatic_conversion_for_leaf_scalar( |
87 | 51 | &mut self, |
88 | 51 | expr: Handle<crate::Expression>, |
89 | 51 | goal_scalar: crate::Scalar, |
90 | 51 | goal_span: Span, |
91 | 51 | ) -> Result<'source, Handle<crate::Expression>> { |
92 | 51 | let expr_span = self.get_expression_span(expr); |
93 | 51 | let expr_resolution = super::resolve!(self, expr); |
94 | 51 | let types = &self.module.types; |
95 | 51 | let expr_inner = expr_resolution.inner_with(types); |
96 | | |
97 | 51 | let make_error = || { |
98 | 0 | let source_type = self.type_resolution_to_string(expr_resolution); |
99 | 0 | super::Error::AutoConversionLeafScalar(Box::new(AutoConversionLeafScalarError { |
100 | 0 | dest_span: goal_span, |
101 | 0 | dest_scalar: goal_scalar.to_wgsl_for_diagnostics(), |
102 | 0 | source_span: expr_span, |
103 | 0 | source_type, |
104 | 0 | })) |
105 | 0 | }; |
106 | | |
107 | 51 | let expr_scalar = match expr_inner.automatically_convertible_scalar(&self.module.types) { |
108 | 51 | Some(scalar) => scalar, |
109 | 0 | None => return Err(Box::new(make_error())), |
110 | | }; |
111 | | |
112 | 51 | if expr_scalar == goal_scalar { |
113 | 1 | return Ok(expr); |
114 | 50 | } |
115 | | |
116 | 50 | if !expr_scalar.automatically_converts_to(goal_scalar) { |
117 | 0 | return Err(Box::new(make_error())); |
118 | 50 | } |
119 | | |
120 | 50 | assert!(expr_scalar.is_abstract()); |
121 | | |
122 | 50 | self.convert_leaf_scalar(expr, expr_span, goal_scalar) |
123 | 51 | } |
124 | | |
125 | 50 | fn convert_leaf_scalar( |
126 | 50 | &mut self, |
127 | 50 | expr: Handle<crate::Expression>, |
128 | 50 | expr_span: Span, |
129 | 50 | goal_scalar: crate::Scalar, |
130 | 50 | ) -> Result<'source, Handle<crate::Expression>> { |
131 | 50 | let expr_inner = super::resolve_inner!(self, expr); |
132 | 50 | if let crate::TypeInner::Array { .. } = *expr_inner { |
133 | 0 | self.as_const_evaluator() |
134 | 0 | .cast_array(expr, goal_scalar, expr_span) |
135 | 0 | .map_err(|err| { |
136 | 0 | Box::new(super::Error::ConstantEvaluatorError(err.into(), expr_span)) |
137 | 0 | }) |
138 | | } else { |
139 | 50 | let cast = crate::Expression::As { |
140 | 50 | expr, |
141 | 50 | kind: goal_scalar.kind, |
142 | 50 | convert: Some(goal_scalar.width), |
143 | 50 | }; |
144 | 50 | self.append_expression(cast, expr_span) |
145 | | } |
146 | 50 | } |
147 | | |
148 | | /// Try to convert `exprs` to `goal_ty` using WGSL's automatic conversions. |
149 | 0 | pub fn try_automatic_conversions_slice( |
150 | 0 | &mut self, |
151 | 0 | exprs: &mut [Handle<crate::Expression>], |
152 | 0 | goal_ty: &crate::proc::TypeResolution, |
153 | 0 | goal_span: Span, |
154 | 0 | ) -> Result<'source, ()> { |
155 | 0 | for expr in exprs.iter_mut() { |
156 | 0 | *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?; |
157 | | } |
158 | | |
159 | 0 | Ok(()) |
160 | 0 | } |
161 | | |
162 | | /// Apply WGSL's automatic conversions to a vector constructor's arguments. |
163 | | /// |
164 | | /// When calling a vector constructor like `vec3<f32>(...)`, the parameters |
165 | | /// can be a mix of scalars and vectors, with the latter being spread out to |
166 | | /// contribute each of their components as a component of the new value. |
167 | | /// When the element type is explicit, as with `<f32>` in the example above, |
168 | | /// WGSL's automatic conversions should convert abstract scalar and vector |
169 | | /// parameters to the constructor's required scalar type. |
170 | 0 | pub fn try_automatic_conversions_for_vector( |
171 | 0 | &mut self, |
172 | 0 | exprs: &mut [Handle<crate::Expression>], |
173 | 0 | goal_scalar: crate::Scalar, |
174 | 0 | goal_span: Span, |
175 | 0 | ) -> Result<'source, ()> { |
176 | | use crate::proc::TypeResolution as Tr; |
177 | | use crate::TypeInner as Ti; |
178 | 0 | let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar)); |
179 | | |
180 | 0 | for (i, expr) in exprs.iter_mut().enumerate() { |
181 | | // Keep the TypeResolution so we can get full type names |
182 | | // in error messages. |
183 | 0 | let expr_resolution = super::resolve!(self, *expr); |
184 | 0 | let types = &self.module.types; |
185 | 0 | let expr_inner = expr_resolution.inner_with(types); |
186 | | |
187 | 0 | match *expr_inner { |
188 | | Ti::Scalar(_) => { |
189 | 0 | *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?; |
190 | | } |
191 | 0 | Ti::Vector { size, scalar: _ } => { |
192 | 0 | let goal_vector_res = Tr::Value(Ti::Vector { |
193 | 0 | size, |
194 | 0 | scalar: goal_scalar, |
195 | 0 | }); |
196 | 0 | *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?; |
197 | | } |
198 | | _ => { |
199 | 0 | let span = self.get_expression_span(*expr); |
200 | 0 | return Err(Box::new(super::Error::InvalidConstructorComponentType( |
201 | 0 | span, i as i32, |
202 | 0 | ))); |
203 | | } |
204 | | } |
205 | | } |
206 | | |
207 | 0 | Ok(()) |
208 | 0 | } |
209 | | |
210 | | /// Convert `expr` to the leaf scalar type `scalar`. |
211 | 1.56k | pub fn convert_to_leaf_scalar( |
212 | 1.56k | &mut self, |
213 | 1.56k | expr: &mut Handle<crate::Expression>, |
214 | 1.56k | goal: crate::Scalar, |
215 | 1.56k | ) -> Result<'source, ()> { |
216 | 1.56k | let inner = super::resolve_inner!(self, *expr); |
217 | | // Do nothing if `inner` doesn't even have leaf scalars; |
218 | | // it's a type error that validation will catch. |
219 | 1.56k | if inner.scalar() != Some(goal) { |
220 | 3 | let cast = crate::Expression::As { |
221 | 3 | expr: *expr, |
222 | 3 | kind: goal.kind, |
223 | 3 | convert: Some(goal.width), |
224 | 3 | }; |
225 | 3 | let expr_span = self.get_expression_span(*expr); |
226 | 3 | *expr = self.append_expression(cast, expr_span)?; |
227 | 1.56k | } |
228 | | |
229 | 1.56k | Ok(()) |
230 | 1.56k | } |
231 | | |
232 | | /// Convert all expressions in `exprs` to a common scalar type. |
233 | | /// |
234 | | /// Note that the caller is responsible for making sure these |
235 | | /// conversions are actually justified. This function simply |
236 | | /// generates `As` expressions, regardless of whether they are |
237 | | /// permitted WGSL automatic conversions. Callers intending to |
238 | | /// implement automatic conversions need to determine for |
239 | | /// themselves whether the casts we we generate are justified, |
240 | | /// perhaps by calling `TypeInner::automatically_converts_to` or |
241 | | /// `Scalar::automatic_conversion_combine`. |
242 | 2 | pub fn convert_slice_to_common_leaf_scalar( |
243 | 2 | &mut self, |
244 | 2 | exprs: &mut [Handle<crate::Expression>], |
245 | 2 | goal: crate::Scalar, |
246 | 2 | ) -> Result<'source, ()> { |
247 | 4 | for expr in exprs.iter_mut() { |
248 | 4 | self.convert_to_leaf_scalar(expr, goal)?; |
249 | | } |
250 | | |
251 | 2 | Ok(()) |
252 | 2 | } |
253 | | |
254 | | /// Return an expression for the concretized value of `expr`. |
255 | | /// |
256 | | /// If `expr` is already concrete, return it unchanged. |
257 | 14.8k | pub fn concretize( |
258 | 14.8k | &mut self, |
259 | 14.8k | mut expr: Handle<crate::Expression>, |
260 | 14.8k | ) -> Result<'source, Handle<crate::Expression>> { |
261 | 14.8k | let inner = super::resolve_inner!(self, expr); |
262 | 14.8k | if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) { |
263 | 14.8k | let concretized = scalar.concretize(); |
264 | 14.8k | if concretized != scalar { |
265 | 7 | assert!(scalar.is_abstract()); |
266 | 7 | let expr_span = self.get_expression_span(expr); |
267 | 7 | expr = self |
268 | 7 | .as_const_evaluator() |
269 | 7 | .cast_array(expr, concretized, expr_span) |
270 | 7 | .map_err(|err| { |
271 | | // A `TypeResolution` includes the type's full name, if |
272 | | // it has one. Also, avoid holding the borrow of `inner` |
273 | | // across the call to `cast_array`. |
274 | 0 | let expr_type = &self.typifier()[expr]; |
275 | 0 | super::Error::ConcretizationFailed(Box::new(ConcretizationFailedError { |
276 | 0 | expr_span, |
277 | 0 | expr_type: self.type_resolution_to_string(expr_type), |
278 | 0 | scalar: concretized.to_wgsl_for_diagnostics(), |
279 | 0 | inner: err, |
280 | 0 | })) |
281 | 0 | })?; |
282 | 14.8k | } |
283 | 0 | } |
284 | | |
285 | 14.8k | Ok(expr) |
286 | 14.8k | } |
287 | | |
288 | | /// Find the consensus scalar of `components` under WGSL's automatic |
289 | | /// conversions. |
290 | | /// |
291 | | /// If `components` can all be converted to any common scalar via |
292 | | /// WGSL's automatic conversions, return the best such scalar. |
293 | | /// |
294 | | /// The `components` slice must not be empty. All elements' types must |
295 | | /// have been resolved. |
296 | | /// |
297 | | /// If `components` are definitely not acceptable as arguments to such |
298 | | /// constructors, return `Err(i)`, where `i` is the index in |
299 | | /// `components` of some problematic argument. |
300 | | /// |
301 | | /// This function doesn't fully type-check the arguments - it only |
302 | | /// considers their leaf scalar types. This means it may return `Ok` |
303 | | /// even when the Naga validator will reject the resulting |
304 | | /// construction expression later. |
305 | 782 | pub fn automatic_conversion_consensus<'handle, I>( |
306 | 782 | &self, |
307 | 782 | components: I, |
308 | 782 | ) -> core::result::Result<crate::Scalar, usize> |
309 | 782 | where |
310 | 782 | I: IntoIterator<Item = &'handle Handle<crate::Expression>>, |
311 | 782 | I::IntoIter: Clone, // for debugging |
312 | | { |
313 | 782 | let types = &self.module.types; |
314 | 782 | let components_iter = components.into_iter(); |
315 | 782 | log::debug!( |
316 | 0 | "wgsl automatic_conversion_consensus: {}", |
317 | 0 | components_iter |
318 | 0 | .clone() |
319 | 0 | .map(|&expr| { |
320 | 0 | let res = &self.typifier()[expr]; |
321 | 0 | self.type_resolution_to_string(res) |
322 | 0 | }) Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<core::slice::iter::Iter<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#1} Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&[naga::arena::handle::Handle<naga::ir::Expression>; 2]>::{closure#1} Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&alloc::vec::Vec<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#1} |
323 | 0 | .collect::<Vec<String>>() |
324 | 0 | .join(", ") |
325 | | ); |
326 | 1.56k | let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types)); <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<core::slice::iter::Iter<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#0} Line | Count | Source | 326 | 1.56k | let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types)); |
<naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&[naga::arena::handle::Handle<naga::ir::Expression>; 2]>::{closure#0} Line | Count | Source | 326 | 4 | let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types)); |
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&alloc::vec::Vec<naga::arena::handle::Handle<naga::ir::Expression>>>::{closure#0} |
327 | 782 | let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?; |
328 | 782 | for (inner, i) in inners.zip(1..) { |
329 | 782 | let scalar = inner.scalar().ok_or(i)?; |
330 | 782 | match best.automatic_conversion_combine(scalar) { |
331 | 782 | Some(new_best) => { |
332 | 782 | best = new_best; |
333 | 782 | } |
334 | 0 | None => return Err(i), |
335 | | } |
336 | | } |
337 | | |
338 | 782 | log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics()); |
339 | 782 | Ok(best) |
340 | 782 | } <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<core::slice::iter::Iter<naga::arena::handle::Handle<naga::ir::Expression>>> Line | Count | Source | 305 | 780 | pub fn automatic_conversion_consensus<'handle, I>( | 306 | 780 | &self, | 307 | 780 | components: I, | 308 | 780 | ) -> core::result::Result<crate::Scalar, usize> | 309 | 780 | where | 310 | 780 | I: IntoIterator<Item = &'handle Handle<crate::Expression>>, | 311 | 780 | I::IntoIter: Clone, // for debugging | 312 | | { | 313 | 780 | let types = &self.module.types; | 314 | 780 | let components_iter = components.into_iter(); | 315 | 780 | log::debug!( | 316 | 0 | "wgsl automatic_conversion_consensus: {}", | 317 | 0 | components_iter | 318 | 0 | .clone() | 319 | 0 | .map(|&expr| { | 320 | | let res = &self.typifier()[expr]; | 321 | | self.type_resolution_to_string(res) | 322 | | }) | 323 | 0 | .collect::<Vec<String>>() | 324 | 0 | .join(", ") | 325 | | ); | 326 | 780 | let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types)); | 327 | 780 | let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?; | 328 | 780 | for (inner, i) in inners.zip(1..) { | 329 | 780 | let scalar = inner.scalar().ok_or(i)?; | 330 | 780 | match best.automatic_conversion_combine(scalar) { | 331 | 780 | Some(new_best) => { | 332 | 780 | best = new_best; | 333 | 780 | } | 334 | 0 | None => return Err(i), | 335 | | } | 336 | | } | 337 | | | 338 | 780 | log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics()); | 339 | 780 | Ok(best) | 340 | 780 | } |
<naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&[naga::arena::handle::Handle<naga::ir::Expression>; 2]> Line | Count | Source | 305 | 2 | pub fn automatic_conversion_consensus<'handle, I>( | 306 | 2 | &self, | 307 | 2 | components: I, | 308 | 2 | ) -> core::result::Result<crate::Scalar, usize> | 309 | 2 | where | 310 | 2 | I: IntoIterator<Item = &'handle Handle<crate::Expression>>, | 311 | 2 | I::IntoIter: Clone, // for debugging | 312 | | { | 313 | 2 | let types = &self.module.types; | 314 | 2 | let components_iter = components.into_iter(); | 315 | 2 | log::debug!( | 316 | 0 | "wgsl automatic_conversion_consensus: {}", | 317 | 0 | components_iter | 318 | 0 | .clone() | 319 | 0 | .map(|&expr| { | 320 | | let res = &self.typifier()[expr]; | 321 | | self.type_resolution_to_string(res) | 322 | | }) | 323 | 0 | .collect::<Vec<String>>() | 324 | 0 | .join(", ") | 325 | | ); | 326 | 2 | let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types)); | 327 | 2 | let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?; | 328 | 2 | for (inner, i) in inners.zip(1..) { | 329 | 2 | let scalar = inner.scalar().ok_or(i)?; | 330 | 2 | match best.automatic_conversion_combine(scalar) { | 331 | 2 | Some(new_best) => { | 332 | 2 | best = new_best; | 333 | 2 | } | 334 | 0 | None => return Err(i), | 335 | | } | 336 | | } | 337 | | | 338 | 2 | log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics()); | 339 | 2 | Ok(best) | 340 | 2 | } |
Unexecuted instantiation: <naga::front::wgsl::lower::ExpressionContext>::automatic_conversion_consensus::<&alloc::vec::Vec<naga::arena::handle::Handle<naga::ir::Expression>>> |
341 | | } |
342 | | |
343 | | impl crate::TypeInner { |
344 | 14.9k | fn automatically_convertible_scalar( |
345 | 14.9k | &self, |
346 | 14.9k | types: &crate::UniqueArena<crate::Type>, |
347 | 14.9k | ) -> Option<crate::Scalar> { |
348 | | use crate::TypeInner as Ti; |
349 | 14.9k | match *self { |
350 | 9.41k | Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => { |
351 | 14.9k | Some(scalar) |
352 | | } |
353 | 0 | Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types), |
354 | | Ti::Atomic(_) |
355 | | | Ti::Pointer { .. } |
356 | | | Ti::ValuePointer { .. } |
357 | | | Ti::Struct { .. } |
358 | | | Ti::Image { .. } |
359 | | | Ti::Sampler { .. } |
360 | | | Ti::AccelerationStructure { .. } |
361 | | | Ti::RayQuery { .. } |
362 | 0 | | Ti::BindingArray { .. } => None, |
363 | | } |
364 | 14.9k | } |
365 | | |
366 | | /// Return the leaf scalar type of `pointer`. |
367 | | /// |
368 | | /// `pointer` must be a `TypeInner` representing a pointer type. |
369 | 50 | pub fn pointer_automatically_convertible_scalar( |
370 | 50 | &self, |
371 | 50 | types: &crate::UniqueArena<crate::Type>, |
372 | 50 | ) -> Option<crate::Scalar> { |
373 | | use crate::TypeInner as Ti; |
374 | 50 | match *self { |
375 | 0 | Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => { |
376 | 0 | Some(scalar) |
377 | | } |
378 | 0 | Ti::Atomic(_) => None, |
379 | 50 | Ti::Pointer { base, .. } | Ti::Array { base, .. } => { |
380 | 50 | types[base].inner.automatically_convertible_scalar(types) |
381 | | } |
382 | 0 | Ti::ValuePointer { scalar, .. } => Some(scalar), |
383 | | Ti::Struct { .. } |
384 | | | Ti::Image { .. } |
385 | | | Ti::Sampler { .. } |
386 | | | Ti::AccelerationStructure { .. } |
387 | | | Ti::RayQuery { .. } |
388 | 0 | | Ti::BindingArray { .. } => None, |
389 | | } |
390 | 50 | } |
391 | | } |
392 | | |
393 | | impl crate::Scalar { |
394 | | /// Find the common type of `self` and `other` under WGSL's |
395 | | /// automatic conversions. |
396 | | /// |
397 | | /// If there are any scalars to which WGSL's automatic conversions |
398 | | /// will convert both `self` and `other`, return the best such |
399 | | /// scalar. Otherwise, return `None`. |
400 | 832 | pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> { |
401 | | use crate::ScalarKind as Sk; |
402 | | |
403 | 832 | match (self.kind, other.kind) { |
404 | | // When the kinds match... |
405 | | (Sk::AbstractFloat, Sk::AbstractFloat) |
406 | | | (Sk::AbstractInt, Sk::AbstractInt) |
407 | | | (Sk::Sint, Sk::Sint) |
408 | | | (Sk::Uint, Sk::Uint) |
409 | | | (Sk::Float, Sk::Float) |
410 | | | (Sk::Bool, Sk::Bool) => { |
411 | 779 | if self.width == other.width { |
412 | | // ... either no conversion is necessary ... |
413 | 779 | Some(self) |
414 | | } else { |
415 | | // ... or no conversion is possible. |
416 | | // We never convert concrete to concrete, and |
417 | | // abstract types should have only one size. |
418 | 0 | None |
419 | | } |
420 | | } |
421 | | |
422 | | // AbstractInt converts to AbstractFloat. |
423 | 2 | (Sk::AbstractFloat, Sk::AbstractInt) => Some(self), |
424 | 0 | (Sk::AbstractInt, Sk::AbstractFloat) => Some(other), |
425 | | |
426 | | // AbstractFloat converts to Float. |
427 | 0 | (Sk::AbstractFloat, Sk::Float) => Some(other), |
428 | 0 | (Sk::Float, Sk::AbstractFloat) => Some(self), |
429 | | |
430 | | // AbstractInt converts to concrete integer or float. |
431 | 51 | (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other), |
432 | 0 | (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self), |
433 | | |
434 | | // AbstractFloat can't be reconciled with concrete integer types. |
435 | | (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => { |
436 | 0 | None |
437 | | } |
438 | | |
439 | | // Nothing can be reconciled with `bool`. |
440 | 0 | (Sk::Bool, _) | (_, Sk::Bool) => None, |
441 | | |
442 | | // Different concrete types cannot be reconciled. |
443 | 0 | (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None, |
444 | | } |
445 | 832 | } |
446 | | |
447 | | /// Return `true` if automatic conversions will covert `self` to `goal`. |
448 | 50 | pub fn automatically_converts_to(self, goal: Self) -> bool { |
449 | 50 | self.automatic_conversion_combine(goal) == Some(goal) |
450 | 50 | } |
451 | | |
452 | 14.8k | pub(in crate::front::wgsl) const fn concretize(self) -> Self { |
453 | | use crate::ScalarKind as Sk; |
454 | 14.8k | match self.kind { |
455 | 14.8k | Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self, |
456 | 5 | Sk::AbstractInt => Self::I32, |
457 | 2 | Sk::AbstractFloat => Self::F32, |
458 | | } |
459 | 14.8k | } |
460 | | } |