1use alloc::{boxed::Box, string::String, vec::Vec};
4
5use crate::common::wgsl::{TryToWgsl, TypeContext};
6use crate::front::wgsl::error::{
7 AutoConversionError, AutoConversionLeafScalarError, ConcretizationFailedError,
8 TypeMismatchError,
9};
10use crate::front::wgsl::Result;
11use crate::{Handle, Span};
12
13impl<'source> super::ExpressionContext<'source, '_, '_> {
14 pub fn try_automatic_conversions(
29 &mut self,
30 expr: Handle<crate::Expression>,
31 goal_ty: &crate::proc::TypeResolution,
32 goal_span: Span,
33 ) -> Result<'source, Handle<crate::Expression>> {
34 let expr_span = self.get_expression_span(expr);
35 let expr_resolution = super::resolve!(self, expr);
38 let types = &self.module.types;
39 let expr_inner = expr_resolution.inner_with(types);
40 let goal_inner = goal_ty.inner_with(types);
41
42 if self.module.compare_types(expr_resolution, goal_ty) {
44 return Ok(expr);
45 }
46
47 if !expr_inner.is_abstract(types) {
54 let source_type = self.type_resolution_to_string(expr_resolution);
55 let dest_type = self.type_resolution_to_string(goal_ty);
56
57 return Err(Box::new(super::Error::TypeMismatch(Box::new(
58 TypeMismatchError {
59 dest_span: goal_span,
60 dest_type,
61 source_span: expr_span,
62 source_type,
63 },
64 ))));
65 }
66
67 let (_expr_scalar, goal_scalar) =
68 match expr_inner.automatically_converts_to(goal_inner, types) {
69 Some(scalars) => scalars,
70 None => {
71 let source_type = self.type_resolution_to_string(expr_resolution);
72 let dest_type = self.type_resolution_to_string(goal_ty);
73
74 return Err(Box::new(super::Error::AutoConversion(Box::new(
75 AutoConversionError {
76 dest_span: goal_span,
77 dest_type,
78 source_span: expr_span,
79 source_type,
80 },
81 ))));
82 }
83 };
84
85 self.convert_leaf_scalar(expr, expr_span, goal_scalar)
86 }
87
88 pub fn try_automatic_conversion_for_leaf_scalar(
101 &mut self,
102 expr: Handle<crate::Expression>,
103 goal_scalar: crate::Scalar,
104 goal_span: Span,
105 ) -> Result<'source, Handle<crate::Expression>> {
106 let expr_span = self.get_expression_span(expr);
107 let expr_resolution = super::resolve!(self, expr);
108 let types = &self.module.types;
109 let expr_inner = expr_resolution.inner_with(types);
110
111 let make_error = || {
112 let source_type = self.type_resolution_to_string(expr_resolution);
113 super::Error::AutoConversionLeafScalar(Box::new(AutoConversionLeafScalarError {
114 dest_span: goal_span,
115 dest_scalar: goal_scalar.to_wgsl_for_diagnostics(),
116 source_span: expr_span,
117 source_type,
118 }))
119 };
120
121 let expr_scalar = match expr_inner.automatically_convertible_scalar(&self.module.types) {
122 Some(scalar) => scalar,
123 None => return Err(Box::new(make_error())),
124 };
125
126 if expr_scalar == goal_scalar {
127 return Ok(expr);
128 }
129
130 if !expr_scalar.automatically_converts_to(goal_scalar) {
131 return Err(Box::new(make_error()));
132 }
133
134 assert!(expr_scalar.is_abstract());
135
136 self.convert_leaf_scalar(expr, expr_span, goal_scalar)
137 }
138
139 fn convert_leaf_scalar(
140 &mut self,
141 expr: Handle<crate::Expression>,
142 expr_span: Span,
143 goal_scalar: crate::Scalar,
144 ) -> Result<'source, Handle<crate::Expression>> {
145 let expr_inner = super::resolve_inner!(self, expr);
146 if let crate::TypeInner::Array { .. } = *expr_inner {
147 self.as_const_evaluator()
148 .cast_array(expr, goal_scalar, expr_span)
149 .map_err(|err| {
150 Box::new(super::Error::ConstantEvaluatorError(err.into(), expr_span))
151 })
152 } else {
153 let cast = crate::Expression::As {
154 expr,
155 target: goal_scalar,
156 };
157 self.append_expression(cast, expr_span)
158 }
159 }
160
161 pub fn try_automatic_conversions_slice(
163 &mut self,
164 exprs: &mut [Handle<crate::Expression>],
165 goal_ty: &crate::proc::TypeResolution,
166 goal_span: Span,
167 ) -> Result<'source, ()> {
168 for expr in exprs.iter_mut() {
169 *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
170 }
171
172 Ok(())
173 }
174
175 pub fn try_automatic_conversions_for_vector(
184 &mut self,
185 exprs: &mut [Handle<crate::Expression>],
186 goal_scalar: crate::Scalar,
187 goal_span: Span,
188 ) -> Result<'source, ()> {
189 use crate::proc::TypeResolution as Tr;
190 use crate::TypeInner as Ti;
191 let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
192
193 for (i, expr) in exprs.iter_mut().enumerate() {
194 let expr_resolution = super::resolve!(self, *expr);
197 let types = &self.module.types;
198 let expr_inner = expr_resolution.inner_with(types);
199
200 match *expr_inner {
201 Ti::Scalar(_) => {
202 *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
203 }
204 Ti::Vector { size, scalar: _ } => {
205 let goal_vector_res = Tr::Value(Ti::Vector {
206 size,
207 scalar: goal_scalar,
208 });
209 *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
210 }
211 _ => {
212 let span = self.get_expression_span(*expr);
213 return Err(Box::new(super::Error::InvalidConstructorComponentType(
214 span, i as i32,
215 )));
216 }
217 }
218 }
219
220 Ok(())
221 }
222
223 pub fn convert_to_leaf_scalar(
225 &mut self,
226 expr: &mut Handle<crate::Expression>,
227 goal: crate::Scalar,
228 ) -> Result<'source, ()> {
229 let inner = super::resolve_inner!(self, *expr);
230 if inner.scalar() != Some(goal) {
233 let cast = crate::Expression::As {
234 expr: *expr,
235 target: goal,
236 };
237 let expr_span = self.get_expression_span(*expr);
238 *expr = self.append_expression(cast, expr_span)?;
239 }
240
241 Ok(())
242 }
243
244 pub fn convert_slice_to_common_leaf_scalar(
255 &mut self,
256 exprs: &mut [Handle<crate::Expression>],
257 goal: crate::Scalar,
258 ) -> Result<'source, ()> {
259 for expr in exprs.iter_mut() {
260 self.convert_to_leaf_scalar(expr, goal)?;
261 }
262
263 Ok(())
264 }
265
266 pub fn concretize(
270 &mut self,
271 expr: Handle<crate::Expression>,
272 ) -> Result<'source, Handle<crate::Expression>> {
273 let inner = super::resolve_inner!(self, expr);
274 if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
275 use crate::ScalarKind as Sk;
276 let concretization_preferences = match scalar.kind {
277 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => return Ok(expr),
279 Sk::AbstractInt => {
280 [crate::Scalar::I32, crate::Scalar::U32, crate::Scalar::F32].as_slice()
281 }
282 Sk::AbstractFloat => [crate::Scalar::F32].as_slice(),
283 };
284 let expr_span = self.get_expression_span(expr);
285 let mut errors = Vec::new();
286 for concrete_scalar in concretization_preferences {
287 match self
288 .as_const_evaluator()
289 .cast_array(expr, *concrete_scalar, expr_span)
290 {
291 Ok(expr) => return Ok(expr),
292 Err(crate::proc::ConstantEvaluatorError::TypeTooLarge(ty)) => {
293 return Err(Box::new(super::Error::TypeTooLarge {
296 span: self.module.types.get_span(ty),
297 }));
298 }
299 Err(err) => {
300 errors.push((concrete_scalar.to_wgsl_for_diagnostics(), err));
301 }
302 }
303 }
304 if !errors.is_empty() {
305 let expr_type = &self.typifier()[expr];
309 return Err(Box::new(super::Error::ConcretizationFailed(Box::new(
310 ConcretizationFailedError {
311 expr_span,
312 expr_type: self.type_resolution_to_string(expr_type),
313 concretization_preferences: errors,
314 },
315 ))));
316 }
317 }
318
319 Ok(expr)
320 }
321
322 pub fn automatic_conversion_consensus<'handle, I>(
344 &self,
345 base: Option<crate::Scalar>,
346 components: I,
347 ) -> core::result::Result<crate::Scalar, usize>
348 where
349 I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
350 I::IntoIter: Clone, {
352 let types = &self.module.types;
353 let components_iter = components.into_iter();
354 log::debug!(
355 "wgsl automatic_conversion_consensus: {}",
356 components_iter
357 .clone()
358 .map(|&expr| {
359 let res = &self.typifier()[expr];
360 self.type_resolution_to_string(res)
361 })
362 .collect::<Vec<String>>()
363 .join(", ")
364 );
365 let mut components_iter = components_iter
366 .map(|&c| self.typifier()[c].inner_with(types).scalar())
367 .enumerate();
368 let base = base
369 .or_else(|| components_iter.next().unwrap().1)
370 .ok_or(0usize)?;
371 let best = components_iter.try_fold(base, |best, (i, scalar)| {
372 scalar
373 .and_then(|scalar| best.automatic_conversion_combine(scalar))
374 .ok_or(i)
375 })?;
376 log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics());
377 Ok(best)
378 }
379}
380
381impl crate::TypeInner {
382 fn automatically_convertible_scalar(
383 &self,
384 types: &crate::UniqueArena<crate::Type>,
385 ) -> Option<crate::Scalar> {
386 use crate::TypeInner as Ti;
387 match *self {
388 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
389 Some(scalar)
390 }
391 Ti::CooperativeMatrix { .. } => None,
392 Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),
393 Ti::Atomic(_)
394 | Ti::Pointer { .. }
395 | Ti::ValuePointer { .. }
396 | Ti::Struct { .. }
397 | Ti::Image { .. }
398 | Ti::Sampler { .. }
399 | Ti::AccelerationStructure { .. }
400 | Ti::RayQuery { .. }
401 | Ti::BindingArray { .. } => None,
402 }
403 }
404
405 pub fn pointer_automatically_convertible_scalar(
409 &self,
410 types: &crate::UniqueArena<crate::Type>,
411 ) -> Option<crate::Scalar> {
412 use crate::TypeInner as Ti;
413 match *self {
414 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
415 Some(scalar)
416 }
417 Ti::CooperativeMatrix { .. } => None,
418 Ti::Atomic(_) => None,
419 Ti::Pointer { base, .. } | Ti::Array { base, .. } => {
420 types[base].inner.automatically_convertible_scalar(types)
421 }
422 Ti::ValuePointer { scalar, .. } => Some(scalar),
423 Ti::Struct { .. }
424 | Ti::Image { .. }
425 | Ti::Sampler { .. }
426 | Ti::AccelerationStructure { .. }
427 | Ti::RayQuery { .. }
428 | Ti::BindingArray { .. } => None,
429 }
430 }
431}
432
433impl crate::Scalar {
434 pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
441 use crate::ScalarKind as Sk;
442
443 match (self.kind, other.kind) {
444 (Sk::AbstractFloat, Sk::AbstractFloat)
446 | (Sk::AbstractInt, Sk::AbstractInt)
447 | (Sk::Sint, Sk::Sint)
448 | (Sk::Uint, Sk::Uint)
449 | (Sk::Float, Sk::Float)
450 | (Sk::Bool, Sk::Bool) => {
451 if self.width == other.width {
452 Some(self)
454 } else {
455 None
459 }
460 }
461
462 (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
464 (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
465
466 (Sk::AbstractFloat, Sk::Float) => Some(other),
468 (Sk::Float, Sk::AbstractFloat) => Some(self),
469
470 (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
472 (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
473
474 (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
476 None
477 }
478
479 (Sk::Bool, _) | (_, Sk::Bool) => None,
481
482 (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
484 }
485 }
486
487 pub fn automatically_converts_to(self, goal: Self) -> bool {
489 self.automatic_conversion_combine(goal) == Some(goal)
490 }
491
492 pub(in crate::front::wgsl) const fn concretize(self) -> Self {
493 use crate::ScalarKind as Sk;
494 match self.kind {
495 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
496 Sk::AbstractInt => Self::I32,
497 Sk::AbstractFloat => Self::F32,
498 }
499 }
500}