1use crate::{Handle, Span};
4
5impl<'source, 'temp, 'out> super::ExpressionContext<'source, 'temp, 'out> {
6 pub fn try_automatic_conversions(
19 &mut self,
20 expr: Handle<crate::Expression>,
21 goal_ty: &crate::proc::TypeResolution,
22 goal_span: Span,
23 ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
24 let expr_span = self.get_expression_span(expr);
25 let expr_resolution = super::resolve!(self, expr);
28 let types = &self.module.types;
29 let expr_inner = expr_resolution.inner_with(types);
30 let goal_inner = goal_ty.inner_with(types);
31
32 if expr_inner.equivalent(goal_inner, types) {
34 return Ok(expr);
35 }
36
37 let (_expr_scalar, goal_scalar) =
38 match expr_inner.automatically_converts_to(goal_inner, types) {
39 Some(scalars) => scalars,
40 None => {
41 let gctx = &self.module.to_ctx();
42 let source_type = expr_resolution.to_wgsl(gctx);
43 let dest_type = goal_ty.to_wgsl(gctx);
44
45 return Err(super::Error::AutoConversion {
46 dest_span: goal_span,
47 dest_type,
48 source_span: expr_span,
49 source_type,
50 });
51 }
52 };
53
54 self.convert_leaf_scalar(expr, expr_span, goal_scalar)
55 }
56
57 pub fn try_automatic_conversion_for_leaf_scalar(
70 &mut self,
71 expr: Handle<crate::Expression>,
72 goal_scalar: crate::Scalar,
73 goal_span: Span,
74 ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
75 let expr_span = self.get_expression_span(expr);
76 let expr_resolution = super::resolve!(self, expr);
77 let types = &self.module.types;
78 let expr_inner = expr_resolution.inner_with(types);
79
80 let make_error = || {
81 let gctx = &self.module.to_ctx();
82 let source_type = expr_resolution.to_wgsl(gctx);
83 super::Error::AutoConversionLeafScalar {
84 dest_span: goal_span,
85 dest_scalar: goal_scalar.to_wgsl(),
86 source_span: expr_span,
87 source_type,
88 }
89 };
90
91 let expr_scalar = match expr_inner.scalar() {
92 Some(scalar) => scalar,
93 None => return Err(make_error()),
94 };
95
96 if expr_scalar == goal_scalar {
97 return Ok(expr);
98 }
99
100 if !expr_scalar.automatically_converts_to(goal_scalar) {
101 return Err(make_error());
102 }
103
104 assert!(expr_scalar.is_abstract());
105
106 self.convert_leaf_scalar(expr, expr_span, goal_scalar)
107 }
108
109 fn convert_leaf_scalar(
110 &mut self,
111 expr: Handle<crate::Expression>,
112 expr_span: Span,
113 goal_scalar: crate::Scalar,
114 ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
115 let expr_inner = super::resolve_inner!(self, expr);
116 if let crate::TypeInner::Array { .. } = *expr_inner {
117 self.as_const_evaluator()
118 .cast_array(expr, goal_scalar, expr_span)
119 .map_err(|err| super::Error::ConstantEvaluatorError(err, expr_span))
120 } else {
121 let cast = crate::Expression::As {
122 expr,
123 kind: goal_scalar.kind,
124 convert: Some(goal_scalar.width),
125 };
126 self.append_expression(cast, expr_span)
127 }
128 }
129
130 pub fn try_automatic_conversions_slice(
132 &mut self,
133 exprs: &mut [Handle<crate::Expression>],
134 goal_ty: &crate::proc::TypeResolution,
135 goal_span: Span,
136 ) -> Result<(), super::Error<'source>> {
137 for expr in exprs.iter_mut() {
138 *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
139 }
140
141 Ok(())
142 }
143
144 pub fn try_automatic_conversions_for_vector(
153 &mut self,
154 exprs: &mut [Handle<crate::Expression>],
155 goal_scalar: crate::Scalar,
156 goal_span: Span,
157 ) -> Result<(), super::Error<'source>> {
158 use crate::proc::TypeResolution as Tr;
159 use crate::TypeInner as Ti;
160 let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
161
162 for (i, expr) in exprs.iter_mut().enumerate() {
163 let expr_resolution = super::resolve!(self, *expr);
166 let types = &self.module.types;
167 let expr_inner = expr_resolution.inner_with(types);
168
169 match *expr_inner {
170 Ti::Scalar(_) => {
171 *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
172 }
173 Ti::Vector { size, scalar: _ } => {
174 let goal_vector_res = Tr::Value(Ti::Vector {
175 size,
176 scalar: goal_scalar,
177 });
178 *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
179 }
180 _ => {
181 let span = self.get_expression_span(*expr);
182 return Err(super::Error::InvalidConstructorComponentType(
183 span, i as i32,
184 ));
185 }
186 }
187 }
188
189 Ok(())
190 }
191
192 pub fn convert_to_leaf_scalar(
194 &mut self,
195 expr: &mut Handle<crate::Expression>,
196 goal: crate::Scalar,
197 ) -> Result<(), super::Error<'source>> {
198 let inner = super::resolve_inner!(self, *expr);
199 if inner.scalar() != Some(goal) {
202 let cast = crate::Expression::As {
203 expr: *expr,
204 kind: goal.kind,
205 convert: Some(goal.width),
206 };
207 let expr_span = self.get_expression_span(*expr);
208 *expr = self.append_expression(cast, expr_span)?;
209 }
210
211 Ok(())
212 }
213
214 pub fn convert_slice_to_common_leaf_scalar(
225 &mut self,
226 exprs: &mut [Handle<crate::Expression>],
227 goal: crate::Scalar,
228 ) -> Result<(), super::Error<'source>> {
229 for expr in exprs.iter_mut() {
230 self.convert_to_leaf_scalar(expr, goal)?;
231 }
232
233 Ok(())
234 }
235
236 pub fn concretize(
240 &mut self,
241 mut expr: Handle<crate::Expression>,
242 ) -> Result<Handle<crate::Expression>, super::Error<'source>> {
243 let inner = super::resolve_inner!(self, expr);
244 if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
245 let concretized = scalar.concretize();
246 if concretized != scalar {
247 assert!(scalar.is_abstract());
248 let expr_span = self.get_expression_span(expr);
249 expr = self
250 .as_const_evaluator()
251 .cast_array(expr, concretized, expr_span)
252 .map_err(|err| {
253 let expr_type = &self.typifier()[expr];
257 super::Error::ConcretizationFailed {
258 expr_span,
259 expr_type: expr_type.to_wgsl(&self.module.to_ctx()),
260 scalar: concretized.to_wgsl(),
261 inner: err,
262 }
263 })?;
264 }
265 }
266
267 Ok(expr)
268 }
269
270 pub fn automatic_conversion_consensus<'handle, I>(
288 &self,
289 components: I,
290 ) -> Result<crate::Scalar, usize>
291 where
292 I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
293 I::IntoIter: Clone, {
295 let types = &self.module.types;
296 let mut inners = components
297 .into_iter()
298 .map(|&c| self.typifier()[c].inner_with(types));
299 log::debug!(
300 "wgsl automatic_conversion_consensus: {:?}",
301 inners
302 .clone()
303 .map(|inner| inner.to_wgsl(&self.module.to_ctx()))
304 .collect::<Vec<String>>()
305 );
306 let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
307 for (inner, i) in inners.zip(1..) {
308 let scalar = inner.scalar().ok_or(i)?;
309 match best.automatic_conversion_combine(scalar) {
310 Some(new_best) => {
311 best = new_best;
312 }
313 None => return Err(i),
314 }
315 }
316
317 log::debug!(" consensus: {:?}", best.to_wgsl());
318 Ok(best)
319 }
320}
321
322impl crate::TypeInner {
323 fn automatically_converts_to(
337 &self,
338 goal: &Self,
339 types: &crate::UniqueArena<crate::Type>,
340 ) -> Option<(crate::Scalar, crate::Scalar)> {
341 use crate::ScalarKind as Sk;
342 use crate::TypeInner as Ti;
343
344 let expr_scalar;
350 let goal_scalar;
351 match (self, goal) {
352 (&Ti::Scalar(expr), &Ti::Scalar(goal)) => {
353 expr_scalar = expr;
354 goal_scalar = goal;
355 }
356 (
357 &Ti::Vector {
358 size: expr_size,
359 scalar: expr,
360 },
361 &Ti::Vector {
362 size: goal_size,
363 scalar: goal,
364 },
365 ) if expr_size == goal_size => {
366 expr_scalar = expr;
367 goal_scalar = goal;
368 }
369 (
370 &Ti::Matrix {
371 rows: expr_rows,
372 columns: expr_columns,
373 scalar: expr,
374 },
375 &Ti::Matrix {
376 rows: goal_rows,
377 columns: goal_columns,
378 scalar: goal,
379 },
380 ) if expr_rows == goal_rows && expr_columns == goal_columns => {
381 expr_scalar = expr;
382 goal_scalar = goal;
383 }
384 (
385 &Ti::Array {
386 base: expr_base,
387 size: expr_size,
388 stride: _,
389 },
390 &Ti::Array {
391 base: goal_base,
392 size: goal_size,
393 stride: _,
394 },
395 ) if expr_size == goal_size => {
396 return types[expr_base]
397 .inner
398 .automatically_converts_to(&types[goal_base].inner, types);
399 }
400 _ => return None,
401 }
402
403 match (expr_scalar.kind, goal_scalar.kind) {
404 (Sk::AbstractFloat, Sk::Float) => {}
405 (Sk::AbstractInt, Sk::Sint | Sk::Uint | Sk::AbstractFloat | Sk::Float) => {}
406 _ => return None,
407 }
408
409 log::trace!(" okay: expr {expr_scalar:?}, goal {goal_scalar:?}");
410 Some((expr_scalar, goal_scalar))
411 }
412
413 fn automatically_convertible_scalar(
414 &self,
415 types: &crate::UniqueArena<crate::Type>,
416 ) -> Option<crate::Scalar> {
417 use crate::TypeInner as Ti;
418 match *self {
419 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
420 Some(scalar)
421 }
422 Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),
423 Ti::Atomic(_)
424 | Ti::Pointer { .. }
425 | Ti::ValuePointer { .. }
426 | Ti::Struct { .. }
427 | Ti::Image { .. }
428 | Ti::Sampler { .. }
429 | Ti::AccelerationStructure
430 | Ti::RayQuery
431 | Ti::BindingArray { .. } => None,
432 }
433 }
434}
435
436impl crate::Scalar {
437 pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
444 use crate::ScalarKind as Sk;
445
446 match (self.kind, other.kind) {
447 (Sk::AbstractFloat, Sk::AbstractFloat)
449 | (Sk::AbstractInt, Sk::AbstractInt)
450 | (Sk::Sint, Sk::Sint)
451 | (Sk::Uint, Sk::Uint)
452 | (Sk::Float, Sk::Float)
453 | (Sk::Bool, Sk::Bool) => {
454 if self.width == other.width {
455 Some(self)
457 } else {
458 None
462 }
463 }
464
465 (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
467 (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
468
469 (Sk::AbstractFloat, Sk::Float) => Some(other),
471 (Sk::Float, Sk::AbstractFloat) => Some(self),
472
473 (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
475 (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
476
477 (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
479 None
480 }
481
482 (Sk::Bool, _) | (_, Sk::Bool) => None,
484
485 (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
487 }
488 }
489
490 pub fn automatically_converts_to(self, goal: Self) -> bool {
492 self.automatic_conversion_combine(goal) == Some(goal)
493 }
494
495 const fn concretize(self) -> Self {
496 use crate::ScalarKind as Sk;
497 match self.kind {
498 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
499 Sk::AbstractInt => Self::I32,
500 Sk::AbstractFloat => Self::F32,
501 }
502 }
503}