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};
9use crate::front::wgsl::Result;
10use crate::{Handle, Span};
11
12impl<'source> super::ExpressionContext<'source, '_, '_> {
13 pub fn try_automatic_conversions(
26 &mut self,
27 expr: Handle<crate::Expression>,
28 goal_ty: &crate::proc::TypeResolution,
29 goal_span: Span,
30 ) -> Result<'source, Handle<crate::Expression>> {
31 let expr_span = self.get_expression_span(expr);
32 let expr_resolution = super::resolve!(self, expr);
35 let types = &self.module.types;
36 let expr_inner = expr_resolution.inner_with(types);
37 let goal_inner = goal_ty.inner_with(types);
38
39 if !expr_inner.is_abstract(types) {
45 return Ok(expr);
46 }
47
48 if self.module.compare_types(expr_resolution, goal_ty) {
50 return Ok(expr);
51 }
52
53 let (_expr_scalar, goal_scalar) =
54 match expr_inner.automatically_converts_to(goal_inner, types) {
55 Some(scalars) => scalars,
56 None => {
57 let source_type = self.type_resolution_to_string(expr_resolution);
58 let dest_type = self.type_resolution_to_string(goal_ty);
59
60 return Err(Box::new(super::Error::AutoConversion(Box::new(
61 AutoConversionError {
62 dest_span: goal_span,
63 dest_type,
64 source_span: expr_span,
65 source_type,
66 },
67 ))));
68 }
69 };
70
71 self.convert_leaf_scalar(expr, expr_span, goal_scalar)
72 }
73
74 pub fn try_automatic_conversion_for_leaf_scalar(
87 &mut self,
88 expr: Handle<crate::Expression>,
89 goal_scalar: crate::Scalar,
90 goal_span: Span,
91 ) -> Result<'source, Handle<crate::Expression>> {
92 let expr_span = self.get_expression_span(expr);
93 let expr_resolution = super::resolve!(self, expr);
94 let types = &self.module.types;
95 let expr_inner = expr_resolution.inner_with(types);
96
97 let make_error = || {
98 let source_type = self.type_resolution_to_string(expr_resolution);
99 super::Error::AutoConversionLeafScalar(Box::new(AutoConversionLeafScalarError {
100 dest_span: goal_span,
101 dest_scalar: goal_scalar.to_wgsl_for_diagnostics(),
102 source_span: expr_span,
103 source_type,
104 }))
105 };
106
107 let expr_scalar = match expr_inner.automatically_convertible_scalar(&self.module.types) {
108 Some(scalar) => scalar,
109 None => return Err(Box::new(make_error())),
110 };
111
112 if expr_scalar == goal_scalar {
113 return Ok(expr);
114 }
115
116 if !expr_scalar.automatically_converts_to(goal_scalar) {
117 return Err(Box::new(make_error()));
118 }
119
120 assert!(expr_scalar.is_abstract());
121
122 self.convert_leaf_scalar(expr, expr_span, goal_scalar)
123 }
124
125 fn convert_leaf_scalar(
126 &mut self,
127 expr: Handle<crate::Expression>,
128 expr_span: Span,
129 goal_scalar: crate::Scalar,
130 ) -> Result<'source, Handle<crate::Expression>> {
131 let expr_inner = super::resolve_inner!(self, expr);
132 if let crate::TypeInner::Array { .. } = *expr_inner {
133 self.as_const_evaluator()
134 .cast_array(expr, goal_scalar, expr_span)
135 .map_err(|err| {
136 Box::new(super::Error::ConstantEvaluatorError(err.into(), expr_span))
137 })
138 } else {
139 let cast = crate::Expression::As {
140 expr,
141 kind: goal_scalar.kind,
142 convert: Some(goal_scalar.width),
143 };
144 self.append_expression(cast, expr_span)
145 }
146 }
147
148 pub fn try_automatic_conversions_slice(
150 &mut self,
151 exprs: &mut [Handle<crate::Expression>],
152 goal_ty: &crate::proc::TypeResolution,
153 goal_span: Span,
154 ) -> Result<'source, ()> {
155 for expr in exprs.iter_mut() {
156 *expr = self.try_automatic_conversions(*expr, goal_ty, goal_span)?;
157 }
158
159 Ok(())
160 }
161
162 pub fn try_automatic_conversions_for_vector(
171 &mut self,
172 exprs: &mut [Handle<crate::Expression>],
173 goal_scalar: crate::Scalar,
174 goal_span: Span,
175 ) -> Result<'source, ()> {
176 use crate::proc::TypeResolution as Tr;
177 use crate::TypeInner as Ti;
178 let goal_scalar_res = Tr::Value(Ti::Scalar(goal_scalar));
179
180 for (i, expr) in exprs.iter_mut().enumerate() {
181 let expr_resolution = super::resolve!(self, *expr);
184 let types = &self.module.types;
185 let expr_inner = expr_resolution.inner_with(types);
186
187 match *expr_inner {
188 Ti::Scalar(_) => {
189 *expr = self.try_automatic_conversions(*expr, &goal_scalar_res, goal_span)?;
190 }
191 Ti::Vector { size, scalar: _ } => {
192 let goal_vector_res = Tr::Value(Ti::Vector {
193 size,
194 scalar: goal_scalar,
195 });
196 *expr = self.try_automatic_conversions(*expr, &goal_vector_res, goal_span)?;
197 }
198 _ => {
199 let span = self.get_expression_span(*expr);
200 return Err(Box::new(super::Error::InvalidConstructorComponentType(
201 span, i as i32,
202 )));
203 }
204 }
205 }
206
207 Ok(())
208 }
209
210 pub fn convert_to_leaf_scalar(
212 &mut self,
213 expr: &mut Handle<crate::Expression>,
214 goal: crate::Scalar,
215 ) -> Result<'source, ()> {
216 let inner = super::resolve_inner!(self, *expr);
217 if inner.scalar() != Some(goal) {
220 let cast = crate::Expression::As {
221 expr: *expr,
222 kind: goal.kind,
223 convert: Some(goal.width),
224 };
225 let expr_span = self.get_expression_span(*expr);
226 *expr = self.append_expression(cast, expr_span)?;
227 }
228
229 Ok(())
230 }
231
232 pub fn convert_slice_to_common_leaf_scalar(
243 &mut self,
244 exprs: &mut [Handle<crate::Expression>],
245 goal: crate::Scalar,
246 ) -> Result<'source, ()> {
247 for expr in exprs.iter_mut() {
248 self.convert_to_leaf_scalar(expr, goal)?;
249 }
250
251 Ok(())
252 }
253
254 pub fn concretize(
258 &mut self,
259 expr: Handle<crate::Expression>,
260 ) -> Result<'source, Handle<crate::Expression>> {
261 let inner = super::resolve_inner!(self, expr);
262 if let Some(scalar) = inner.automatically_convertible_scalar(&self.module.types) {
263 use crate::ScalarKind as Sk;
264 let concretization_preferences = match scalar.kind {
265 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => return Ok(expr),
267 Sk::AbstractInt => {
268 [crate::Scalar::I32, crate::Scalar::U32, crate::Scalar::F32].as_slice()
269 }
270 Sk::AbstractFloat => [crate::Scalar::F32].as_slice(),
271 };
272 let expr_span = self.get_expression_span(expr);
273 let mut errors = Vec::new();
274 for concrete_scalar in concretization_preferences {
275 match self
276 .as_const_evaluator()
277 .cast_array(expr, *concrete_scalar, expr_span)
278 {
279 Ok(expr) => return Ok(expr),
280 Err(err) => {
281 errors.push((concrete_scalar.to_wgsl_for_diagnostics(), err));
282 }
283 }
284 }
285 if !errors.is_empty() {
286 let expr_type = &self.typifier()[expr];
290 return Err(Box::new(super::Error::ConcretizationFailed(Box::new(
291 ConcretizationFailedError {
292 expr_span,
293 expr_type: self.type_resolution_to_string(expr_type),
294 concretization_preferences: errors,
295 },
296 ))));
297 }
298 }
299
300 Ok(expr)
301 }
302
303 pub fn automatic_conversion_consensus<'handle, I>(
325 &self,
326 base: Option<crate::Scalar>,
327 components: I,
328 ) -> core::result::Result<crate::Scalar, usize>
329 where
330 I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
331 I::IntoIter: Clone, {
333 let types = &self.module.types;
334 let components_iter = components.into_iter();
335 log::debug!(
336 "wgsl automatic_conversion_consensus: {}",
337 components_iter
338 .clone()
339 .map(|&expr| {
340 let res = &self.typifier()[expr];
341 self.type_resolution_to_string(res)
342 })
343 .collect::<Vec<String>>()
344 .join(", ")
345 );
346 let mut components_iter = components_iter
347 .map(|&c| self.typifier()[c].inner_with(types).scalar())
348 .enumerate();
349 let base = base
350 .or_else(|| components_iter.next().unwrap().1)
351 .ok_or(0usize)?;
352 let best = components_iter.try_fold(base, |best, (i, scalar)| {
353 scalar
354 .and_then(|scalar| best.automatic_conversion_combine(scalar))
355 .ok_or(i)
356 })?;
357 log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics());
358 Ok(best)
359 }
360}
361
362impl crate::TypeInner {
363 fn automatically_convertible_scalar(
364 &self,
365 types: &crate::UniqueArena<crate::Type>,
366 ) -> Option<crate::Scalar> {
367 use crate::TypeInner as Ti;
368 match *self {
369 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
370 Some(scalar)
371 }
372 Ti::CooperativeMatrix { .. } => None,
373 Ti::Array { base, .. } => types[base].inner.automatically_convertible_scalar(types),
374 Ti::Atomic(_)
375 | Ti::Pointer { .. }
376 | Ti::ValuePointer { .. }
377 | Ti::Struct { .. }
378 | Ti::Image { .. }
379 | Ti::Sampler { .. }
380 | Ti::AccelerationStructure { .. }
381 | Ti::RayQuery { .. }
382 | Ti::BindingArray { .. } => None,
383 }
384 }
385
386 pub fn pointer_automatically_convertible_scalar(
390 &self,
391 types: &crate::UniqueArena<crate::Type>,
392 ) -> Option<crate::Scalar> {
393 use crate::TypeInner as Ti;
394 match *self {
395 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
396 Some(scalar)
397 }
398 Ti::CooperativeMatrix { .. } => None,
399 Ti::Atomic(_) => None,
400 Ti::Pointer { base, .. } | Ti::Array { base, .. } => {
401 types[base].inner.automatically_convertible_scalar(types)
402 }
403 Ti::ValuePointer { scalar, .. } => Some(scalar),
404 Ti::Struct { .. }
405 | Ti::Image { .. }
406 | Ti::Sampler { .. }
407 | Ti::AccelerationStructure { .. }
408 | Ti::RayQuery { .. }
409 | Ti::BindingArray { .. } => None,
410 }
411 }
412}
413
414impl crate::Scalar {
415 pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
422 use crate::ScalarKind as Sk;
423
424 match (self.kind, other.kind) {
425 (Sk::AbstractFloat, Sk::AbstractFloat)
427 | (Sk::AbstractInt, Sk::AbstractInt)
428 | (Sk::Sint, Sk::Sint)
429 | (Sk::Uint, Sk::Uint)
430 | (Sk::Float, Sk::Float)
431 | (Sk::Bool, Sk::Bool) => {
432 if self.width == other.width {
433 Some(self)
435 } else {
436 None
440 }
441 }
442
443 (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
445 (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
446
447 (Sk::AbstractFloat, Sk::Float) => Some(other),
449 (Sk::Float, Sk::AbstractFloat) => Some(self),
450
451 (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
453 (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
454
455 (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
457 None
458 }
459
460 (Sk::Bool, _) | (_, Sk::Bool) => None,
462
463 (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
465 }
466 }
467
468 pub fn automatically_converts_to(self, goal: Self) -> bool {
470 self.automatic_conversion_combine(goal) == Some(goal)
471 }
472
473 pub(in crate::front::wgsl) const fn concretize(self) -> Self {
474 use crate::ScalarKind as Sk;
475 match self.kind {
476 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
477 Sk::AbstractInt => Self::I32,
478 Sk::AbstractFloat => Self::F32,
479 }
480 }
481}