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 kind: goal_scalar.kind,
156 convert: Some(goal_scalar.width),
157 };
158 self.append_expression(cast, expr_span)
159 }
160 }
161
162 pub fn try_automatic_conversions_slice(
164 &mut self,
165 exprs: &mut [Handle<crate::Expression>],
166 goal_ty: &crate::proc::TypeResolution,
167 goal_span: Span,
168 ) -> Result<'source, ()> {
169 for expr in exprs.iter_mut() {
170 *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
171 }
172
173 Ok(())
174 }
175
176 pub fn try_automatic_conversions_for_vector(
185 &mut self,
186 exprs: &mut [Handle<crate::Expression>],
187 goal_scalar: crate::Scalar,
188 goal_span: Span,
189 ) -> Result<'source, ()> {
190 use crate::proc::TypeResolution as Tr;
191 use crate::TypeInner as Ti;
192 let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
193
194 for (i, expr) in exprs.iter_mut().enumerate() {
195 let expr_resolution = super::resolve!(self, *expr);
198 let types = &self.module.types;
199 let expr_inner = expr_resolution.inner_with(types);
200
201 match *expr_inner {
202 Ti::Scalar(_) => {
203 *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
204 }
205 Ti::Vector { size, scalar: _ } => {
206 let goal_vector_res = Tr::Value(Ti::Vector {
207 size,
208 scalar: goal_scalar,
209 });
210 *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
211 }
212 _ => {
213 let span = self.get_expression_span(*expr);
214 return Err(Box::new(super::Error::InvalidConstructorComponentType(
215 span, i as i32,
216 )));
217 }
218 }
219 }
220
221 Ok(())
222 }
223
224 pub fn convert_to_leaf_scalar(
226 &mut self,
227 expr: &mut Handle<crate::Expression>,
228 goal: crate::Scalar,
229 ) -> Result<'source, ()> {
230 let inner = super::resolve_inner!(self, *expr);
231 if inner.scalar() != Some(goal) {
234 let cast = crate::Expression::As {
235 expr: *expr,
236 kind: goal.kind,
237 convert: Some(goal.width),
238 };
239 let expr_span = self.get_expression_span(*expr);
240 *expr = self.append_expression(cast, expr_span)?;
241 }
242
243 Ok(())
244 }
245
246 pub fn convert_slice_to_common_leaf_scalar(
257 &mut self,
258 exprs: &mut [Handle<crate::Expression>],
259 goal: crate::Scalar,
260 ) -> Result<'source, ()> {
261 for expr in exprs.iter_mut() {
262 self.convert_to_leaf_scalar(expr, goal)?;
263 }
264
265 Ok(())
266 }
267
268 pub fn concretize(
272 &mut self,
273 expr: Handle<crate::Expression>,
274 ) -> Result<'source, Handle<crate::Expression>> {
275 let inner = super::resolve_inner!(self, expr);
276 if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
277 use crate::ScalarKind as Sk;
278 let concretization_preferences = match scalar.kind {
279 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => return Ok(expr),
281 Sk::AbstractInt => {
282 [crate::Scalar::I32, crate::Scalar::U32, crate::Scalar::F32].as_slice()
283 }
284 Sk::AbstractFloat => [crate::Scalar::F32].as_slice(),
285 };
286 let expr_span = self.get_expression_span(expr);
287 let mut errors = Vec::new();
288 for concrete_scalar in concretization_preferences {
289 match self
290 .as_const_evaluator()
291 .cast_array(expr, *concrete_scalar, expr_span)
292 {
293 Ok(expr) => return Ok(expr),
294 Err(crate::proc::ConstantEvaluatorError::TypeTooLarge(ty)) => {
295 return Err(Box::new(super::Error::TypeTooLarge {
298 span: self.module.types.get_span(ty),
299 }));
300 }
301 Err(err) => {
302 errors.push((concrete_scalar.to_wgsl_for_diagnostics(), err));
303 }
304 }
305 }
306 if !errors.is_empty() {
307 let expr_type = &self.typifier()[expr];
311 return Err(Box::new(super::Error::ConcretizationFailed(Box::new(
312 ConcretizationFailedError {
313 expr_span,
314 expr_type: self.type_resolution_to_string(expr_type),
315 concretization_preferences: errors,
316 },
317 ))));
318 }
319 }
320
321 Ok(expr)
322 }
323
324 pub fn automatic_conversion_consensus<'handle, I>(
346 &self,
347 base: Option<crate::Scalar>,
348 components: I,
349 ) -> core::result::Result<crate::Scalar, usize>
350 where
351 I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
352 I::IntoIter: Clone, {
354 let types = &self.module.types;
355 let components_iter = components.into_iter();
356 log::debug!(
357 "wgsl automatic_conversion_consensus: {}",
358 components_iter
359 .clone()
360 .map(|&expr| {
361 let res = &self.typifier()[expr];
362 self.type_resolution_to_string(res)
363 })
364 .collect::<Vec<String>>()
365 .join(", ")
366 );
367 let mut components_iter = components_iter
368 .map(|&c| self.typifier()[c].inner_with(types).scalar())
369 .enumerate();
370 let base = base
371 .or_else(|| components_iter.next().unwrap().1)
372 .ok_or(0usize)?;
373 let best = components_iter.try_fold(base, |best, (i, scalar)| {
374 scalar
375 .and_then(|scalar| best.automatic_conversion_combine(scalar))
376 .ok_or(i)
377 })?;
378 log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics());
379 Ok(best)
380 }
381}
382
383impl crate::TypeInner {
384 fn automatically_convertible_scalar(
385 &self,
386 types: &crate::UniqueArena<crate::Type>,
387 ) -> Option<crate::Scalar> {
388 use crate::TypeInner as Ti;
389 match *self {
390 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
391 Some(scalar)
392 }
393 Ti::CooperativeMatrix { .. } => None,
394 Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),
395 Ti::Atomic(_)
396 | Ti::Pointer { .. }
397 | Ti::ValuePointer { .. }
398 | Ti::Struct { .. }
399 | Ti::Image { .. }
400 | Ti::Sampler { .. }
401 | Ti::AccelerationStructure { .. }
402 | Ti::RayQuery { .. }
403 | Ti::BindingArray { .. } => None,
404 }
405 }
406
407 pub fn pointer_automatically_convertible_scalar(
411 &self,
412 types: &crate::UniqueArena<crate::Type>,
413 ) -> Option<crate::Scalar> {
414 use crate::TypeInner as Ti;
415 match *self {
416 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
417 Some(scalar)
418 }
419 Ti::CooperativeMatrix { .. } => None,
420 Ti::Atomic(_) => None,
421 Ti::Pointer { base, .. } | Ti::Array { base, .. } => {
422 types[base].inner.automatically_convertible_scalar(types)
423 }
424 Ti::ValuePointer { scalar, .. } => Some(scalar),
425 Ti::Struct { .. }
426 | Ti::Image { .. }
427 | Ti::Sampler { .. }
428 | Ti::AccelerationStructure { .. }
429 | Ti::RayQuery { .. }
430 | Ti::BindingArray { .. } => None,
431 }
432 }
433}
434
435impl crate::Scalar {
436 pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
443 use crate::ScalarKind as Sk;
444
445 match (self.kind, other.kind) {
446 (Sk::AbstractFloat, Sk::AbstractFloat)
448 | (Sk::AbstractInt, Sk::AbstractInt)
449 | (Sk::Sint, Sk::Sint)
450 | (Sk::Uint, Sk::Uint)
451 | (Sk::Float, Sk::Float)
452 | (Sk::Bool, Sk::Bool) => {
453 if self.width == other.width {
454 Some(self)
456 } else {
457 None
461 }
462 }
463
464 (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
466 (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
467
468 (Sk::AbstractFloat, Sk::Float) => Some(other),
470 (Sk::Float, Sk::AbstractFloat) => Some(self),
471
472 (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
474 (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
475
476 (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
478 None
479 }
480
481 (Sk::Bool, _) | (_, Sk::Bool) => None,
483
484 (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
486 }
487 }
488
489 pub fn automatically_converts_to(self, goal: Self) -> bool {
491 self.automatic_conversion_combine(goal) == Some(goal)
492 }
493
494 pub(in crate::front::wgsl) const fn concretize(self) -> Self {
495 use crate::ScalarKind as Sk;
496 match self.kind {
497 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
498 Sk::AbstractInt => Self::I32,
499 Sk::AbstractFloat => Self::F32,
500 }
501 }
502}