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 mut 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 let concretized = scalar.concretize();
264 if concretized != scalar {
265 assert!(scalar.is_abstract());
266 let expr_span = self.get_expression_span(expr);
267 expr = self
268 .as_const_evaluator()
269 .cast_array(expr, concretized, expr_span)
270 .map_err(|err| {
271 let expr_type = &self.typifier()[expr];
275 super::Error::ConcretizationFailed(Box::new(ConcretizationFailedError {
276 expr_span,
277 expr_type: self.type_resolution_to_string(expr_type),
278 scalar: concretized.to_wgsl_for_diagnostics(),
279 inner: err,
280 }))
281 })?;
282 }
283 }
284
285 Ok(expr)
286 }
287
288 pub fn automatic_conversion_consensus<'handle, I>(
306 &self,
307 components: I,
308 ) -> core::result::Result<crate::Scalar, usize>
309 where
310 I: IntoIterator<Item = &'handle Handle<crate::Expression>>,
311 I::IntoIter: Clone, {
313 let types = &self.module.types;
314 let components_iter = components.into_iter();
315 log::debug!(
316 "wgsl automatic_conversion_consensus: {}",
317 components_iter
318 .clone()
319 .map(|&expr| {
320 let res = &self.typifier()[expr];
321 self.type_resolution_to_string(res)
322 })
323 .collect::<Vec<String>>()
324 .join(", ")
325 );
326 let mut inners = components_iter.map(|&c| self.typifier()[c].inner_with(types));
327 let mut best = inners.next().unwrap().scalar().ok_or(0_usize)?;
328 for (inner, i) in inners.zip(1..) {
329 let scalar = inner.scalar().ok_or(i)?;
330 match best.automatic_conversion_combine(scalar) {
331 Some(new_best) => {
332 best = new_best;
333 }
334 None => return Err(i),
335 }
336 }
337
338 log::debug!(" consensus: {}", best.to_wgsl_for_diagnostics());
339 Ok(best)
340 }
341}
342
343impl crate::TypeInner {
344 fn automatically_convertible_scalar(
345 &self,
346 types: &crate::UniqueArena<crate::Type>,
347 ) -> Option<crate::Scalar> {
348 use crate::TypeInner as Ti;
349 match *self {
350 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
351 Some(scalar)
352 }
353 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 | Ti::BindingArray { .. } => None,
363 }
364 }
365
366 pub fn pointer_automatically_convertible_scalar(
370 &self,
371 types: &crate::UniqueArena<crate::Type>,
372 ) -> Option<crate::Scalar> {
373 use crate::TypeInner as Ti;
374 match *self {
375 Ti::Scalar(scalar) | Ti::Vector { scalar, .. } | Ti::Matrix { scalar, .. } => {
376 Some(scalar)
377 }
378 Ti::Atomic(_) => None,
379 Ti::Pointer { base, .. } | Ti::Array { base, .. } => {
380 types[base].inner.automatically_convertible_scalar(types)
381 }
382 Ti::ValuePointer { scalar, .. } => Some(scalar),
383 Ti::Struct { .. }
384 | Ti::Image { .. }
385 | Ti::Sampler { .. }
386 | Ti::AccelerationStructure { .. }
387 | Ti::RayQuery { .. }
388 | Ti::BindingArray { .. } => None,
389 }
390 }
391}
392
393impl crate::Scalar {
394 pub const fn automatic_conversion_combine(self, other: Self) -> Option<crate::Scalar> {
401 use crate::ScalarKind as Sk;
402
403 match (self.kind, other.kind) {
404 (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 if self.width == other.width {
412 Some(self)
414 } else {
415 None
419 }
420 }
421
422 (Sk::AbstractFloat, Sk::AbstractInt) => Some(self),
424 (Sk::AbstractInt, Sk::AbstractFloat) => Some(other),
425
426 (Sk::AbstractFloat, Sk::Float) => Some(other),
428 (Sk::Float, Sk::AbstractFloat) => Some(self),
429
430 (Sk::AbstractInt, Sk::Uint | Sk::Sint | Sk::Float) => Some(other),
432 (Sk::Uint | Sk::Sint | Sk::Float, Sk::AbstractInt) => Some(self),
433
434 (Sk::AbstractFloat, Sk::Uint | Sk::Sint) | (Sk::Uint | Sk::Sint, Sk::AbstractFloat) => {
436 None
437 }
438
439 (Sk::Bool, _) | (_, Sk::Bool) => None,
441
442 (Sk::Sint | Sk::Uint | Sk::Float, Sk::Sint | Sk::Uint | Sk::Float) => None,
444 }
445 }
446
447 pub fn automatically_converts_to(self, goal: Self) -> bool {
449 self.automatic_conversion_combine(goal) == Some(goal)
450 }
451
452 pub(in crate::front::wgsl) const fn concretize(self) -> Self {
453 use crate::ScalarKind as Sk;
454 match self.kind {
455 Sk::Sint | Sk::Uint | Sk::Float | Sk::Bool => self,
456 Sk::AbstractInt => Self::I32,
457 Sk::AbstractFloat => Self::F32,
458 }
459 }
460}