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}